Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4c5a603b2 | ||
|
|
bcb4317acc | ||
|
|
6cd0bc3114 | ||
|
|
48fb2ab44c | ||
|
|
691a00f7a1 | ||
|
|
68b350c5e6 | ||
|
|
8e8a4ce028 | ||
|
|
bc54e276dd | ||
|
|
ae6c1ee824 | ||
|
|
d6feb8474f | ||
|
|
822e55972b | ||
|
|
ec7f93ef4b | ||
|
|
b16c2bd065 | ||
|
|
97cf7a5545 | ||
|
|
86125a39fd | ||
|
|
c1a270be35 | ||
|
|
d41e3d4e28 | ||
|
|
eddd44d999 | ||
|
|
03fda25f92 | ||
|
|
d9c038c93f | ||
|
|
9c8c4a5574 | ||
|
|
bbd2df6631 | ||
|
|
61d4d06253 | ||
|
|
ab74869935 | ||
|
|
bd9d3f77c1 | ||
|
|
4c9d69376f | ||
|
|
825ea04a9c | ||
|
|
f9f12d9746 | ||
|
|
ae46e25a3a | ||
|
|
ae395a80b5 |
404
.drone.yml
404
.drone.yml
File diff suppressed because it is too large
Load Diff
52
.github/CODEOWNERS
vendored
52
.github/CODEOWNERS
vendored
@@ -220,15 +220,15 @@
|
||||
|
||||
|
||||
# Continuous Integration
|
||||
.drone.yml @grafana/grafana-delivery
|
||||
.drone.star @grafana/grafana-delivery
|
||||
/scripts/drone/ @grafana/grafana-delivery
|
||||
/pkg/build/ @grafana/grafana-delivery
|
||||
/.dockerignore @grafana/grafana-delivery
|
||||
/Dockerfile @grafana/grafana-delivery
|
||||
/Makefile @grafana/grafana-delivery
|
||||
/scripts/build/ @grafana/grafana-delivery
|
||||
/scripts/list-release-artifacts.sh @grafana/grafana-delivery
|
||||
.drone.yml @grafana/grafana-release-guild
|
||||
.drone.star @grafana/grafana-release-guild
|
||||
/scripts/drone/ @grafana/grafana-release-guild
|
||||
/pkg/build/ @grafana/grafana-release-guild
|
||||
/.dockerignore @grafana/grafana-release-guild
|
||||
/Dockerfile @grafana/grafana-release-guild
|
||||
/Makefile @grafana/grafana-release-guild
|
||||
/scripts/build/ @grafana/grafana-release-guild
|
||||
/scripts/list-release-artifacts.sh @grafana/grafana-release-guild
|
||||
|
||||
# OSS Plugin Partnerships backend code
|
||||
/pkg/tsdb/cloudwatch/ @grafana/aws-datasources
|
||||
@@ -456,25 +456,25 @@ lerna.json @grafana/frontend-ops
|
||||
|
||||
/scripts/benchmark-access-control.sh @grafana/grafana-authnz-team
|
||||
/scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend
|
||||
/scripts/ci-* @grafana/grafana-delivery
|
||||
/scripts/circle-* @grafana/grafana-delivery
|
||||
/scripts/ci-* @grafana/grafana-release-guild
|
||||
/scripts/circle-* @grafana/grafana-release-guild
|
||||
/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/grafana-bi-squad
|
||||
/scripts/cli/ @grafana/grafana-frontend-platform
|
||||
/scripts/clean-git-or-error.sh @grafana/grafana-as-code
|
||||
/scripts/grafana-server/ @grafana/grafana-frontend-platform
|
||||
/scripts/helpers/ @grafana/grafana-delivery
|
||||
/scripts/helpers/ @grafana/grafana-release-guild
|
||||
/scripts/import_many_dashboards.sh @torkelo
|
||||
/scripts/mixin-check.sh @bergquist
|
||||
/scripts/openapi3/ @grafana/grafana-operator-experience-squad
|
||||
/scripts/prepare-packagejson.js @grafana/frontend-ops
|
||||
/scripts/protobuf-check.sh @grafana/plugins-platform-backend
|
||||
/scripts/stripnulls.sh @grafana/grafana-as-code
|
||||
/scripts/tag_release.sh @grafana/grafana-delivery
|
||||
/scripts/trigger_docker_build.sh @grafana/grafana-delivery
|
||||
/scripts/trigger_grafana_packer.sh @grafana/grafana-delivery
|
||||
/scripts/trigger_windows_build.sh @grafana/grafana-delivery
|
||||
/scripts/validate-devenv-dashboards.sh @grafana/grafana-delivery
|
||||
/scripts/verify-repo-update/ @grafana/grafana-delivery
|
||||
/scripts/tag_release.sh @grafana/grafana-release-guild
|
||||
/scripts/trigger_docker_build.sh @grafana/grafana-release-guild
|
||||
/scripts/trigger_grafana_packer.sh @grafana/grafana-release-guild
|
||||
/scripts/trigger_windows_build.sh @grafana/grafana-release-guild
|
||||
/scripts/validate-devenv-dashboards.sh @grafana/grafana-release-guild
|
||||
/scripts/verify-repo-update/ @grafana/grafana-release-guild
|
||||
|
||||
/scripts/webpack/ @grafana/frontend-ops
|
||||
/scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform
|
||||
@@ -571,10 +571,10 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/pr-commands.json @marefr
|
||||
/.github/renovate.json5 @grafana/frontend-ops
|
||||
/.github/teams.yml @armandgrillet
|
||||
/.github/workflows/auto-milestone.yml @grafana/grafana-delivery
|
||||
/.github/workflows/backport.yml @grafana/grafana-delivery
|
||||
/.github/workflows/bump-version.yml @grafana/grafana-delivery
|
||||
/.github/workflows/close-milestone.yml @grafana/grafana-delivery
|
||||
/.github/workflows/auto-milestone.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/backport.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/bump-version.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/close-milestone.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/cloud-data-sources-code-coverage.yml @grafana/partner-plugins @grafana/aws-datasources
|
||||
/.github/workflows/codeowners-validator.yml @tolzhabayev
|
||||
/.github/workflows/codeql-analysis.yml @DanCech
|
||||
@@ -594,8 +594,8 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/pr-codeql-analysis-python.yml @DanCech
|
||||
/.github/workflows/pr-commands-closed.yml @tolzhabayev
|
||||
/.github/workflows/pr-commands.yml @marefr
|
||||
/.github/workflows/pr-patch-check.yml @grafana/grafana-delivery
|
||||
/.github/workflows/sync-mirror.yml @grafana/grafana-delivery
|
||||
/.github/workflows/pr-patch-check.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/sync-mirror.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/publish-technical-documentation-next.yml @grafana/docs-grafana
|
||||
/.github/workflows/publish-technical-documentation-release.yml @grafana/docs-grafana
|
||||
/.github/workflows/remove-milestone.yml @grafana/grafana-frontend-platform
|
||||
@@ -603,9 +603,9 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend
|
||||
/.github/workflows/scripts/pr-get-job-link.js @grafana/plugins-platform-frontend
|
||||
/.github/workflows/stale.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/update-changelog.yml @grafana/grafana-delivery
|
||||
/.github/workflows/update-changelog.yml @grafana/grafana-release-guild
|
||||
/.github/workflows/snyk.yml @grafana/security-team
|
||||
/.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-delivery
|
||||
/.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-release-guild
|
||||
|
||||
# Conf
|
||||
/conf/defaults.ini @torkelo
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
name: Set go version
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.21.5'
|
||||
go-version: '1.21.8'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Owned by grafana-delivery-squad
|
||||
# Intended to be dropped into the base repo (Ex: grafana/grafana) for use in the security mirror.
|
||||
# Owned by grafana-release-guild
|
||||
# Intended to be dropped into the base repo (Ex: grafana/grafana) for use in the security mirror.
|
||||
name: Create security patch
|
||||
run-name: create-security-patch
|
||||
on:
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
trigger_downstream_create_security_patch:
|
||||
concurrency: create-patch-${{ github.ref_name }}
|
||||
uses: grafana/security-patch-actions/.github/workflows/create-patch.yml@main
|
||||
if: github.repository == 'grafana/grafana-security-mirror'
|
||||
if: github.repository == 'grafana/grafana-security-mirror'
|
||||
with:
|
||||
repo: "${{ github.repository }}"
|
||||
src_ref: "${{ github.head_ref }}" # this is the source branch name, Ex: "feature/newthing"
|
||||
|
||||
2
.github/workflows/pr-codeql-analysis-go.yml
vendored
2
.github/workflows/pr-codeql-analysis-go.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Set go version
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.21.5'
|
||||
go-version: '1.21.8'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
28
.github/workflows/pr-commands.yml
vendored
28
.github/workflows/pr-commands.yml
vendored
@@ -8,20 +8,44 @@ on:
|
||||
concurrency:
|
||||
group: pr-commands-${{ github.event.number }}
|
||||
jobs:
|
||||
config:
|
||||
runs-on: "ubuntu-latest"
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
- name: "Check for secrets"
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${{ (secrets.GRAFANA_PR_AUTOMATION_APP_ID != '' &&
|
||||
secrets.GRAFANA_PR_AUTOMATION_APP_PEM != '' &&
|
||||
secrets.GRAFANA_MISC_STATS_API_KEY != ''
|
||||
) || '' }}" ]; then
|
||||
echo "has-secrets=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
main:
|
||||
needs: config
|
||||
if: needs.config.outputs.has-secrets
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "grafana/grafana-github-actions"
|
||||
path: ./actions
|
||||
ref: main
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: "Generate token"
|
||||
id: generate_token
|
||||
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
|
||||
with:
|
||||
app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }}
|
||||
private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }}
|
||||
- name: Run Commands
|
||||
uses: ./actions/commands
|
||||
with:
|
||||
metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}}
|
||||
token: ${{secrets.GH_BOT_ACCESS_TOKEN}}
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
configPath: pr-commands
|
||||
|
||||
3
.github/workflows/pr-patch-check.yml
vendored
3
.github/workflows/pr-patch-check.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# Owned by grafana-delivery-squad
|
||||
# Owned by grafana-release-guild
|
||||
# Intended to be dropped into the base repo Ex: grafana/grafana
|
||||
name: Check for patch conflicts
|
||||
run-name: check-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }}
|
||||
@@ -18,6 +18,7 @@ on:
|
||||
jobs:
|
||||
trigger_downstream_patch_check:
|
||||
uses: grafana/security-patch-actions/.github/workflows/test-patches.yml@main
|
||||
if: github.repository == 'grafana/grafana'
|
||||
with:
|
||||
src_repo: "${{ github.repository }}"
|
||||
src_ref: "${{ github.head_ref }}" # this is the source branch name, Ex: "feature/newthing"
|
||||
|
||||
2
.github/workflows/sync-mirror.yml
vendored
2
.github/workflows/sync-mirror.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# Owned by grafana-delivery-squad
|
||||
# Owned by grafana-release-guild
|
||||
# Intended to be dropped into the base repo, Ex: grafana/grafana
|
||||
name: Sync to mirror
|
||||
run-name: sync-to-mirror-${{ github.ref_name }}
|
||||
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,3 +1,40 @@
|
||||
<!-- 9.5.17 START -->
|
||||
|
||||
# 9.5.17 (2024-03-05)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- Bump go-git to v5.11.0. [#83711](https://github.com/grafana/grafana/issues/83711), [@papagian](https://github.com/papagian)
|
||||
- **Plugins:** Bump otelgrpc instrumentation to 0.47.0. [#83674](https://github.com/grafana/grafana/issues/83674), [@wbrowne](https://github.com/wbrowne)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Auth:** Fix email verification bypass when using basic authentication. [#83494](https://github.com/grafana/grafana/issues/83494)
|
||||
|
||||
<!-- 9.5.17 END -->
|
||||
<!-- 9.5.16 START -->
|
||||
|
||||
# 9.5.16 (2024-01-29)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Annotations:** Split cleanup into separate queries and deletes to avoid deadlocks on MySQL. [#80682](https://github.com/grafana/grafana/issues/80682), [@alexweav](https://github.com/alexweav)
|
||||
|
||||
<!-- 9.5.16 END -->
|
||||
<!-- 9.5.15 START -->
|
||||
|
||||
# 9.5.15 (2023-12-18)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** Attempt to retry retryable errors. [#79209](https://github.com/grafana/grafana/issues/79209), [@gotjosh](https://github.com/gotjosh)
|
||||
- **Unified Alerting:** Set to 1 by default. [#79109](https://github.com/grafana/grafana/issues/79109), [@gotjosh](https://github.com/gotjosh)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Recorded Queries:** Add org isolation (remote write target per org), and fix cross org Delete/List. (Enterprise)
|
||||
|
||||
<!-- 9.5.15 END -->
|
||||
<!-- 9.5.14 START -->
|
||||
|
||||
# 9.5.14 (2023-11-13)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_IMAGE=alpine:3.18.3
|
||||
ARG JS_IMAGE=node:18-alpine3.18
|
||||
ARG BASE_IMAGE=alpine:3.19.1
|
||||
ARG JS_IMAGE=node:18-alpine
|
||||
ARG JS_PLATFORM=linux/amd64
|
||||
ARG GO_IMAGE=golang:1.21.5-alpine3.18
|
||||
ARG GO_IMAGE=golang:1.21.8-alpine
|
||||
|
||||
ARG GO_SRC=go-builder
|
||||
ARG JS_SRC=js-builder
|
||||
|
||||
2
Makefile
2
Makefile
@@ -245,7 +245,7 @@ build-docker-full-ubuntu: ## Build Docker image based on Ubuntu for development.
|
||||
--build-arg COMMIT_SHA=$$(git rev-parse HEAD) \
|
||||
--build-arg BUILD_BRANCH=$$(git rev-parse --abbrev-ref HEAD) \
|
||||
--build-arg BASE_IMAGE=ubuntu:22.04 \
|
||||
--build-arg GO_IMAGE=golang:1.21.5 \
|
||||
--build-arg GO_IMAGE=golang:1.21.8 \
|
||||
--tag grafana/grafana$(TAG_SUFFIX):dev-ubuntu \
|
||||
$(DOCKER_BUILD_ARGS)
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ reporting_distributor = grafana-labs
|
||||
# for new versions of grafana. The check is used
|
||||
# in some UI views to notify that a grafana update exists.
|
||||
# This option does not cause any auto updates, nor send any information
|
||||
# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
|
||||
# only a GET request to https://grafana.com/api/grafana/versions/stable to get the latest version.
|
||||
check_for_updates = true
|
||||
|
||||
# Set to false to disable all checks to https://grafana.com
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
# for new versions of grafana. The check is used
|
||||
# in some UI views to notify that a grafana update exists.
|
||||
# This option does not cause any auto updates, nor send any information
|
||||
# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
|
||||
# only a GET request to https://grafana.com/api/grafana/versions/stable to get the latest version.
|
||||
;check_for_updates = true
|
||||
|
||||
# Set to false to disable all checks to https://grafana.com
|
||||
|
||||
@@ -48,7 +48,7 @@ Instead, when it is merged & closed then a bot will look for the most appropriat
|
||||
|
||||
That milestone should always reflect the branch that the pull request is merged into.
|
||||
For every major and minor release there is a milestone ending with `.x` (e.g. `10.0.x` for the 10.0.x releases).
|
||||
Pull requests targetting `main` should use the `.x` milestone of the next minor (or major) version (you can find that version number inside the `package.json` file).
|
||||
Pull requests targeting `main` should use the `.x` milestone of the next minor (or major) version (you can find that version number inside the `package.json` file).
|
||||
Backport pull requestss should use the version of the target branch (e.g. `9.4.x` for the `v9.4.x` branch).
|
||||
|
||||
### Include in changelog and release notes?
|
||||
|
||||
@@ -104,7 +104,6 @@ The following table outlines the various generic OAuth2 configuration options. Y
|
||||
| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` |
|
||||
| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` |
|
||||
| `groups_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a string array of groups. | |
|
||||
| `allowed_groups` | No | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | |
|
||||
| `allowed_organizations` | No | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | |
|
||||
| `allowed_domains` | No | List comma- or space-separated domains. The user should belong to at least one domain to log in. | |
|
||||
| `team_ids` | No | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | |
|
||||
|
||||
@@ -17,8 +17,10 @@ Copy Grafana configuration files that you might have modified in your Grafana de
|
||||
|
||||
The Grafana configuration files are located in the following directories:
|
||||
|
||||
- Default configuration: `$WORKING_DIR/conf/defaults.ini`
|
||||
- Custom configuration: `$WORKING_DIR/conf/custom.ini`
|
||||
- Default configuration: `$WORKING_DIR/defaults.ini` (Don't change this file)
|
||||
- Custom configuration: `$WORKING_DIR/custom.ini`
|
||||
|
||||
For more information on where to find configuration files, refer to [Configuration file location](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location).
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
If you installed Grafana using the `deb` or `rpm` packages, then your configuration file is located at
|
||||
|
||||
@@ -8,13 +8,13 @@ title: Upgrade guide common tasks
|
||||
|
||||
## Upgrade Grafana
|
||||
|
||||
The following sections provide instructions for how to upgrade Grafana based on your installation method.
|
||||
The following sections provide instructions for how to upgrade Grafana based on your installation method. For more information on where to find configuration files, refer to [Configuration file location](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location).
|
||||
|
||||
### Debian
|
||||
|
||||
To upgrade Grafana installed from a Debian package (`.deb`), complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/grafana.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
@@ -32,7 +32,7 @@ To upgrade Grafana installed from a Debian package (`.deb`), complete the follow
|
||||
|
||||
To upgrade Grafana installed from the Grafana Labs APT repository, complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/grafana.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
@@ -49,7 +49,7 @@ Grafana automatically updates when you run `apt-get upgrade`.
|
||||
|
||||
To upgrade Grafana installed from the binary `.tar.gz` package, complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
1. In your current installation of Grafana, save your custom configuration changes to the custom configuration file, `custom.ini` or `grafana.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
@@ -61,7 +61,7 @@ To upgrade Grafana installed from the binary `.tar.gz` package, complete the fol
|
||||
|
||||
To upgrade Grafana installed using RPM or YUM complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/grafana.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
@@ -84,7 +84,7 @@ To upgrade Grafana installed using RPM or YUM complete the following steps:
|
||||
|
||||
To upgrade Grafana running in a Docker container, complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
1. Use Grafana [environment variables](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#override-configuration-with-environment-variables) to save your custom configurations; this is the recommended method. Alternatively, you can view your configuration files manually by accessing the deployed container.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
@@ -119,7 +119,7 @@ To upgrade Grafana installed on Windows, complete the following steps:
|
||||
|
||||
To upgrade Grafana installed on Mac, complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
1. In your current installation of Grafana, save your custom configuration changes to the custom configuration file, `custom.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
|
||||
40
emails/templates/verify_email_update.mjml
Normal file
40
emails/templates/verify_email_update.mjml
Normal file
@@ -0,0 +1,40 @@
|
||||
<mjml>
|
||||
<!-- global variables -->
|
||||
<mj-include path="./partials/_globals.mjml" />
|
||||
<!-- css styling -->
|
||||
<mj-include path="./partials/layout/theme.css" type="css" css-inline="inline" />
|
||||
<mj-head>
|
||||
<!-- ⬇ Don't forget to specifify an email subject below! ⬇ -->
|
||||
<mj-title>
|
||||
{{ Subject .Subject .TemplateData "Verify your new email - {{.Name}}" }}
|
||||
</mj-title>
|
||||
<mj-include path="./partials/layout/head.mjml" />
|
||||
</mj-head>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
<mj-include path="./partials/layout/header.mjml" />
|
||||
</mj-section>
|
||||
<mj-section css-class="background">
|
||||
<mj-column>
|
||||
<mj-text>
|
||||
<h2>Hi {{ .Name }},</h2>
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
Please click the following link to verify your email within <strong>{{ .VerificationEmailLifetimeHours }} hour(s)</strong>.
|
||||
</mj-text>
|
||||
<mj-button href="{{ .AppUrl }}user/email/update?code={{ .Code }}">
|
||||
Verify Email
|
||||
</mj-button>
|
||||
<mj-text>
|
||||
You can also copy and paste this link into your browser directly:
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
<a rel="noopener" href="{{ .AppUrl }}user/email/update?code={{ .Code }}">{{ .AppUrl }}user/email/update?code={{ .Code }}</a>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-include path="./partials/layout/footer.mjml" />
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
6
emails/templates/verify_email_update.txt
Normal file
6
emails/templates/verify_email_update.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
[[HiddenSubject .Subject "Verify your new email - [[.Name]]"]]
|
||||
|
||||
Hi [[.Name]],
|
||||
|
||||
Copy and paste the following link directly in your browser to verify your email within [[.VerificationEmailLifetimeHours]] hour(s).
|
||||
[[.AppUrl]]user/email/update?code=[[.Code]]
|
||||
92
go.mod
92
go.mod
@@ -30,6 +30,13 @@ require (
|
||||
k8s.io/apimachinery v0.26.2
|
||||
)
|
||||
|
||||
replace (
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
|
||||
go.opentelemetry.io/otel => go.opentelemetry.io/otel v1.22.0
|
||||
go.opentelemetry.io/otel/metric => go.opentelemetry.io/otel/metric v1.22.0
|
||||
go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v1.22.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.30.1
|
||||
cuelang.org/go v0.5.0-beta.2
|
||||
@@ -47,7 +54,7 @@ require (
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gchaincl/sqlhooks v1.3.0
|
||||
github.com/getsentry/sentry-go v0.13.0
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.4
|
||||
github.com/go-openapi/strfmt v0.21.7
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
@@ -59,11 +66,11 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/google/wire v0.5.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/grafana/alerting v0.0.0-20230502194537-ce9fba922d97
|
||||
github.com/grafana/alerting v0.0.0-20240216133158-fbb9275f6319
|
||||
github.com/grafana/cuetsy v0.1.6
|
||||
github.com/grafana/grafana-aws-sdk v0.12.0
|
||||
github.com/grafana/grafana-azure-sdk-go v1.9.0
|
||||
@@ -82,7 +89,7 @@ require (
|
||||
github.com/m3db/prometheus_remote_client_golang v0.4.4
|
||||
github.com/magefile/mage v1.14.0
|
||||
github.com/mattn/go-isatty v0.0.16
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/mattn/go-sqlite3 v1.14.19
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
@@ -107,21 +114,21 @@ require (
|
||||
go.opentelemetry.io/collector v0.31.0
|
||||
go.opentelemetry.io/collector/model v0.31.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0
|
||||
go.opentelemetry.io/otel v1.18.0
|
||||
go.opentelemetry.io/otel v1.22.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.0.0
|
||||
go.opentelemetry.io/otel/sdk v1.17.0
|
||||
go.opentelemetry.io/otel/trace v1.18.0
|
||||
golang.org/x/crypto v0.15.0
|
||||
go.opentelemetry.io/otel/sdk v1.22.0
|
||||
go.opentelemetry.io/otel/trace v1.22.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/exp v0.0.0-20221211140036-ad323defaf05
|
||||
golang.org/x/net v0.18.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/oauth2 v0.13.0
|
||||
golang.org/x/sync v0.4.0
|
||||
golang.org/x/time v0.2.0
|
||||
golang.org/x/tools v0.7.0
|
||||
golang.org/x/tools v0.13.0
|
||||
gonum.org/v1/gonum v0.11.0
|
||||
google.golang.org/api v0.126.0
|
||||
google.golang.org/grpc v1.58.3
|
||||
google.golang.org/protobuf v1.31.0
|
||||
google.golang.org/api v0.128.0
|
||||
google.golang.org/grpc v1.60.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/mail.v2 v2.3.1
|
||||
@@ -171,13 +178,13 @@ require (
|
||||
github.com/go-openapi/validate v0.22.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang/glog v1.1.0 // indirect
|
||||
github.com/golang/glog v1.1.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/flatbuffers v2.0.8+incompatible // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.11.0
|
||||
github.com/googleapis/gax-go/v2 v2.12.0
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/grafana/grafana-google-sdk-go v0.1.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
|
||||
@@ -225,15 +232,15 @@ require (
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/goleak v1.2.1 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/kms v1.12.1
|
||||
cloud.google.com/go/kms v1.15.2
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0
|
||||
@@ -254,7 +261,7 @@ require (
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/urfave/cli v1.22.12
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0
|
||||
@@ -276,8 +283,9 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.4 // indirect
|
||||
cloud.google.com/go v0.110.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
@@ -286,9 +294,11 @@ require (
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/bmatcuk/doublestar v1.1.1 // indirect
|
||||
github.com/buildkite/yaml v2.1.0+incompatible // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/containerd/containerd v1.6.8 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/digitalocean/godo v1.88.0 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
@@ -301,7 +311,7 @@ require (
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect
|
||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||
github.com/grafana/sqlds/v2 v2.3.10 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
@@ -319,21 +329,23 @@ require (
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
|
||||
github.com/segmentio/asm v1.1.4 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 // indirect
|
||||
github.com/weaveworks/promrus v1.2.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.18.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
||||
go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
@@ -343,15 +355,15 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.21.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.1 // indirect
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.2 // indirect
|
||||
filippo.io/age v1.1.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f // indirect
|
||||
@@ -366,19 +378,19 @@ require (
|
||||
github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
github.com/docker/docker v20.10.21+incompatible
|
||||
github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/go-github v17.0.0+incompatible
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/hmarr/codeowners v1.1.2
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.15.13 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.10.0 // indirect
|
||||
@@ -389,11 +401,11 @@ require (
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/wk8/go-ordered-map v1.0.0
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xlab/treeprint v1.1.0
|
||||
github.com/yudai/pp v2.0.1+incompatible // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
golang.org/x/mod v0.9.0
|
||||
golang.org/x/mod v0.12.0
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
|
||||
166
go.sum
166
go.sum
@@ -41,8 +41,8 @@ cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2Z
|
||||
cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=
|
||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
|
||||
cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
|
||||
cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
||||
cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME=
|
||||
cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
@@ -58,8 +58,8 @@ cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6m
|
||||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk=
|
||||
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
@@ -70,12 +70,12 @@ cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx
|
||||
cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
|
||||
cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
|
||||
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
|
||||
cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4=
|
||||
cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
|
||||
cloud.google.com/go/kms v1.1.0/go.mod h1:WdbppnCDMDpOvoYBMn1+gNmOeEoZYqAv+HeuKARGCXI=
|
||||
cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
|
||||
cloud.google.com/go/kms v1.12.1 h1:xZmZuwy2cwzsocmKDOPu4BL7umg8QXagQx6fKVmf45U=
|
||||
cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
|
||||
cloud.google.com/go/kms v1.15.2 h1:lh6qra6oC4AyWe5fUUUBe/S27k12OHAleOOOw6KakdE=
|
||||
cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w=
|
||||
cloud.google.com/go/monitoring v1.1.0/go.mod h1:L81pzz7HKn14QCMaCs6NTQkdBnE87TElyanS95vIcl4=
|
||||
cloud.google.com/go/monitoring v1.4.0/go.mod h1:y6xnxfwI3hTFWOdkOaD7nfJVlwuC3/mS/5kvtT131p4=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
@@ -102,6 +102,8 @@ contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeS
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.3.0/go.mod h1:rpCPVQKhiyH8oomWgm34ZmgIdZa8OVYO5WAIygPbBBE=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.10/go.mod h1:I5htMbyta491eUxufwwZPQdcKvvgzMB4O9ni41YnIM8=
|
||||
contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg=
|
||||
filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE=
|
||||
@@ -240,6 +242,8 @@ github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JP
|
||||
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
|
||||
@@ -256,6 +260,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -458,6 +464,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
|
||||
github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=
|
||||
github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds=
|
||||
@@ -499,6 +506,10 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -634,6 +645,8 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
|
||||
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
|
||||
github.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b/go.mod h1:v9FBN7gdVTpiD/+LZ7Po0UKvROyT87uLVxTHVky/dlQ=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
|
||||
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
|
||||
@@ -740,6 +753,8 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac h1:XDAn206aIqKPdF5YczuuJXSQPx+WOen0Pxbxp5Fq8Pg=
|
||||
github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -751,6 +766,8 @@ github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw
|
||||
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -779,8 +796,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
@@ -835,13 +852,19 @@ github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2H
|
||||
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
|
||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -872,8 +895,9 @@ github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
@@ -1105,8 +1129,8 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
|
||||
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
|
||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -1180,8 +1204,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
|
||||
@@ -1233,13 +1258,14 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
@@ -1247,8 +1273,8 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0
|
||||
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
|
||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
@@ -1285,8 +1311,8 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20230502194537-ce9fba922d97 h1:Zi7sQOEquRNK+dPKNNZ3J3aczF+qPlLpsRzRQC13rTc=
|
||||
github.com/grafana/alerting v0.0.0-20230502194537-ce9fba922d97/go.mod h1:5edgy6tQY4+W2wuJdi8g2GjbVmpJufguy7QGIRl2q4o=
|
||||
github.com/grafana/alerting v0.0.0-20240216133158-fbb9275f6319 h1:w+/lg6KIa+A8xQDHanZ0NWT116y2CSuDme7pi8IJdms=
|
||||
github.com/grafana/alerting v0.0.0-20240216133158-fbb9275f6319/go.mod h1:5edgy6tQY4+W2wuJdi8g2GjbVmpJufguy7QGIRl2q4o=
|
||||
github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw=
|
||||
github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
|
||||
github.com/grafana/cuetsy v0.1.6 h1:61QGIDy1rVABU3OkoarOn0+qPdGopIJr34PyWVmGDfs=
|
||||
@@ -1592,6 +1618,8 @@ github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0Lh
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -1732,6 +1760,8 @@ github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
@@ -1958,6 +1988,8 @@ github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuR
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
@@ -2151,6 +2183,8 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
@@ -2311,6 +2345,8 @@ github.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ
|
||||
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
||||
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
@@ -2413,41 +2449,33 @@ go.opentelemetry.io/collector/model v0.31.0/go.mod h1:PcHNnM+RUl0uD8VkSn93PO78N7
|
||||
go.opentelemetry.io/contrib v0.21.0 h1:RMJ6GlUVzLYp/zmItxTTdAmr1gnpO/HHMFmvjAhvJQM=
|
||||
go.opentelemetry.io/contrib v0.21.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0/go.mod h1:Vm5u/mtkj1OMhtao0v+BGo2LUoLCgHYXvRmj0jWITlE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0 h1:b8xjZxHbLrXAum4SxJd1Rlm7Y/fKaB+6ACI7/e5EfSA=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0/go.mod h1:1ei0a32xOGkFoySu7y1DAHfcuIhC0pNZpvY2huXuMy4=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0 h1:ewRgsETI7b5nPCK3FqKdY9mFR/9ZwtexwC26//Srjn0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0/go.mod h1:+BrAX3hlRmkYIKl2e/eSRaKLkClDTY19gzegkQ+KeEQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0/go.mod h1:JQAtechjxLEL81EjmbRwxBq/XEzGaHcsPuDHAx54hg4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 h1:xdJjwy5t/8I+TZehMMQ+r2h50HREihH2oMUhimQ+jug=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0/go.mod h1:tU0nwW4QTvKceNUP60/PQm0FI8zDSwey7gIFt3RR/yw=
|
||||
go.opentelemetry.io/contrib/zpages v0.0.0-20210722161726-7668016acb73/go.mod h1:NAkejuYm41lpyL43Fu1XdnCOYxN5NVV80/MJ03JQ/X8=
|
||||
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
||||
go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs=
|
||||
go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI=
|
||||
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
|
||||
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.0.0 h1:cLhx8llHw02h5JTqGqaRbYn+QVKHmrzD9vEbKnSPk5U=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.0.0/go.mod h1:q10N1AolE1JjqKrFJK2tYw0iZpmX+HBaXBtuCzRnBGQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 h1:U5GYackKpVKlPrd/5gKMlrTlP2dCESAAFU682VCpieY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0/go.mod h1:aFsJfCEnLzEu9vRRAcUiB/cpRTbVsNdF3OHSPpdjxZQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 h1:iGeIsSYwpYSvh5UGzWrJfTDJvPjrXtxl3GUppj6IXQU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0/go.mod h1:1j3H3G1SBYpZFti6OI4P0uRQCW20MXkG5v4UWXppLLE=
|
||||
go.opentelemetry.io/otel/internal/metric v0.21.0/go.mod h1:iOfAaY2YycsXfYD4kaRSbLx2LKmfpKObWBEv9QK5zFo=
|
||||
go.opentelemetry.io/otel/metric v0.21.0/go.mod h1:JWCt1bjivC4iCrz/aCrM1GSw+ZcvY44KCbaeeRhzHnc=
|
||||
go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ=
|
||||
go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k=
|
||||
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
|
||||
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
|
||||
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0-RC1/go.mod h1:kj6yPn7Pgt5ByRuwesbaWcRLA+V7BSDg3Hf8xRvsvf8=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM=
|
||||
go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys=
|
||||
go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE=
|
||||
go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ=
|
||||
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
|
||||
go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
|
||||
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
||||
go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10=
|
||||
go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
|
||||
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
|
||||
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
@@ -2547,9 +2575,10 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -2606,6 +2635,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -2707,8 +2738,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -2737,8 +2768,8 @@ golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7Lm
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
|
||||
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -2755,8 +2786,8 @@ golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -2929,8 +2960,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -2941,7 +2972,7 @@ golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -3073,6 +3104,8 @@ golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -3148,8 +3181,8 @@ google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6r
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
|
||||
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
|
||||
google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg=
|
||||
google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -3159,8 +3192,9 @@ google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -3274,12 +3308,12 @@ google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP
|
||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
|
||||
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
@@ -3324,8 +3358,8 @@ google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200910201057-6591123024b3/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
@@ -3343,8 +3377,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"__message": "This file is now deprecated, and will be removed in a future release. No further updates should be made to this file",
|
||||
"stable": "9.4.7",
|
||||
"testing": "9.4.7"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"],
|
||||
"version": "9.5.15"
|
||||
"version": "9.5.18"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"repository": "github:grafana/grafana",
|
||||
"scripts": {
|
||||
"build": "yarn i18n:compile && NODE_ENV=production webpack --progress --config scripts/webpack/webpack.prod.js",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@grafana/schema": "9.5.15",
|
||||
"@grafana/schema": "9.5.18",
|
||||
"@types/d3-interpolate": "^3.0.0",
|
||||
"d3-interpolate": "3.0.1",
|
||||
"date-fns": "2.29.3",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana End-to-End Test Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -63,7 +63,7 @@
|
||||
"@babel/core": "7.20.5",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@cypress/webpack-preprocessor": "5.17.0",
|
||||
"@grafana/e2e-selectors": "9.5.15",
|
||||
"@grafana/e2e-selectors": "9.5.18",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@mochajs/json-file-reporter": "^1.2.0",
|
||||
"babel-loader": "9.1.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@grafana/eslint-plugin",
|
||||
"description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"main": "./index.cjs",
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -37,10 +37,10 @@
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "9.5.15",
|
||||
"@grafana/e2e-selectors": "9.5.15",
|
||||
"@grafana/data": "9.5.18",
|
||||
"@grafana/e2e-selectors": "9.5.18",
|
||||
"@grafana/faro-web-sdk": "1.0.2",
|
||||
"@grafana/ui": "9.5.15",
|
||||
"@grafana/ui": "9.5.18",
|
||||
"@sentry/browser": "6.19.7",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/schema",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana Schema Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/toolkit",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana Toolkit",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -51,10 +51,10 @@
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@grafana/data": "9.5.15",
|
||||
"@grafana/data": "9.5.18",
|
||||
"@grafana/eslint-config": "5.1.0",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@grafana/ui": "9.5.15",
|
||||
"@grafana/ui": "9.5.18",
|
||||
"@jest/core": "27.5.1",
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/eslint": "8.4.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -49,10 +49,10 @@
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.10.6",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@grafana/data": "9.5.15",
|
||||
"@grafana/e2e-selectors": "9.5.15",
|
||||
"@grafana/data": "9.5.18",
|
||||
"@grafana/e2e-selectors": "9.5.18",
|
||||
"@grafana/faro-web-sdk": "1.0.2",
|
||||
"@grafana/schema": "9.5.15",
|
||||
"@grafana/schema": "9.5.18",
|
||||
"@leeoniya/ufuzzy": "1.0.6",
|
||||
"@monaco-editor/react": "4.4.6",
|
||||
"@popperjs/core": "2.11.6",
|
||||
|
||||
@@ -194,6 +194,11 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Post("/api/user/signup", quota(user.QuotaTargetSrv), quota(org.QuotaTargetSrv), routing.Wrap(hs.SignUp))
|
||||
r.Post("/api/user/signup/step2", routing.Wrap(hs.SignUpStep2))
|
||||
|
||||
// update user email
|
||||
if hs.Cfg.Smtp.Enabled && setting.VerifyEmailEnabled {
|
||||
r.Get("/user/email/update", reqSignedInNoAnonymous, routing.Wrap(hs.UpdateUserEmail))
|
||||
}
|
||||
|
||||
// invited
|
||||
r.Get("/api/user/invite/:code", routing.Wrap(hs.GetInviteInfoByCode))
|
||||
r.Post("/api/user/invite/complete", routing.Wrap(hs.CompleteInvite))
|
||||
|
||||
@@ -234,6 +234,10 @@ func setupScenarioContext(t *testing.T, url string) *scenarioContext {
|
||||
return sc
|
||||
}
|
||||
|
||||
func authedUserWithPermissions(userID, orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
|
||||
return &user.SignedInUser{UserID: userID, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}}
|
||||
}
|
||||
|
||||
type fakeRenderService struct {
|
||||
rendering.Service
|
||||
}
|
||||
|
||||
@@ -349,6 +349,9 @@ func (hs *HTTPServer) DeleteDashboardSnapshot(c *contextmodel.ReqContext) respon
|
||||
if queryResult == nil {
|
||||
return response.Error(http.StatusNotFound, "Failed to get dashboard snapshot", nil)
|
||||
}
|
||||
if queryResult.OrgID != c.OrgID {
|
||||
return response.Error(http.StatusUnauthorized, "OrgID mismatch", nil)
|
||||
}
|
||||
|
||||
if queryResult.External {
|
||||
err := deleteExternalDashboardSnapshot(queryResult.ExternalDeleteURL)
|
||||
|
||||
@@ -44,6 +44,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
|
||||
dashSnapSvc.On("DeleteDashboardSnapshot", mock.Anything, mock.AnythingOfType("*dashboardsnapshots.DeleteDashboardSnapshotCommand")).Return(nil).Maybe()
|
||||
res := &dashboardsnapshots.DashboardSnapshot{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
Key: "12345",
|
||||
DeleteKey: "54321",
|
||||
Dashboard: jsonModel,
|
||||
@@ -103,12 +104,11 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshotByDeleteKey
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{"deleteKey": "12345"}).exec()
|
||||
|
||||
require.Equal(t, 200, sc.resp.Code)
|
||||
require.Equal(t, 200, sc.resp.Code, "BODY: "+sc.resp.Body.String())
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, strings.HasPrefix(respJSON.Get("message").MustString(), "Snapshot deleted"))
|
||||
assert.Equal(t, 1, respJSON.Get("id").MustInt())
|
||||
|
||||
assert.Equal(t, http.MethodGet, externalRequest.Method)
|
||||
assert.Equal(t, ts.URL, fmt.Sprintf("http://%s", externalRequest.Host))
|
||||
@@ -333,7 +333,7 @@ func TestGetDashboardSnapshotNotFound(t *testing.T) {
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshot
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, sc.resp.Code)
|
||||
assert.Equal(t, http.StatusNotFound, sc.resp.Code, "BODY: "+sc.resp.Body.String())
|
||||
}, sqlmock)
|
||||
|
||||
loggedInUserScenarioWithRole(t,
|
||||
@@ -344,7 +344,7 @@ func TestGetDashboardSnapshotNotFound(t *testing.T) {
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshotByDeleteKey
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"deleteKey": "12345"}).exec()
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, sc.resp.Code)
|
||||
assert.Equal(t, http.StatusNotFound, sc.resp.Code, "BODY: "+sc.resp.Body.String())
|
||||
}, sqlmock)
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ func TestGetDashboardSnapshotFailure(t *testing.T) {
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshot
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
|
||||
|
||||
assert.Equal(t, http.StatusForbidden, sc.resp.Code)
|
||||
assert.Equal(t, http.StatusForbidden, sc.resp.Code, "BODY: "+sc.resp.Body.String())
|
||||
}, sqlmock)
|
||||
|
||||
loggedInUserScenarioWithRole(t,
|
||||
@@ -418,7 +418,7 @@ func TestGetDashboardSnapshotFailure(t *testing.T) {
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshotByDeleteKey
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"deleteKey": "12345"}).exec()
|
||||
|
||||
assert.Equal(t, http.StatusInternalServerError, sc.resp.Code)
|
||||
assert.Equal(t, http.StatusInternalServerError, sc.resp.Code, "BODY: "+sc.resp.Body.String())
|
||||
}, sqlmock)
|
||||
|
||||
loggedInUserScenarioWithRole(t,
|
||||
@@ -429,7 +429,7 @@ func TestGetDashboardSnapshotFailure(t *testing.T) {
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshotByDeleteKey
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"deleteKey": "12345"}).exec()
|
||||
|
||||
assert.Equal(t, http.StatusForbidden, sc.resp.Code)
|
||||
assert.Equal(t, http.StatusForbidden, sc.resp.Code, "BODY: "+sc.resp.Body.String())
|
||||
}, sqlmock)
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ type HTTPServer struct {
|
||||
authInfoService login.AuthInfoService
|
||||
authenticator loginpkg.Authenticator
|
||||
teamPermissionsService accesscontrol.TeamPermissionsService
|
||||
NotificationService *notifications.NotificationService
|
||||
NotificationService notifications.Service
|
||||
DashboardService dashboards.DashboardService
|
||||
dashboardProvisioningService dashboards.DashboardProvisioningService
|
||||
folderService folder.Service
|
||||
@@ -236,7 +236,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore,
|
||||
teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service,
|
||||
authInfoService login.AuthInfoService, storageService store.StorageService, httpEntityStore httpentitystore.HTTPEntityStore,
|
||||
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
||||
notificationService notifications.Service, dashboardService dashboards.DashboardService,
|
||||
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
|
||||
datasourcePermissionsService permissions.DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService,
|
||||
dashboardsnapshotsService dashboardsnapshots.Service, pluginSettings pluginSettings.Service,
|
||||
|
||||
190
pkg/api/user.go
190
pkg/api/user.go
@@ -4,16 +4,22 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -117,6 +123,7 @@ func (hs *HTTPServer) GetUserByLoginOrEmail(c *contextmodel.ReqContext) response
|
||||
// 200: okResponse
|
||||
// 401: unauthorisedError
|
||||
// 403: forbiddenError
|
||||
// 409: conflictError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) UpdateSignedInUser(c *contextmodel.ReqContext) response.Response {
|
||||
cmd := user.UpdateUserCommand{}
|
||||
@@ -151,6 +158,7 @@ func (hs *HTTPServer) UpdateSignedInUser(c *contextmodel.ReqContext) response.Re
|
||||
// 401: unauthorisedError
|
||||
// 403: forbiddenError
|
||||
// 404: notFoundError
|
||||
// 409: conflictError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) UpdateUser(c *contextmodel.ReqContext) response.Response {
|
||||
cmd := user.UpdateUserCommand{}
|
||||
@@ -212,6 +220,39 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC
|
||||
}
|
||||
}
|
||||
|
||||
// If email is being updated, we need to verify it. Likewise, if username is being updated and the new username
|
||||
// is an email, we also need to verify it.
|
||||
// To avoid breaking changes, email verification is implemented in a way that if the email field is being updated,
|
||||
// all the other fields being updated in the same request are disregarded. We do this because email might need to
|
||||
// be verified and if so, it goes through a different code flow.
|
||||
if hs.Cfg.Smtp.Enabled && setting.VerifyEmailEnabled {
|
||||
query := user.GetUserByIDQuery{ID: cmd.UserID}
|
||||
usr, err := hs.userService.GetByID(ctx, &query)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
return response.Error(http.StatusNotFound, user.ErrUserNotFound.Error(), nil)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get user", err)
|
||||
}
|
||||
|
||||
if len(cmd.Email) != 0 && usr.Email != cmd.Email {
|
||||
// Email is being updated
|
||||
newEmail, err := ValidateAndNormalizeEmail(cmd.Email)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Invalid email address", err)
|
||||
}
|
||||
|
||||
return hs.verifyEmailUpdate(ctx, newEmail, user.EmailUpdateAction, usr)
|
||||
}
|
||||
if len(cmd.Login) != 0 && usr.Login != cmd.Login {
|
||||
// Username is being updated. If it's an email, go through the email verification flow
|
||||
newEmailLogin, err := ValidateAndNormalizeEmail(cmd.Login)
|
||||
if err == nil && newEmailLogin != usr.Email {
|
||||
return hs.verifyEmailUpdate(ctx, newEmailLogin, user.LoginUpdateAction, usr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := hs.userService.Update(ctx, &cmd); err != nil {
|
||||
if errors.Is(err, user.ErrCaseInsensitive) {
|
||||
return response.Error(http.StatusConflict, "Update would result in user login conflict", err)
|
||||
@@ -222,6 +263,104 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC
|
||||
return response.Success("User updated")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) verifyEmailUpdate(ctx context.Context, email string, field user.UpdateEmailActionType, usr *user.User) response.Response {
|
||||
// Verify that email is not already being used
|
||||
query := user.GetUserByLoginQuery{LoginOrEmail: email}
|
||||
existingUsr, err := hs.userService.GetByLogin(ctx, &query)
|
||||
if err != nil && !errors.Is(err, user.ErrUserNotFound) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to validate if email is already in use", err)
|
||||
}
|
||||
if existingUsr != nil {
|
||||
return response.Error(http.StatusConflict, "Email is already being used", nil)
|
||||
}
|
||||
|
||||
// Invalidate any pending verifications for this user
|
||||
expireCmd := tempuser.ExpirePreviousVerificationsCommand{InvitedByUserID: usr.ID}
|
||||
err = hs.tempUserService.ExpirePreviousVerifications(ctx, &expireCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Could not invalidate pending email verifications", err)
|
||||
}
|
||||
|
||||
code, err := util.GetRandomString(20)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to generate random string", err)
|
||||
}
|
||||
|
||||
tempCmd := tempuser.CreateTempUserCommand{
|
||||
OrgID: -1,
|
||||
Email: email,
|
||||
Code: code,
|
||||
Status: tempuser.TmpUserEmailUpdateStarted,
|
||||
// used to fetch the User in the second step of the verification flow
|
||||
InvitedByUserID: usr.ID,
|
||||
// used to determine if the user was updating their email or username in the second step of the verification flow
|
||||
Name: string(field),
|
||||
}
|
||||
|
||||
tempUser, err := hs.tempUserService.CreateTempUser(ctx, &tempCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to create email change", err)
|
||||
}
|
||||
|
||||
emailCmd := notifications.SendVerifyEmailCommand{Email: tempUser.Email, Code: tempUser.Code, User: usr}
|
||||
err = hs.NotificationService.SendVerificationEmail(ctx, &emailCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to send verification email", err)
|
||||
}
|
||||
|
||||
// Record email as sent
|
||||
emailSentCmd := tempuser.UpdateTempUserWithEmailSentCommand{Code: tempUser.Code}
|
||||
err = hs.tempUserService.UpdateTempUserWithEmailSent(ctx, &emailSentCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to record verification email", err)
|
||||
}
|
||||
|
||||
return response.Success("Email sent for verification")
|
||||
}
|
||||
|
||||
// swagger:route GET /user/email/update user updateUserEmail
|
||||
//
|
||||
// Update user email.
|
||||
//
|
||||
// Update the email of user given a verification code.
|
||||
//
|
||||
// Responses:
|
||||
// 302: okResponse
|
||||
func (hs *HTTPServer) UpdateUserEmail(c *contextmodel.ReqContext) response.Response {
|
||||
var err error
|
||||
|
||||
q := c.Req.URL.Query()
|
||||
code, err := url.QueryUnescape(q.Get("code"))
|
||||
if err != nil || code == "" {
|
||||
return hs.RedirectResponseWithError(c, errors.New("bad request data"))
|
||||
}
|
||||
|
||||
tempUser, err := hs.validateEmailCode(c.Req.Context(), code)
|
||||
if err != nil {
|
||||
return hs.RedirectResponseWithError(c, err)
|
||||
}
|
||||
|
||||
cmd, err := hs.updateCmdFromEmailVerification(c.Req.Context(), tempUser)
|
||||
if err != nil {
|
||||
return hs.RedirectResponseWithError(c, err)
|
||||
}
|
||||
|
||||
if err := hs.userService.Update(c.Req.Context(), cmd); err != nil {
|
||||
if errors.Is(err, user.ErrCaseInsensitive) {
|
||||
return hs.RedirectResponseWithError(c, errors.New("update would result in user login conflict"))
|
||||
}
|
||||
return hs.RedirectResponseWithError(c, errors.New("failed to update user"))
|
||||
}
|
||||
|
||||
// Mark temp user as completed
|
||||
updateTmpUserCmd := tempuser.UpdateTempUserStatusCommand{Code: code, Status: tempuser.TmpUserEmailUpdateCompleted}
|
||||
if err := hs.tempUserService.UpdateTempUserStatus(c.Req.Context(), &updateTmpUserCmd); err != nil {
|
||||
return hs.RedirectResponseWithError(c, errors.New("failed to update verification status"))
|
||||
}
|
||||
|
||||
return response.Redirect(hs.Cfg.AppSubURL + "/profile")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) isExternalUser(ctx context.Context, userID int64) (bool, error) {
|
||||
getAuthQuery := login.GetAuthInfoQuery{UserId: userID}
|
||||
var err error
|
||||
@@ -524,6 +663,57 @@ func (hs *HTTPServer) ClearHelpFlags(c *contextmodel.ReqContext) response.Respon
|
||||
return response.JSON(http.StatusOK, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) updateCmdFromEmailVerification(ctx context.Context, tempUser *tempuser.TempUserDTO) (*user.UpdateUserCommand, error) {
|
||||
userQuery := user.GetUserByLoginQuery{LoginOrEmail: tempUser.InvitedByLogin}
|
||||
usr, err := hs.userService.GetByLogin(ctx, &userQuery)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
return nil, user.ErrUserNotFound
|
||||
}
|
||||
return nil, errors.New("failed to get user")
|
||||
}
|
||||
|
||||
cmd := &user.UpdateUserCommand{UserID: usr.ID, Email: tempUser.Email}
|
||||
|
||||
switch tempUser.Name {
|
||||
case string(user.EmailUpdateAction):
|
||||
// User updated the email field
|
||||
if _, err := mail.ParseAddress(usr.Login); err == nil {
|
||||
// If username was also an email, we update it to keep it in sync with the email field
|
||||
cmd.Login = tempUser.Email
|
||||
}
|
||||
case string(user.LoginUpdateAction):
|
||||
// User updated the username field with a new email
|
||||
cmd.Login = tempUser.Email
|
||||
default:
|
||||
return nil, errors.New("trying to update email on unknown field")
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) validateEmailCode(ctx context.Context, code string) (*tempuser.TempUserDTO, error) {
|
||||
tempUserQuery := tempuser.GetTempUserByCodeQuery{Code: code}
|
||||
tempUser, err := hs.tempUserService.GetTempUserByCode(ctx, &tempUserQuery)
|
||||
if err != nil {
|
||||
if errors.Is(err, tempuser.ErrTempUserNotFound) {
|
||||
return nil, errors.New("invalid email verification code")
|
||||
}
|
||||
return nil, errors.New("failed to read temp user")
|
||||
}
|
||||
|
||||
if tempUser.Status != tempuser.TmpUserEmailUpdateStarted {
|
||||
return nil, errors.New("invalid email verification code")
|
||||
}
|
||||
if !tempUser.EmailSent {
|
||||
return nil, errors.New("verification email was not recorded as sent")
|
||||
}
|
||||
if tempUser.EmailSentOn.Add(hs.Cfg.VerificationEmailMaxLifetime).Before(time.Now()) {
|
||||
return nil, errors.New("invalid email verification code")
|
||||
}
|
||||
|
||||
return tempUser, nil
|
||||
}
|
||||
|
||||
// swagger:parameters searchUsers
|
||||
type SearchUsersParams struct {
|
||||
// Limit the maximum number of users to return per page
|
||||
|
||||
@@ -5,9 +5,18 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -38,6 +47,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const newEmail = "newEmail@localhost"
|
||||
|
||||
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
||||
settings := setting.NewCfg()
|
||||
sqlStore := db.InitTestDB(t)
|
||||
@@ -242,6 +253,627 @@ func TestHTTPServer_UpdateUser(t *testing.T) {
|
||||
}, hs)
|
||||
}
|
||||
|
||||
func TestUser_UpdateEmail(t *testing.T) {
|
||||
doReq := func(req *http.Request, usr *user.User) (*http.Response, error) {
|
||||
r := webtest.RequestWithSignedInUser(
|
||||
req,
|
||||
authedUserWithPermissions(
|
||||
usr.ID,
|
||||
usr.OrgID,
|
||||
[]accesscontrol.Permission{
|
||||
{
|
||||
Action: accesscontrol.ActionUsersWrite,
|
||||
Scope: accesscontrol.ScopeGlobalUsersAll,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}}
|
||||
return client.Do(r)
|
||||
}
|
||||
|
||||
sendUpdateReq := func(server *webtest.Server, usr *user.User, body string) {
|
||||
req := server.NewRequest(
|
||||
http.MethodPut,
|
||||
"/api/user",
|
||||
strings.NewReader(body),
|
||||
)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
res, err := doReq(req, usr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
|
||||
sendVerificationReq := func(server *webtest.Server, usr *user.User, code string) {
|
||||
url := fmt.Sprintf("/user/email/update?code=%s", url.QueryEscape(code))
|
||||
req := server.NewGetRequest(url)
|
||||
res, err := doReq(req, usr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusFound, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
|
||||
getVerificationTempUser := func(tempUserSvc tempuser.Service, code string) *tempuser.TempUserDTO {
|
||||
tmpUserQuery := tempuser.GetTempUserByCodeQuery{Code: code}
|
||||
tmpUser, err := tempUserSvc.GetTempUserByCode(context.Background(), &tmpUserQuery)
|
||||
require.NoError(t, err)
|
||||
return tmpUser
|
||||
}
|
||||
|
||||
verifyEmailData := func(tempUserSvc tempuser.Service, nsMock *notifications.NotificationServiceMock, originalUsr *user.User, newEmail string) {
|
||||
verification := nsMock.EmailVerification
|
||||
tmpUsr := getVerificationTempUser(tempUserSvc, verification.Code)
|
||||
|
||||
require.True(t, nsMock.EmailVerified)
|
||||
require.Equal(t, newEmail, verification.Email)
|
||||
require.Equal(t, originalUsr.ID, verification.User.ID)
|
||||
require.Equal(t, tmpUsr.Code, verification.Code)
|
||||
}
|
||||
|
||||
verifyUserNotUpdated := func(userSvc user.Service, usr *user.User) {
|
||||
userQuery := user.GetUserByIDQuery{ID: usr.ID}
|
||||
checkUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, usr.Email, checkUsr.Email)
|
||||
require.Equal(t, usr.Login, checkUsr.Login)
|
||||
require.Equal(t, usr.Name, checkUsr.Name)
|
||||
}
|
||||
|
||||
setupScenario := func(cfg *setting.Cfg) (*webtest.Server, user.Service, tempuser.Service, *notifications.NotificationServiceMock) {
|
||||
setting.VerifyEmailEnabled = true
|
||||
settings := setting.NewCfg()
|
||||
settings.Smtp.Enabled = true
|
||||
settings.VerificationEmailMaxLifetime = 1 * time.Hour
|
||||
|
||||
if cfg != nil {
|
||||
settings = cfg
|
||||
}
|
||||
|
||||
nsMock := notifications.MockNotificationService()
|
||||
sqlStore := db.InitTestDB(t)
|
||||
sqlStore.Cfg = settings
|
||||
|
||||
tempUserSvc := tempuserimpl.ProvideService(sqlStore)
|
||||
orgSvc, err := orgimpl.ProvideService(sqlStore, settings, quotatest.New(false, nil))
|
||||
require.NoError(t, err)
|
||||
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, settings, nil, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
||||
require.NoError(t, err)
|
||||
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = settings
|
||||
|
||||
hs.SQLStore = sqlStore
|
||||
hs.userService = userSvc
|
||||
hs.tempUserService = tempUserSvc
|
||||
hs.NotificationService = nsMock
|
||||
hs.SecretsService = fakes.NewFakeSecretsService()
|
||||
// User is internal
|
||||
hs.authInfoService = &logintest.AuthInfoServiceFake{ExpectedError: user.ErrUserNotFound}
|
||||
})
|
||||
|
||||
return server, userSvc, tempUserSvc, nsMock
|
||||
}
|
||||
|
||||
createUser := func(userSvc user.Service, name string, email string, login string) *user.User {
|
||||
createUserCmd := user.CreateUserCommand{
|
||||
Email: email,
|
||||
Name: name,
|
||||
Login: login,
|
||||
Company: "testCompany",
|
||||
IsAdmin: true,
|
||||
}
|
||||
usr, err := userSvc.Create(context.Background(), &createUserCmd)
|
||||
require.NoError(t, err)
|
||||
return usr
|
||||
}
|
||||
|
||||
disabledCases := []struct {
|
||||
Name string
|
||||
Field user.UpdateEmailActionType
|
||||
}{
|
||||
{
|
||||
Name: "Updating Email field",
|
||||
Field: user.EmailUpdateAction,
|
||||
},
|
||||
{
|
||||
Name: "Updating Login (username) field",
|
||||
Field: user.LoginUpdateAction,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range disabledCases {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
t.Run("With verification disabled should update without verifying", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
smtpConfigured bool
|
||||
verifyEmailEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "SMTP not configured",
|
||||
smtpConfigured: false,
|
||||
verifyEmailEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config verify_email_enabled = false",
|
||||
smtpConfigured: true,
|
||||
verifyEmailEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config verify_email_enabled = false and SMTP not configured",
|
||||
smtpConfigured: false,
|
||||
verifyEmailEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, ttt := range tests {
|
||||
var body string
|
||||
newName := "newName"
|
||||
|
||||
settings := setting.NewCfg()
|
||||
settings.Smtp.Enabled = ttt.smtpConfigured
|
||||
server, userSvc, _, nsMock := setupScenario(settings)
|
||||
|
||||
// Override after calling setupScenario()
|
||||
setting.VerifyEmailEnabled = ttt.verifyEmailEnabled
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
switch tt.Field {
|
||||
case user.LoginUpdateAction:
|
||||
body = fmt.Sprintf(`{"login": "%s", "name": "%s"}`, newEmail, newName)
|
||||
case user.EmailUpdateAction:
|
||||
body = fmt.Sprintf(`{"email": "%s", "login": "%s", "name": "%s"}`, newEmail, originalUsr.Login, newName)
|
||||
}
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify that email has not been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
|
||||
switch tt.Field {
|
||||
case user.LoginUpdateAction:
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
case user.EmailUpdateAction:
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
}
|
||||
|
||||
// Verify name has been updated
|
||||
require.NotEqual(t, originalUsr.Name, updatedUsr.Name)
|
||||
require.Equal(t, newName, updatedUsr.Name)
|
||||
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Company, updatedUsr.Company)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Update Email and disregard other fields", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newName := "newName"
|
||||
body := fmt.Sprintf(`{"email": "%s", "name": "%s"}`, newEmail, newName)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
require.NotEqual(t, newName, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Email when Login was also an email should update both", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "email@localhost")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify Email and Login have been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Login with an email should update Email too", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"login": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify Email and Login have been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Login should not need verification if it is not an email", func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newLogin := "newLogin"
|
||||
newName := "newName"
|
||||
body := fmt.Sprintf(`{"login": "%s", "name": "%s"}`, newLogin, newName)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify that email has not been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify Login has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.NotEqual(t, originalUsr.Name, updatedUsr.Name)
|
||||
require.Equal(t, newLogin, updatedUsr.Login)
|
||||
require.Equal(t, newName, updatedUsr.Name)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
})
|
||||
|
||||
t.Run("Update Login should not need verification if it is being updated to the already configured email", func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"login": "%s"}`, originalUsr.Email)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify that email has not been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify Login has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
})
|
||||
|
||||
t.Run("Update Login and Email with different email values at once should disregard the Login update", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newLogin := "newEmail2@localhost"
|
||||
body := fmt.Sprintf(`{"email": "%s", "login": "%s"}`, newEmail, newLogin)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify only Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
// Fields unchanged
|
||||
require.NotEqual(t, newLogin, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Login and Email with different email values at once when Login was already an email should update both with Email", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "email@localhost")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newLogin := "newEmail2@localhost"
|
||||
body := fmt.Sprintf(`{"email": "%s", "login": "%s"}`, newEmail, newLogin)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify only Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.NotEqual(t, newLogin, updatedUsr.Login)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Email verification should expire", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Smtp.Enabled = true
|
||||
cfg.VerificationEmailMaxLifetime = 0 // Expire instantly
|
||||
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(cfg)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify user has not been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
})
|
||||
|
||||
t.Run("A new verification should revoke other pending verifications", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// First email verification
|
||||
firstNewEmail := "newEmail1@localhost"
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, firstNewEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, firstNewEmail)
|
||||
firstCode := nsMock.EmailVerification.Code
|
||||
|
||||
// Second email verification
|
||||
secondNewEmail := "newEmail2@localhost"
|
||||
body = fmt.Sprintf(`{"email": "%s"}`, secondNewEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, secondNewEmail)
|
||||
secondCode := nsMock.EmailVerification.Code
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Try to follow through with the first verification unsuccessfully
|
||||
sendVerificationReq(server, originalUsr, firstCode)
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Follow through with second verification successfully
|
||||
sendVerificationReq(server, originalUsr, secondCode)
|
||||
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, secondNewEmail, updatedUsr.Email)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
})
|
||||
|
||||
t.Run("Email verification should fail if code is not valid", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow should fail if using the wrong code
|
||||
sendVerificationReq(server, originalUsr, "notTheRightCode")
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
})
|
||||
|
||||
t.Run("Email verification code can only be used once", func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Start email update
|
||||
require.NotEqual(t, originalUsr.Email, newEmail)
|
||||
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Use code to verify successfully
|
||||
codeToReuse := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, codeToReuse)
|
||||
|
||||
// User should have an updated Email
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
|
||||
// Change email back to what it was
|
||||
body = fmt.Sprintf(`{"email": "%s"}`, originalUsr.Email)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
sendVerificationReq(server, originalUsr, nsMock.EmailVerification.Code)
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Re-use code to verify new email again, unsuccessfully
|
||||
sendVerificationReq(server, originalUsr, codeToReuse)
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
})
|
||||
|
||||
t.Run("Update Email with an email that is already being used should fail", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
clashLogin bool
|
||||
}{
|
||||
{
|
||||
description: "when Email clashes",
|
||||
clashLogin: false,
|
||||
},
|
||||
{
|
||||
description: "when Login clashes",
|
||||
clashLogin: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name1", "email1@localhost", "login1@localhost")
|
||||
badUsr := createUser(userSvc, "name2", "email2@localhost", "login2")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Update `badUsr` to use the same email as `originalUsr`
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, originalUsr.Email)
|
||||
if tt.clashLogin {
|
||||
body = fmt.Sprintf(`{"login": "%s"}`, originalUsr.Login)
|
||||
}
|
||||
req := server.NewRequest(
|
||||
http.MethodPut,
|
||||
"/api/user",
|
||||
strings.NewReader(body),
|
||||
)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
res, err := doReq(req, badUsr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusConflict, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
|
||||
// Verify that no email has been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify user has not been updated
|
||||
verifyUserNotUpdated(userSvc, badUsr)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type updateUserContext struct {
|
||||
desc string
|
||||
url string
|
||||
|
||||
@@ -81,32 +81,32 @@ var ArtifactConfigs = []buildArtifact{
|
||||
Arch: "arm64",
|
||||
urlPostfix: ".linux-arm64.tar.gz",
|
||||
},
|
||||
{
|
||||
Os: debOS,
|
||||
Arch: "armv7",
|
||||
urlPostfix: "_armhf.deb",
|
||||
},
|
||||
{
|
||||
Os: debOS,
|
||||
Arch: "armv6",
|
||||
packagePostfix: "-rpi",
|
||||
urlPostfix: "_armhf.deb",
|
||||
},
|
||||
{
|
||||
Os: rhelOS,
|
||||
Arch: "armv7",
|
||||
urlPostfix: ".armhfp.rpm",
|
||||
},
|
||||
{
|
||||
Os: "linux",
|
||||
Arch: "armv6",
|
||||
urlPostfix: ".linux-armv6.tar.gz",
|
||||
},
|
||||
{
|
||||
Os: "linux",
|
||||
Arch: "armv7",
|
||||
urlPostfix: ".linux-armv7.tar.gz",
|
||||
},
|
||||
// {
|
||||
// Os: debOS,
|
||||
// Arch: "armv7",
|
||||
// urlPostfix: "_armhf.deb",
|
||||
// },
|
||||
// {
|
||||
// Os: debOS,
|
||||
// Arch: "armv6",
|
||||
// packagePostfix: "-rpi",
|
||||
// urlPostfix: "_armhf.deb",
|
||||
// },
|
||||
// {
|
||||
// Os: rhelOS,
|
||||
// Arch: "armv7",
|
||||
// urlPostfix: ".armhfp.rpm",
|
||||
// },
|
||||
// {
|
||||
// Os: "linux",
|
||||
// Arch: "armv6",
|
||||
// urlPostfix: ".linux-armv6.tar.gz",
|
||||
// },
|
||||
// {
|
||||
// Os: "linux",
|
||||
// Arch: "armv7",
|
||||
// urlPostfix: ".linux-armv7.tar.gz",
|
||||
// },
|
||||
{
|
||||
Os: "darwin",
|
||||
Arch: "amd64",
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||
trace "go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -185,7 +186,7 @@ func initTracerProvider(exp tracesdk.SpanExporter, version string, customAttribs
|
||||
}
|
||||
|
||||
func (ots *Opentelemetry) initNoopTracerProvider() (tracerProvider, error) {
|
||||
return &noopTracerProvider{TracerProvider: trace.NewNoopTracerProvider()}, nil
|
||||
return &noopTracerProvider{TracerProvider: noop.NewTracerProvider()}, nil
|
||||
}
|
||||
|
||||
func (ots *Opentelemetry) initOpentelemetryTracer() error {
|
||||
|
||||
@@ -42,13 +42,12 @@ func newClientConfig(executablePath string, env []string, logger log.Logger,
|
||||
AllowedProtocols: []goplugin.Protocol{goplugin.ProtocolGRPC},
|
||||
GRPCDialOptions: []grpc.DialOption{
|
||||
grpc.WithChainUnaryInterceptor(
|
||||
otelgrpc.UnaryClientInterceptor(),
|
||||
grpc_opentracing.UnaryClientInterceptor(),
|
||||
),
|
||||
grpc.WithChainStreamInterceptor(
|
||||
otelgrpc.StreamClientInterceptor(),
|
||||
grpc_opentracing.StreamClientInterceptor(),
|
||||
),
|
||||
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package annotationsimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -14,31 +15,30 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestAnnotationCleanUp(t *testing.T) {
|
||||
func TestIntegrationAnnotationCleanUp(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test")
|
||||
}
|
||||
|
||||
fakeSQL := db.InitTestDB(t)
|
||||
|
||||
t.Cleanup(func() {
|
||||
err := fakeSQL.WithDbSession(context.Background(), func(session *db.Session) error {
|
||||
_, err := session.Exec("DELETE FROM annotation")
|
||||
return err
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
createTestAnnotations(t, fakeSQL, 21, 6)
|
||||
assertAnnotationCount(t, fakeSQL, "", 21)
|
||||
assertAnnotationTagCount(t, fakeSQL, 42)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *setting.Cfg
|
||||
alertAnnotationCount int64
|
||||
dashboardAnnotationCount int64
|
||||
APIAnnotationCount int64
|
||||
affectedAnnotations int64
|
||||
name string
|
||||
createAnnotationsNum int
|
||||
createOldAnnotationsNum int
|
||||
|
||||
cfg *setting.Cfg
|
||||
alertAnnotationCount int64
|
||||
annotationCleanupJobBatchSize int
|
||||
dashboardAnnotationCount int64
|
||||
APIAnnotationCount int64
|
||||
affectedAnnotations int64
|
||||
}{
|
||||
{
|
||||
name: "default settings should not delete any annotations",
|
||||
name: "default settings should not delete any annotations",
|
||||
createAnnotationsNum: 21,
|
||||
createOldAnnotationsNum: 6,
|
||||
annotationCleanupJobBatchSize: 1,
|
||||
cfg: &setting.Cfg{
|
||||
AlertingAnnotationCleanupSetting: settingsFn(0, 0),
|
||||
DashboardAnnotationCleanupSettings: settingsFn(0, 0),
|
||||
@@ -50,7 +50,10 @@ func TestAnnotationCleanUp(t *testing.T) {
|
||||
affectedAnnotations: 0,
|
||||
},
|
||||
{
|
||||
name: "should remove annotations created before cut off point",
|
||||
name: "should remove annotations created before cut off point",
|
||||
createAnnotationsNum: 21,
|
||||
createOldAnnotationsNum: 6,
|
||||
annotationCleanupJobBatchSize: 1,
|
||||
cfg: &setting.Cfg{
|
||||
AlertingAnnotationCleanupSetting: settingsFn(time.Hour*48, 0),
|
||||
DashboardAnnotationCleanupSettings: settingsFn(time.Hour*48, 0),
|
||||
@@ -62,7 +65,10 @@ func TestAnnotationCleanUp(t *testing.T) {
|
||||
affectedAnnotations: 6,
|
||||
},
|
||||
{
|
||||
name: "should only keep three annotations",
|
||||
name: "should only keep three annotations",
|
||||
createAnnotationsNum: 15,
|
||||
createOldAnnotationsNum: 6,
|
||||
annotationCleanupJobBatchSize: 1,
|
||||
cfg: &setting.Cfg{
|
||||
AlertingAnnotationCleanupSetting: settingsFn(0, 3),
|
||||
DashboardAnnotationCleanupSettings: settingsFn(0, 3),
|
||||
@@ -74,7 +80,10 @@ func TestAnnotationCleanUp(t *testing.T) {
|
||||
affectedAnnotations: 6,
|
||||
},
|
||||
{
|
||||
name: "running the max count delete again should not remove any annotations",
|
||||
name: "running the max count delete again should not remove any annotations",
|
||||
createAnnotationsNum: 9,
|
||||
createOldAnnotationsNum: 6,
|
||||
annotationCleanupJobBatchSize: 1,
|
||||
cfg: &setting.Cfg{
|
||||
AlertingAnnotationCleanupSetting: settingsFn(0, 3),
|
||||
DashboardAnnotationCleanupSettings: settingsFn(0, 3),
|
||||
@@ -85,12 +94,40 @@ func TestAnnotationCleanUp(t *testing.T) {
|
||||
APIAnnotationCount: 3,
|
||||
affectedAnnotations: 0,
|
||||
},
|
||||
{
|
||||
name: "should not fail if batch size is larger than SQLITE_MAX_VARIABLE_NUMBER for SQLite >= 3.32.0",
|
||||
createAnnotationsNum: 40003,
|
||||
createOldAnnotationsNum: 0,
|
||||
annotationCleanupJobBatchSize: 32767,
|
||||
cfg: &setting.Cfg{
|
||||
AlertingAnnotationCleanupSetting: settingsFn(0, 1),
|
||||
DashboardAnnotationCleanupSettings: settingsFn(0, 1),
|
||||
APIAnnotationCleanupSettings: settingsFn(0, 1),
|
||||
},
|
||||
alertAnnotationCount: 1,
|
||||
dashboardAnnotationCount: 1,
|
||||
APIAnnotationCount: 1,
|
||||
affectedAnnotations: 40000,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
createTestAnnotations(t, fakeSQL, test.createAnnotationsNum, test.createOldAnnotationsNum)
|
||||
assertAnnotationCount(t, fakeSQL, "", int64(test.createAnnotationsNum))
|
||||
assertAnnotationTagCount(t, fakeSQL, 2*int64(test.createAnnotationsNum))
|
||||
|
||||
t.Cleanup(func() {
|
||||
err := fakeSQL.WithDbSession(context.Background(), func(session *db.Session) error {
|
||||
_, deleteAnnotationErr := session.Exec("DELETE FROM annotation")
|
||||
_, deleteAnnotationTagErr := session.Exec("DELETE FROM annotation_tag")
|
||||
return errors.Join(deleteAnnotationErr, deleteAnnotationTagErr)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.AnnotationCleanupJobBatchSize = 1
|
||||
cfg.AnnotationCleanupJobBatchSize = int64(test.annotationCleanupJobBatchSize)
|
||||
cleaner := ProvideCleanupService(fakeSQL, cfg)
|
||||
affectedAnnotations, affectedAnnotationTags, err := cleaner.Run(context.Background(), test.cfg)
|
||||
require.NoError(t, err)
|
||||
@@ -111,7 +148,11 @@ func TestAnnotationCleanUp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldAnnotationsAreDeletedFirst(t *testing.T) {
|
||||
func TestIntegrationOldAnnotationsAreDeletedFirst(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test")
|
||||
}
|
||||
|
||||
fakeSQL := db.InitTestDB(t)
|
||||
|
||||
t.Cleanup(func() {
|
||||
@@ -193,8 +234,11 @@ func createTestAnnotations(t *testing.T, store db.DB, expectedCount int, oldAnno
|
||||
|
||||
cutoffDate := time.Now()
|
||||
|
||||
newAnnotations := make([]*annotations.Item, 0, expectedCount)
|
||||
newAnnotationTags := make([]*annotationTag, 0, 2*expectedCount)
|
||||
for i := 0; i < expectedCount; i++ {
|
||||
a := &annotations.Item{
|
||||
ID: int64(i + 1),
|
||||
DashboardID: 1,
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
@@ -222,22 +266,44 @@ func createTestAnnotations(t *testing.T, store db.DB, expectedCount int, oldAnno
|
||||
a.Created = cutoffDate.AddDate(-10, 0, -10).UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
err := store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
_, err := sess.Insert(a)
|
||||
require.NoError(t, err, "should be able to save annotation", err)
|
||||
|
||||
// mimick the SQL annotation Save logic by writing records to the annotation_tag table
|
||||
// we need to ensure they get deleted when we clean up annotations
|
||||
for tagID := range []int{1, 2} {
|
||||
_, err = sess.Exec("INSERT INTO annotation_tag (annotation_id, tag_id) VALUES(?,?)", a.ID, tagID)
|
||||
require.NoError(t, err, "should be able to save annotation tag ID", err)
|
||||
}
|
||||
return err
|
||||
})
|
||||
require.NoError(t, err)
|
||||
newAnnotations = append(newAnnotations, a)
|
||||
newAnnotationTags = append(newAnnotationTags, &annotationTag{AnnotationID: a.ID, TagID: 1}, &annotationTag{AnnotationID: a.ID, TagID: 2})
|
||||
}
|
||||
|
||||
err := store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
batchsize := 500
|
||||
for i := 0; i < len(newAnnotations); i += batchsize {
|
||||
_, err := sess.InsertMulti(newAnnotations[i:min(i+batchsize, len(newAnnotations))])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
batchsize := 500
|
||||
for i := 0; i < len(newAnnotationTags); i += batchsize {
|
||||
_, err := sess.InsertMulti(newAnnotationTags[i:min(i+batchsize, len(newAnnotationTags))])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func settingsFn(maxAge time.Duration, maxCount int64) setting.AnnotationCleanupSettings {
|
||||
return setting.AnnotationCleanupSettings{MaxAge: maxAge, MaxCount: maxCount}
|
||||
}
|
||||
|
||||
func min(is ...int) int {
|
||||
if len(is) == 0 {
|
||||
return 0
|
||||
}
|
||||
min := is[0]
|
||||
for _, i := range is {
|
||||
if i < min {
|
||||
min = i
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||
"github.com/grafana/grafana/pkg/services/tag"
|
||||
@@ -475,11 +476,21 @@ func (r *xormRepositoryImpl) validateTagsLength(item *annotations.Item) error {
|
||||
func (r *xormRepositoryImpl) CleanAnnotations(ctx context.Context, cfg setting.AnnotationCleanupSettings, annotationType string) (int64, error) {
|
||||
var totalAffected int64
|
||||
if cfg.MaxAge > 0 {
|
||||
cutoffDate := time.Now().Add(-cfg.MaxAge).UnixNano() / int64(time.Millisecond)
|
||||
deleteQuery := `DELETE FROM annotation WHERE id IN (SELECT id FROM (SELECT id FROM annotation WHERE %s AND created < %v ORDER BY id DESC %s) a)`
|
||||
sql := fmt.Sprintf(deleteQuery, annotationType, cutoffDate, r.db.GetDialect().Limit(r.cfg.AnnotationCleanupJobBatchSize))
|
||||
cutoffDate := timeNow().Add(-cfg.MaxAge).UnixNano() / int64(time.Millisecond)
|
||||
// Single-statement approaches, specifically ones using batched sub-queries, seem to deadlock with concurrent inserts on MySQL.
|
||||
// We have a bounded batch size, so work around this by first loading the IDs into memory and allowing any locks to flush inside each batch.
|
||||
// This may under-delete when concurrent inserts happen, but any such annotations will simply be cleaned on the next cycle.
|
||||
//
|
||||
// We execute the following batched operation repeatedly until either we run out of objects, the context is cancelled, or there is an error.
|
||||
affected, err := untilDoneOrCancelled(ctx, func() (int64, error) {
|
||||
cond := fmt.Sprintf(`%s AND created < %v ORDER BY id DESC %s`, annotationType, cutoffDate, r.db.GetDialect().Limit(r.cfg.AnnotationCleanupJobBatchSize))
|
||||
ids, err := r.fetchIDs(ctx, "annotation", cond)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
affected, err := r.executeUntilDoneOrCancelled(ctx, sql)
|
||||
return r.deleteByIDs(ctx, "annotation", ids)
|
||||
})
|
||||
totalAffected += affected
|
||||
if err != nil {
|
||||
return totalAffected, err
|
||||
@@ -487,41 +498,105 @@ func (r *xormRepositoryImpl) CleanAnnotations(ctx context.Context, cfg setting.A
|
||||
}
|
||||
|
||||
if cfg.MaxCount > 0 {
|
||||
deleteQuery := `DELETE FROM annotation WHERE id IN (SELECT id FROM (SELECT id FROM annotation WHERE %s ORDER BY id DESC %s) a)`
|
||||
sql := fmt.Sprintf(deleteQuery, annotationType, r.db.GetDialect().LimitOffset(r.cfg.AnnotationCleanupJobBatchSize, cfg.MaxCount))
|
||||
affected, err := r.executeUntilDoneOrCancelled(ctx, sql)
|
||||
// Similar strategy as the above cleanup process, to avoid deadlocks.
|
||||
affected, err := untilDoneOrCancelled(ctx, func() (int64, error) {
|
||||
cond := fmt.Sprintf(`%s ORDER BY id DESC %s`, annotationType, r.db.GetDialect().LimitOffset(r.cfg.AnnotationCleanupJobBatchSize, cfg.MaxCount))
|
||||
ids, err := r.fetchIDs(ctx, "annotation", cond)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return r.deleteByIDs(ctx, "annotation", ids)
|
||||
})
|
||||
totalAffected += affected
|
||||
return totalAffected, err
|
||||
if err != nil {
|
||||
return totalAffected, err
|
||||
}
|
||||
}
|
||||
|
||||
return totalAffected, nil
|
||||
}
|
||||
|
||||
func (r *xormRepositoryImpl) CleanOrphanedAnnotationTags(ctx context.Context) (int64, error) {
|
||||
deleteQuery := `DELETE FROM annotation_tag WHERE id IN ( SELECT id FROM (SELECT id FROM annotation_tag WHERE NOT EXISTS (SELECT 1 FROM annotation a WHERE annotation_id = a.id) %s) a)`
|
||||
sql := fmt.Sprintf(deleteQuery, r.db.GetDialect().Limit(r.cfg.AnnotationCleanupJobBatchSize))
|
||||
return r.executeUntilDoneOrCancelled(ctx, sql)
|
||||
return untilDoneOrCancelled(ctx, func() (int64, error) {
|
||||
cond := fmt.Sprintf(`NOT EXISTS (SELECT 1 FROM annotation a WHERE annotation_id = a.id) %s`, r.db.GetDialect().Limit(r.cfg.AnnotationCleanupJobBatchSize))
|
||||
ids, err := r.fetchIDs(ctx, "annotation_tag", cond)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return r.deleteByIDs(ctx, "annotation_tag", ids)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *xormRepositoryImpl) executeUntilDoneOrCancelled(ctx context.Context, sql string) (int64, error) {
|
||||
func (r *xormRepositoryImpl) fetchIDs(ctx context.Context, table, condition string) ([]int64, error) {
|
||||
sql := fmt.Sprintf(`SELECT id FROM %s`, table)
|
||||
if condition == "" {
|
||||
return nil, fmt.Errorf("condition must be supplied; cannot fetch IDs from entire table")
|
||||
}
|
||||
sql += fmt.Sprintf(` WHERE %s`, condition)
|
||||
ids := make([]int64, 0)
|
||||
err := r.db.WithDbSession(ctx, func(session *db.Session) error {
|
||||
return session.SQL(sql).Find(&ids)
|
||||
})
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func (r *xormRepositoryImpl) deleteByIDs(ctx context.Context, table string, ids []int64) (int64, error) {
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
sql := ""
|
||||
args := make([]any, 0)
|
||||
|
||||
// SQLite has a parameter limit of 999.
|
||||
// If the batch size is bigger than that, and we're on SQLite, we have to put the IDs directly into the statement.
|
||||
const sqliteParameterLimit = 999
|
||||
if r.db.GetDBType() == migrator.SQLite && r.cfg.AnnotationCleanupJobBatchSize > sqliteParameterLimit {
|
||||
values := fmt.Sprint(ids[0])
|
||||
for _, v := range ids[1:] {
|
||||
values = fmt.Sprintf("%s, %d", values, v)
|
||||
}
|
||||
sql = fmt.Sprintf(`DELETE FROM %s WHERE id IN (%s)`, table, values)
|
||||
} else {
|
||||
placeholders := "?" + strings.Repeat(",?", len(ids)-1)
|
||||
sql = fmt.Sprintf(`DELETE FROM %s WHERE id IN (%s)`, table, placeholders)
|
||||
args = asAny(ids)
|
||||
}
|
||||
|
||||
var affected int64
|
||||
err := r.db.WithDbSession(ctx, func(session *db.Session) error {
|
||||
res, err := session.Exec(append([]any{sql}, args...)...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err = res.RowsAffected()
|
||||
return err
|
||||
})
|
||||
return affected, err
|
||||
}
|
||||
|
||||
func asAny(vs []int64) []any {
|
||||
r := make([]any, len(vs))
|
||||
for i, v := range vs {
|
||||
r[i] = v
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// untilDoneOrCancelled repeatedly executes batched work until that work is either done (i.e., returns zero affected objects),
|
||||
// a batch produces an error, or the provided context is cancelled.
|
||||
// The work to be done is given as a callback that returns the number of affected objects for each batch, plus that batch's errors.
|
||||
func untilDoneOrCancelled(ctx context.Context, batchWork func() (int64, error)) (int64, error) {
|
||||
var totalAffected int64
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return totalAffected, ctx.Err()
|
||||
default:
|
||||
var affected int64
|
||||
err := r.db.WithDbSession(ctx, func(session *db.Session) error {
|
||||
res, err := session.Exec(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
affected, err = res.RowsAffected()
|
||||
totalAffected += affected
|
||||
|
||||
return err
|
||||
})
|
||||
affected, err := batchWork()
|
||||
totalAffected += affected
|
||||
if err != nil {
|
||||
return totalAffected, err
|
||||
}
|
||||
@@ -532,3 +607,8 @@ func (r *xormRepositoryImpl) executeUntilDoneOrCancelled(ctx context.Context, sq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type annotationTag struct {
|
||||
AnnotationID int64 `xorm:"annotation_id"`
|
||||
TagID int64 `xorm:"tag_id"`
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ func (srv *CleanUpService) clean(ctx context.Context) {
|
||||
{"expire old user invites", srv.expireOldUserInvites},
|
||||
{"delete stale short URLs", srv.deleteStaleShortURLs},
|
||||
{"delete stale query history", srv.deleteStaleQueryHistory},
|
||||
{"expire old email verifications", srv.expireOldVerifications},
|
||||
}
|
||||
|
||||
logger := srv.log.FromContext(ctx)
|
||||
@@ -237,6 +238,21 @@ func (srv *CleanUpService) expireOldUserInvites(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *CleanUpService) expireOldVerifications(ctx context.Context) {
|
||||
logger := srv.log.FromContext(ctx)
|
||||
maxVerificationLifetime := srv.Cfg.VerificationEmailMaxLifetime
|
||||
|
||||
cmd := tempuser.ExpireTempUsersCommand{
|
||||
OlderThan: time.Now().Add(-maxVerificationLifetime),
|
||||
}
|
||||
|
||||
if err := srv.tempUserService.ExpireOldVerifications(ctx, &cmd); err != nil {
|
||||
logger.Error("Problem expiring email verifications", "error", err.Error())
|
||||
} else {
|
||||
logger.Debug("Expired email verifications", "rows affected", cmd.NumExpired)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *CleanUpService) deleteStaleShortURLs(ctx context.Context) {
|
||||
logger := srv.log.FromContext(ctx)
|
||||
cmd := shorturls.DeleteShortUrlCommand{
|
||||
|
||||
@@ -2,13 +2,17 @@ package notifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type NotificationServiceMock struct {
|
||||
Webhook SendWebhookSync
|
||||
EmailSync SendEmailCommandSync
|
||||
Email SendEmailCommand
|
||||
ShouldError error
|
||||
Webhook SendWebhookSync
|
||||
EmailSync SendEmailCommandSync
|
||||
Email SendEmailCommand
|
||||
EmailVerified bool
|
||||
EmailVerification SendVerifyEmailCommand
|
||||
ShouldError error
|
||||
|
||||
WebhookHandler func(context.Context, *SendWebhookSync) error
|
||||
EmailHandlerSync func(context.Context, *SendEmailCommandSync) error
|
||||
@@ -39,4 +43,20 @@ func (ns *NotificationServiceMock) SendEmailCommandHandler(ctx context.Context,
|
||||
return ns.ShouldError
|
||||
}
|
||||
|
||||
func (ns *NotificationServiceMock) SendResetPasswordEmail(ctx context.Context, cmd *SendResetPasswordEmailCommand) error {
|
||||
// TODO: Implement if needed
|
||||
return ns.ShouldError
|
||||
}
|
||||
|
||||
func (ns *NotificationServiceMock) ValidateResetPasswordCode(ctx context.Context, query *ValidateResetPasswordCodeQuery, userByLogin GetUserByLoginFunc) (*user.User, error) {
|
||||
// TODO: Implement if needed
|
||||
return nil, ns.ShouldError
|
||||
}
|
||||
|
||||
func (ns *NotificationServiceMock) SendVerificationEmail(ctx context.Context, cmd *SendVerifyEmailCommand) error {
|
||||
ns.EmailVerified = true
|
||||
ns.EmailVerification = *cmd
|
||||
return ns.ShouldError
|
||||
}
|
||||
|
||||
func MockNotificationService() *NotificationServiceMock { return &NotificationServiceMock{} }
|
||||
|
||||
@@ -51,3 +51,9 @@ type SendResetPasswordEmailCommand struct {
|
||||
type ValidateResetPasswordCodeQuery struct {
|
||||
Code string
|
||||
}
|
||||
|
||||
type SendVerifyEmailCommand struct {
|
||||
User *user.User
|
||||
Code string
|
||||
Email string
|
||||
}
|
||||
|
||||
@@ -28,15 +28,25 @@ type EmailSender interface {
|
||||
SendEmailCommandHandlerSync(ctx context.Context, cmd *SendEmailCommandSync) error
|
||||
SendEmailCommandHandler(ctx context.Context, cmd *SendEmailCommand) error
|
||||
}
|
||||
type PasswordResetMailer interface {
|
||||
SendResetPasswordEmail(ctx context.Context, cmd *SendResetPasswordEmailCommand) error
|
||||
ValidateResetPasswordCode(ctx context.Context, query *ValidateResetPasswordCodeQuery, userByLogin GetUserByLoginFunc) (*user.User, error)
|
||||
}
|
||||
type EmailVerificationMailer interface {
|
||||
SendVerificationEmail(ctx context.Context, cmd *SendVerifyEmailCommand) error
|
||||
}
|
||||
type Service interface {
|
||||
WebhookSender
|
||||
EmailSender
|
||||
PasswordResetMailer
|
||||
EmailVerificationMailer
|
||||
}
|
||||
|
||||
var mailTemplates *template.Template
|
||||
var tmplResetPassword = "reset_password"
|
||||
var tmplSignUpStarted = "signup_started"
|
||||
var tmplWelcomeOnSignUp = "welcome_on_signup"
|
||||
var tmplVerifyEmail = "verify_email_update"
|
||||
|
||||
func ProvideService(bus bus.Bus, cfg *setting.Cfg, mailer Mailer, store TempUserStore) (*NotificationService, error) {
|
||||
ns := &NotificationService{
|
||||
@@ -257,6 +267,20 @@ func (ns *NotificationService) ValidateResetPasswordCode(ctx context.Context, qu
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (ns *NotificationService) SendVerificationEmail(ctx context.Context, cmd *SendVerifyEmailCommand) error {
|
||||
return ns.SendEmailCommandHandlerSync(ctx, &SendEmailCommandSync{
|
||||
SendEmailCommand: SendEmailCommand{
|
||||
To: []string{cmd.Email},
|
||||
Template: tmplVerifyEmail,
|
||||
Data: map[string]any{
|
||||
"Code": url.QueryEscape(cmd.Code),
|
||||
"Name": cmd.User.Name,
|
||||
"VerificationEmailLifetimeHours": int(ns.Cfg.VerificationEmailMaxLifetime.Hours()),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (ns *NotificationService) signUpStartedHandler(ctx context.Context, evt *events.SignUpStarted) error {
|
||||
if !setting.VerifyEmailEnabled {
|
||||
return nil
|
||||
|
||||
@@ -15,11 +15,14 @@ var (
|
||||
type TempUserStatus string
|
||||
|
||||
const (
|
||||
TmpUserSignUpStarted TempUserStatus = "SignUpStarted"
|
||||
TmpUserInvitePending TempUserStatus = "InvitePending"
|
||||
TmpUserCompleted TempUserStatus = "Completed"
|
||||
TmpUserRevoked TempUserStatus = "Revoked"
|
||||
TmpUserExpired TempUserStatus = "Expired"
|
||||
TmpUserSignUpStarted TempUserStatus = "SignUpStarted"
|
||||
TmpUserInvitePending TempUserStatus = "InvitePending"
|
||||
TmpUserCompleted TempUserStatus = "Completed"
|
||||
TmpUserRevoked TempUserStatus = "Revoked"
|
||||
TmpUserExpired TempUserStatus = "Expired"
|
||||
TmpUserEmailUpdateStarted TempUserStatus = "EmailUpdateStarted"
|
||||
TmpUserEmailUpdateCompleted TempUserStatus = "EmailUpdateCompleted"
|
||||
TmpUserEmailUpdateExpired TempUserStatus = "EmailUpdateExpired"
|
||||
)
|
||||
|
||||
// TempUser holds data for org invites and unconfirmed sign ups
|
||||
@@ -67,6 +70,12 @@ type ExpireTempUsersCommand struct {
|
||||
NumExpired int64
|
||||
}
|
||||
|
||||
type ExpirePreviousVerificationsCommand struct {
|
||||
InvitedByUserID int64
|
||||
|
||||
NumExpired int64
|
||||
}
|
||||
|
||||
type UpdateTempUserWithEmailSentCommand struct {
|
||||
Code string
|
||||
}
|
||||
|
||||
@@ -11,4 +11,6 @@ type Service interface {
|
||||
GetTempUsersQuery(ctx context.Context, query *GetTempUsersQuery) ([]*TempUserDTO, error)
|
||||
GetTempUserByCode(ctx context.Context, query *GetTempUserByCodeQuery) (*TempUserDTO, error)
|
||||
ExpireOldUserInvites(ctx context.Context, cmd *ExpireTempUsersCommand) error
|
||||
ExpireOldVerifications(ctx context.Context, cmd *ExpireTempUsersCommand) error
|
||||
ExpirePreviousVerifications(ctx context.Context, cmd *ExpirePreviousVerificationsCommand) error
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ type store interface {
|
||||
GetTempUsersQuery(ctx context.Context, query *tempuser.GetTempUsersQuery) ([]*tempuser.TempUserDTO, error)
|
||||
GetTempUserByCode(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error)
|
||||
ExpireOldUserInvites(ctx context.Context, cmd *tempuser.ExpireTempUsersCommand) error
|
||||
ExpireOldVerifications(ctx context.Context, cmd *tempuser.ExpireTempUsersCommand) error
|
||||
ExpirePreviousVerifications(ctx context.Context, cmd *tempuser.ExpirePreviousVerificationsCommand) error
|
||||
}
|
||||
|
||||
type xormStore struct {
|
||||
@@ -169,3 +171,27 @@ func (ss *xormStore) ExpireOldUserInvites(ctx context.Context, cmd *tempuser.Exp
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *xormStore) ExpireOldVerifications(ctx context.Context, cmd *tempuser.ExpireTempUsersCommand) error {
|
||||
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
var rawSQL = "UPDATE temp_user SET status = ?, updated = ? WHERE created <= ? AND status = ?"
|
||||
if result, err := sess.Exec(rawSQL, string(tempuser.TmpUserEmailUpdateExpired), time.Now().Unix(), cmd.OlderThan.Unix(), string(tempuser.TmpUserEmailUpdateStarted)); err != nil {
|
||||
return err
|
||||
} else if cmd.NumExpired, err = result.RowsAffected(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *xormStore) ExpirePreviousVerifications(ctx context.Context, cmd *tempuser.ExpirePreviousVerificationsCommand) error {
|
||||
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
var rawSQL = "UPDATE temp_user SET status = ?, updated = ? WHERE invited_by_user_id = ? AND status = ?"
|
||||
if result, err := sess.Exec(rawSQL, string(tempuser.TmpUserEmailUpdateExpired), time.Now().Unix(), cmd.InvitedByUserID, string(tempuser.TmpUserEmailUpdateStarted)); err != nil {
|
||||
return err
|
||||
} else if cmd.NumExpired, err = result.RowsAffected(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -87,7 +87,32 @@ func TestIntegrationTempUserCommandsAndQueries(t *testing.T) {
|
||||
require.False(t, queryResult[0].EmailSentOn.UTC().Before(queryResult[0].Created.UTC()))
|
||||
})
|
||||
|
||||
t.Run("Should be able expire temp user", func(t *testing.T) {
|
||||
t.Run("Should be able expire all pending verifications from a user", func(t *testing.T) {
|
||||
userID := int64(99)
|
||||
verifications := 5
|
||||
cmd := tempuser.CreateTempUserCommand{
|
||||
OrgID: -1,
|
||||
Name: "email-update",
|
||||
Code: "asd",
|
||||
Email: "e@as.co",
|
||||
Status: tempuser.TmpUserEmailUpdateStarted,
|
||||
InvitedByUserID: userID,
|
||||
}
|
||||
db := db.InitTestDB(t)
|
||||
store = &xormStore{db: db}
|
||||
|
||||
for i := 0; i < verifications; i++ {
|
||||
tempUser, err = store.CreateTempUser(context.Background(), &cmd)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
cmd2 := tempuser.ExpirePreviousVerificationsCommand{InvitedByUserID: userID}
|
||||
err := store.ExpirePreviousVerifications(context.Background(), &cmd2)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(verifications), cmd2.NumExpired)
|
||||
})
|
||||
|
||||
t.Run("Should be able expire temp user related to org invite", func(t *testing.T) {
|
||||
setup(t)
|
||||
createdAt := time.Unix(tempUser.Created, 0)
|
||||
cmd2 := tempuser.ExpireTempUsersCommand{OlderThan: createdAt.Add(1 * time.Second)}
|
||||
@@ -103,4 +128,34 @@ func TestIntegrationTempUserCommandsAndQueries(t *testing.T) {
|
||||
require.Equal(t, int64(0), cmd2.NumExpired)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Should be able expire temp user related to email verification", func(t *testing.T) {
|
||||
cmd := tempuser.CreateTempUserCommand{
|
||||
OrgID: 2256,
|
||||
Name: "email-update",
|
||||
Code: "asd",
|
||||
Email: "e@as.co",
|
||||
Status: tempuser.TmpUserEmailUpdateStarted,
|
||||
InvitedByUserID: 99,
|
||||
}
|
||||
db := db.InitTestDB(t)
|
||||
store = &xormStore{db: db}
|
||||
|
||||
tempUser, err = store.CreateTempUser(context.Background(), &cmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
createdAt := time.Unix(tempUser.Created, 0)
|
||||
cmd2 := tempuser.ExpireTempUsersCommand{OlderThan: createdAt.Add(1 * time.Second)}
|
||||
err := store.ExpireOldVerifications(context.Background(), &cmd2)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(1), cmd2.NumExpired)
|
||||
|
||||
t.Run("Should do nothing when no temp users to expire", func(t *testing.T) {
|
||||
createdAt := time.Unix(tempUser.Created, 0)
|
||||
cmd2 := tempuser.ExpireTempUsersCommand{OlderThan: createdAt.Add(1 * time.Second)}
|
||||
err := store.ExpireOldVerifications(context.Background(), &cmd2)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(0), cmd2.NumExpired)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,3 +66,19 @@ func (s *Service) ExpireOldUserInvites(ctx context.Context, cmd *tempuser.Expire
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ExpireOldVerifications(ctx context.Context, cmd *tempuser.ExpireTempUsersCommand) error {
|
||||
err := s.store.ExpireOldVerifications(ctx, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ExpirePreviousVerifications(ctx context.Context, cmd *tempuser.ExpirePreviousVerificationsCommand) error {
|
||||
err := s.store.ExpirePreviousVerifications(ctx, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const grafanaLatestJSONURL = "https://raw.githubusercontent.com/grafana/grafana/main/latest.json"
|
||||
const grafanaStableVersionURL = "https://grafana.com/api/grafana/versions/stable"
|
||||
|
||||
type GrafanaService struct {
|
||||
hasUpdate bool
|
||||
@@ -60,7 +60,7 @@ func (s *GrafanaService) IsDisabled() bool {
|
||||
func (s *GrafanaService) Run(ctx context.Context) error {
|
||||
s.instrumentedCheckForUpdates(ctx)
|
||||
|
||||
ticker := time.NewTicker(time.Minute * 10)
|
||||
ticker := time.NewTicker(time.Hour * 24)
|
||||
run := true
|
||||
|
||||
for run {
|
||||
@@ -92,13 +92,13 @@ func (s *GrafanaService) instrumentedCheckForUpdates(ctx context.Context) {
|
||||
func (s *GrafanaService) checkForUpdates(ctx context.Context) error {
|
||||
ctxLogger := s.log.FromContext(ctx)
|
||||
ctxLogger.Debug("Checking for updates")
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, grafanaLatestJSONURL, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, grafanaStableVersionURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get latest.json repo from github.com: %w", err)
|
||||
return fmt.Errorf("failed to get stable version from grafana.com: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
@@ -107,27 +107,24 @@ func (s *GrafanaService) checkForUpdates(ctx context.Context) error {
|
||||
}()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update check failed, reading response from github.com: %w", err)
|
||||
return fmt.Errorf("update check failed, reading response from grafana.com: %w", err)
|
||||
}
|
||||
|
||||
type latestJSON struct {
|
||||
Stable string `json:"stable"`
|
||||
Testing string `json:"testing"`
|
||||
type grafanaVersionJSON struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
var latest latestJSON
|
||||
var latest grafanaVersionJSON
|
||||
err = json.Unmarshal(body, &latest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal latest.json: %w", err)
|
||||
return fmt.Errorf("failed to unmarshal response from grafana.com: %w", err)
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
if strings.Contains(s.grafanaVersion, "-") {
|
||||
s.latestVersion = latest.Testing
|
||||
s.hasUpdate = !strings.HasPrefix(s.grafanaVersion, latest.Testing)
|
||||
} else {
|
||||
s.latestVersion = latest.Stable
|
||||
s.hasUpdate = latest.Stable != s.grafanaVersion
|
||||
// only check for updates in stable versions
|
||||
if !strings.Contains(s.grafanaVersion, "-") {
|
||||
s.latestVersion = latest.Version
|
||||
s.hasUpdate = latest.Version != s.grafanaVersion
|
||||
}
|
||||
|
||||
currVersion, err1 := version.NewVersion(s.grafanaVersion)
|
||||
|
||||
@@ -19,6 +19,13 @@ const (
|
||||
HelpFlagDashboardHelp1
|
||||
)
|
||||
|
||||
type UpdateEmailActionType string
|
||||
|
||||
const (
|
||||
EmailUpdateAction UpdateEmailActionType = "email-update"
|
||||
LoginUpdateAction UpdateEmailActionType = "login-update"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrCaseInsensitive = errors.New("case insensitive conflict")
|
||||
|
||||
@@ -350,9 +350,10 @@ type Cfg struct {
|
||||
DateFormats DateFormats
|
||||
|
||||
// User
|
||||
UserInviteMaxLifetime time.Duration
|
||||
HiddenUsers map[string]struct{}
|
||||
CaseInsensitiveLogin bool // Login and Email will be considered case insensitive
|
||||
UserInviteMaxLifetime time.Duration
|
||||
HiddenUsers map[string]struct{}
|
||||
CaseInsensitiveLogin bool // Login and Email will be considered case insensitive
|
||||
VerificationEmailMaxLifetime time.Duration
|
||||
|
||||
// Service Accounts
|
||||
SATokenExpirationDayLimit int
|
||||
@@ -1631,6 +1632,13 @@ func readUserSettings(iniFile *ini.File, cfg *Cfg) error {
|
||||
}
|
||||
}
|
||||
|
||||
verificationEmailMaxLifetimeVal := valueAsString(users, "verification_email_max_lifetime_duration", "1h")
|
||||
verificationEmailMaxLifetimeDuration, err := gtime.ParseDuration(verificationEmailMaxLifetimeVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.VerificationEmailMaxLifetime = verificationEmailMaxLifetimeDuration
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@grafana-plugins/input-datasource",
|
||||
"version": "9.5.15",
|
||||
"version": "9.5.18",
|
||||
"description": "Input Datasource",
|
||||
"private": true,
|
||||
"repository": {
|
||||
@@ -15,15 +15,15 @@
|
||||
},
|
||||
"author": "Grafana Labs",
|
||||
"devDependencies": {
|
||||
"@grafana/toolkit": "9.5.15",
|
||||
"@grafana/toolkit": "9.5.18",
|
||||
"@types/jest": "26.0.15",
|
||||
"@types/lodash": "4.14.149",
|
||||
"@types/react": "17.0.30",
|
||||
"lodash": "4.17.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "9.5.15",
|
||||
"@grafana/ui": "9.5.15",
|
||||
"@grafana/data": "9.5.18",
|
||||
"@grafana/ui": "9.5.18",
|
||||
"jquery": "3.5.1",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
|
||||
@@ -9902,6 +9902,9 @@
|
||||
"403": {
|
||||
"$ref": "#/responses/forbiddenError"
|
||||
},
|
||||
"409": {
|
||||
"$ref": "#/responses/conflictError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/responses/internalServerError"
|
||||
}
|
||||
@@ -9932,6 +9935,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/email/update": {
|
||||
"get": {
|
||||
"description": "Update the email of user given a verification code.",
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Update user email.",
|
||||
"operationId": "updateUserEmail",
|
||||
"responses": {
|
||||
"302": {
|
||||
"$ref": "#/responses/okResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/helpflags/clear": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -10577,6 +10595,9 @@
|
||||
"404": {
|
||||
"$ref": "#/responses/notFoundError"
|
||||
},
|
||||
"409": {
|
||||
"$ref": "#/responses/conflictError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/responses/internalServerError"
|
||||
}
|
||||
|
||||
215
public/emails/verify_email_update.html
Normal file
215
public/emails/verify_email_update.html
Normal file
@@ -0,0 +1,215 @@
|
||||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<title>{{ Subject .Subject .TemplateData "Verify your new email - {{.Name}}" }}</title>
|
||||
{{ __dangerouslyInjectHTML `<!--[if !mso]><!-->` }}
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
{{ __dangerouslyInjectHTML `<!--<![endif]-->` }}
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso]>
|
||||
<noscript>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
</noscript>
|
||||
<![endif]-->` }}
|
||||
{{ __dangerouslyInjectHTML `<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix { width:100% !important; }
|
||||
</style>
|
||||
<![endif]-->` }}
|
||||
{{ __dangerouslyInjectHTML `<!--[if !mso]><!-->` }}
|
||||
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
@import url(https://fonts.googleapis.com/css?family=Inter);
|
||||
|
||||
</style>
|
||||
{{ __dangerouslyInjectHTML `<!--<![endif]-->` }}
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width:480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<style media="screen and (min-width:480px)">
|
||||
.moz-text-html .mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@media only screen and (max-width:479px) {
|
||||
table.mj-full-width-mobile {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
td.mj-full-width-mobile {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="word-spacing:normal;">
|
||||
<div class="canvas" style="background-color: #fff;">
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color:transparent;vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:0;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:200px;">
|
||||
<img src="https://grafana.com/static/assets/img/logo_new_transparent_light_400x100.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="200" height="auto">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="background-outlook" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
|
||||
<div class="background" style="background-color: #FFF; border: 1px solid #e4e5e6; margin: 0px auto; max-width: 600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" class="txt" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family: Inter, Helvetica, Arial; font-size: 13px; line-height: 150%; text-align: left; color: #000000;">
|
||||
<h2>Hi {{ .Name }},</h2>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" class="txt" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family: Inter, Helvetica, Arial; font-size: 13px; line-height: 150%; text-align: left; color: #000000;">Please click the following link to verify your email within <strong>{{ .VerificationEmailLifetimeHours }} hour(s)</strong>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" bgcolor="#3D71D9" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:#3D71D9;" valign="middle">
|
||||
<a href="{{ .AppUrl }}user/email/update?code={{ .Code }}" rel="noopener" style="display: inline-block; background: #3D71D9; color: #ffffff; font-family: Inter, Helvetica, Arial; font-size: 13px; font-weight: normal; line-height: 120%; margin: 0; text-decoration: none; text-transform: none; padding: 10px 25px; mso-padding-alt: 0px; border-radius: 3px;" target="_blank"> Verify Email </a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" class="txt" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family: Inter, Helvetica, Arial; font-size: 13px; line-height: 150%; text-align: left; color: #000000;">You can also copy and paste this link into your browser directly:</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" class="txt" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family: Inter, Helvetica, Arial; font-size: 13px; line-height: 150%; text-align: left; color: #000000;"><a rel="noopener" href="{{ .AppUrl }}user/email/update?code={{ .Code }}" style="color: #6E9FFF;">{{ .AppUrl }}user/email/update?code={{ .Code }}</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color:transparent;vertical-align:top;" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" class="txt" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family: Inter, Helvetica, Arial; font-size: 13px; line-height: 150%; text-align: center; color: #000000;">© {{ now | date "2006" }} Grafana Labs. Sent by <a href="{{ .AppUrl }}" style="color: #6E9FFF;">Grafana v{{ .BuildVersion }}</a>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
9
public/emails/verify_email_update.txt
Normal file
9
public/emails/verify_email_update.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
{{HiddenSubject .Subject "Verify your new email - {{.Name}}"}}
|
||||
|
||||
Hi {{.Name}},
|
||||
|
||||
Copy and paste the following link directly in your browser to verify your email within {{.VerificationEmailLifetimeHours}} hour(s).
|
||||
{{.AppUrl}}user/email/update?code={{.Code}}
|
||||
|
||||
|
||||
Sent by Grafana v{{.BuildVersion}} (c) {{now | date "2006"}} Grafana Labs
|
||||
@@ -21470,6 +21470,9 @@
|
||||
"403": {
|
||||
"$ref": "#/components/responses/forbiddenError"
|
||||
},
|
||||
"409": {
|
||||
"$ref": "#/components/responses/conflictError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/internalServerError"
|
||||
}
|
||||
@@ -21504,6 +21507,21 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/user/email/update": {
|
||||
"get": {
|
||||
"description": "Update the email of user given a verification code.",
|
||||
"operationId": "updateUserEmail",
|
||||
"responses": {
|
||||
"302": {
|
||||
"$ref": "#/components/responses/okResponse"
|
||||
}
|
||||
},
|
||||
"summary": "Update user email.",
|
||||
"tags": [
|
||||
"user"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/user/helpflags/clear": {
|
||||
"get": {
|
||||
"operationId": "clearHelpFlags",
|
||||
@@ -22174,6 +22192,9 @@
|
||||
"404": {
|
||||
"$ref": "#/components/responses/notFoundError"
|
||||
},
|
||||
"409": {
|
||||
"$ref": "#/components/responses/conflictError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/internalServerError"
|
||||
}
|
||||
|
||||
@@ -8,14 +8,13 @@ load(
|
||||
"nodejs_version",
|
||||
)
|
||||
|
||||
# "go" image can be switched back to golang:{}-alpine once this is resolved https://github.com/mattn/go-sqlite3/pull/1177#issuecomment-1849176090
|
||||
images = {
|
||||
"git": "alpine/git:2.40.1",
|
||||
"go": "golang:{}-alpine3.18".format(golang_version),
|
||||
"go": "golang:{}-alpine".format(golang_version),
|
||||
"node": "node:{}-alpine".format(nodejs_version),
|
||||
"cloudsdk": "google/cloud-sdk:431.0.0",
|
||||
"publish": "grafana/grafana-ci-deploy:1.3.3",
|
||||
"alpine": "alpine:3.18.4",
|
||||
"alpine": "alpine:3.19.1",
|
||||
"ubuntu": "ubuntu:22.04",
|
||||
"curl": "byrnedo/alpine-curl:0.1.8",
|
||||
"plugins_slack": "plugins/slack",
|
||||
|
||||
@@ -6,7 +6,11 @@ load(
|
||||
"scripts/drone/steps/lib.star",
|
||||
"slack_step",
|
||||
)
|
||||
load("scripts/drone/vault.star", "pull_secret")
|
||||
load(
|
||||
"scripts/drone/vault.star",
|
||||
"gar_pull_secret",
|
||||
"gcr_pull_secret",
|
||||
)
|
||||
|
||||
failure_template = "Build {{build.number}} failed for commit: <https://github.com/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{ truncate build.commit 8 }}>: {{build.link}}\nBranch: <https://github.com/{{ repo.owner }}/{{ repo.name }}/commits/{{ build.branch }}|{{ build.branch }}>\nAuthor: {{build.author}}"
|
||||
|
||||
@@ -83,7 +87,7 @@ def pipeline(
|
||||
},
|
||||
],
|
||||
"depends_on": depends_on,
|
||||
"image_pull_secrets": [pull_secret],
|
||||
"image_pull_secrets": [gcr_pull_secret, gar_pull_secret],
|
||||
}
|
||||
if environment:
|
||||
pipeline.update(
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
global variables
|
||||
"""
|
||||
|
||||
grabpl_version = "v3.0.46"
|
||||
golang_version = "1.21.5"
|
||||
grabpl_version = "v3.0.50"
|
||||
golang_version = "1.21.8"
|
||||
|
||||
# nodejs_version should match what's in ".nvmrc", but without the v prefix.
|
||||
nodejs_version = "18.12.0"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""
|
||||
This module returns functions for generating Drone secrets fetched from Vault.
|
||||
"""
|
||||
pull_secret = "dockerconfigjson"
|
||||
gcr_pull_secret = "gcr"
|
||||
gar_pull_secret = "gar"
|
||||
drone_token = "drone_token"
|
||||
prerelease_bucket = "prerelease_bucket"
|
||||
gcp_upload_artifacts_key = "gcp_upload_artifacts_key"
|
||||
@@ -43,7 +44,8 @@ def secrets():
|
||||
vault_secret(gcp_grafanauploads, "infra/data/ci/grafana-release-eng/grafanauploads", "credentials.json"),
|
||||
vault_secret(gcp_grafanauploads_base64, "infra/data/ci/grafana-release-eng/grafanauploads", "credentials_base64"),
|
||||
vault_secret("grafana_api_key", "infra/data/ci/grafana-release-eng/grafanacom", "api_key"),
|
||||
vault_secret(pull_secret, "secret/data/common/gcr", ".dockerconfigjson"),
|
||||
vault_secret(gcr_pull_secret, "secret/data/common/gcr", ".dockerconfigjson"),
|
||||
vault_secret(gar_pull_secret, "secret/data/common/gar", ".dockerconfigjson"),
|
||||
vault_secret("github_token", "infra/data/ci/github/grafanabot", "pat"),
|
||||
vault_secret(drone_token, "infra/data/ci/drone", "machine-user-token"),
|
||||
vault_secret(prerelease_bucket, "infra/data/ci/grafana/prerelease", "bucket"),
|
||||
|
||||
@@ -12,8 +12,6 @@ ERSION_DEB="${ERSION//-/\~}"
|
||||
ASSETS=$(cat << EOF
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION_DEB}-1.aarch64.rpm
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION_DEB}-1.aarch64.rpm.sha256
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION_DEB}-1.armhfp.rpm
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION_DEB}-1.armhfp.rpm.sha256
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION_DEB}-1.x86_64.rpm
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION_DEB}-1.x86_64.rpm.sha256
|
||||
gs://${BUCKET}/artifacts/downloads/${VERSION}/oss/release/grafana-${ERSION}.darwin-amd64.tar.gz
|
||||
|
||||
36
yarn.lock
36
yarn.lock
@@ -2998,9 +2998,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana-plugins/input-datasource@workspace:plugins-bundled/internal/input-datasource"
|
||||
dependencies:
|
||||
"@grafana/data": 9.5.15
|
||||
"@grafana/toolkit": 9.5.15
|
||||
"@grafana/ui": 9.5.15
|
||||
"@grafana/data": 9.5.18
|
||||
"@grafana/toolkit": 9.5.18
|
||||
"@grafana/ui": 9.5.18
|
||||
"@types/jest": 26.0.15
|
||||
"@types/lodash": 4.14.149
|
||||
"@types/react": 17.0.30
|
||||
@@ -3033,12 +3033,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/data@9.5.15, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data":
|
||||
"@grafana/data@9.5.18, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/data@workspace:packages/grafana-data"
|
||||
dependencies:
|
||||
"@braintree/sanitize-url": 6.0.2
|
||||
"@grafana/schema": 9.5.15
|
||||
"@grafana/schema": 9.5.18
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@rollup/plugin-commonjs": 23.0.2
|
||||
"@rollup/plugin-json": 5.0.1
|
||||
@@ -3098,7 +3098,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/e2e-selectors@9.5.15, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors":
|
||||
"@grafana/e2e-selectors@9.5.18, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors"
|
||||
dependencies:
|
||||
@@ -3135,7 +3135,7 @@ __metadata:
|
||||
"@babel/core": 7.20.5
|
||||
"@babel/preset-env": 7.20.2
|
||||
"@cypress/webpack-preprocessor": 5.17.0
|
||||
"@grafana/e2e-selectors": 9.5.15
|
||||
"@grafana/e2e-selectors": 9.5.18
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@mochajs/json-file-reporter": ^1.2.0
|
||||
"@rollup/plugin-node-resolve": 15.0.1
|
||||
@@ -3295,11 +3295,11 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/runtime@workspace:packages/grafana-runtime"
|
||||
dependencies:
|
||||
"@grafana/data": 9.5.15
|
||||
"@grafana/e2e-selectors": 9.5.15
|
||||
"@grafana/data": 9.5.18
|
||||
"@grafana/e2e-selectors": 9.5.18
|
||||
"@grafana/faro-web-sdk": 1.0.2
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@grafana/ui": 9.5.15
|
||||
"@grafana/ui": 9.5.18
|
||||
"@rollup/plugin-commonjs": 23.0.2
|
||||
"@rollup/plugin-node-resolve": 15.0.1
|
||||
"@sentry/browser": 6.19.7
|
||||
@@ -3350,7 +3350,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/schema@9.5.15, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||
"@grafana/schema@9.5.18, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/schema@workspace:packages/grafana-schema"
|
||||
dependencies:
|
||||
@@ -3369,7 +3369,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/toolkit@9.5.15, @grafana/toolkit@workspace:*, @grafana/toolkit@workspace:packages/grafana-toolkit":
|
||||
"@grafana/toolkit@9.5.18, @grafana/toolkit@workspace:*, @grafana/toolkit@workspace:packages/grafana-toolkit":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/toolkit@workspace:packages/grafana-toolkit"
|
||||
dependencies:
|
||||
@@ -3385,10 +3385,10 @@ __metadata:
|
||||
"@babel/preset-env": 7.18.9
|
||||
"@babel/preset-react": 7.18.6
|
||||
"@babel/preset-typescript": 7.18.6
|
||||
"@grafana/data": 9.5.15
|
||||
"@grafana/data": 9.5.18
|
||||
"@grafana/eslint-config": 5.1.0
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@grafana/ui": 9.5.15
|
||||
"@grafana/ui": 9.5.18
|
||||
"@jest/core": 27.5.1
|
||||
"@types/command-exists": ^1.2.0
|
||||
"@types/eslint": 8.4.1
|
||||
@@ -3469,17 +3469,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/ui@9.5.15, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui":
|
||||
"@grafana/ui@9.5.18, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/ui@workspace:packages/grafana-ui"
|
||||
dependencies:
|
||||
"@babel/core": 7.20.5
|
||||
"@emotion/css": 11.10.6
|
||||
"@emotion/react": 11.10.6
|
||||
"@grafana/data": 9.5.15
|
||||
"@grafana/e2e-selectors": 9.5.15
|
||||
"@grafana/data": 9.5.18
|
||||
"@grafana/e2e-selectors": 9.5.18
|
||||
"@grafana/faro-web-sdk": 1.0.2
|
||||
"@grafana/schema": 9.5.15
|
||||
"@grafana/schema": 9.5.18
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@leeoniya/ufuzzy": 1.0.6
|
||||
"@mdx-js/react": 1.6.22
|
||||
|
||||
Reference in New Issue
Block a user