Merge branch 'main' of https://github.com/grafana/grafana into kristina/query-library-user-data

This commit is contained in:
Kristina Durivage
2024-06-18 10:37:54 -05:00
224 changed files with 4551 additions and 1010 deletions
+4 -6
View File
@@ -1320,10 +1320,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/core/services/echo/backends/analytics/RudderstackBackend.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@@ -1646,8 +1642,7 @@ exports[`better eslint`] = {
],
"public/app/features/alerting/unified/components/AlertLabels.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"]
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
],
"public/app/features/alerting/unified/components/AnnotationDetailsField.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@@ -2876,6 +2871,9 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/saving/shared.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
],
"public/app/features/dashboard-scene/scene/DashboardControls.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/dashboard-scene/scene/NavToolbarActions.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
+8 -7
View File
@@ -129,14 +129,20 @@ max-func-lines = 60
[linters]
disable-all = true
# try to keep this list sorted, please
enable = [
"asciicheck",
"bodyclose",
"depguard",
"dogsled",
"errcheck",
"errorlint",
"exhaustive",
"exportloopref",
# "gochecknoinits",
# "goconst",
# "gocritic", # Temporarily disabled on 2022-09-09, running into weird bug "ruleguard: execution error: used Run() with an empty rule set; forgot to call Load() first?"
"gocyclo",
"goimports",
"goprintffuncname",
"gosec",
@@ -145,19 +151,14 @@ enable = [
"ineffassign",
"misspell",
"nakedret",
"exportloopref",
"prealloc",
"revive",
"staticcheck",
"stylecheck",
"typecheck",
"unconvert",
"unused",
"whitespace",
"gocyclo",
"exhaustive",
"typecheck",
"asciicheck",
"errorlint",
"revive",
]
# Disabled linters (might want them later)
+5
View File
@@ -4,6 +4,7 @@ const path = require('path');
const benchmarkPlugin = require('./e2e/cypress/plugins/benchmark/index');
const readProvisions = require('./e2e/cypress/plugins/readProvisions');
const smtpTester = require('./e2e/cypress/plugins/smtpTester');
const typescriptPreprocessor = require('./e2e/cypress/plugins/typescriptPreprocessor');
module.exports = defineConfig({
@@ -27,6 +28,10 @@ module.exports = defineConfig({
benchmarkPlugin.initialize(on, config);
}
if (config.env['SMTP_PLUGIN_ENABLED'] === true) {
smtpTester.initialize(on, config);
}
on('task', {
readProvisions: (filePaths) => readProvisions({ CWD: process.cwd(), filePaths }),
});
@@ -50,6 +50,8 @@ To edit a feature toggle, follow these steps:
1. Navigate to the list of feature toggles and select your feature state overrides.
1. Click **Save changes** and wait for your Grafana instance to restart with the updated feature toggles.
{{% admonition type="note" %}}
{{< admonition type="note" >}}
If you don't have the feature toggle management page, enable the `featureToggleAdminPage` feature toggle.
Editing feature toggles with the feature toggle management page is available now in all tiers of [Grafana Cloud](/docs/grafana-cloud/).
{{% /admonition %}}
{{< /admonition >}}
@@ -32,6 +32,10 @@ All templates should be written in [text/template](https://pkg.go.dev/text/templ
Each template is evaluated whenever the alert rule is evaluated, and is evaluated for every alert separately. For example, if your alert rule has a templated summary annotation, and the alert rule has 10 firing alerts, then the template will be executed 10 times, once for each alert. You should try to avoid doing expensive computations in your templates as much as possible.
{{% admonition type="caution" %}}
Extra whitespace in label templates can break matches with notification policies.
{{% /admonition %}}
## Examples
The following examples attempt to show the most common use-cases we have seen for templates. You can use these examples verbatim, or adapt them as necessary for your use case. For more information on how to write text/template refer see [the beginner's guide to alert notification templates in Grafana](https://grafana.com/blog/2023/04/05/grafana-alerting-a-beginners-guide-to-templating-alert-notifications/).
+31 -25
View File
@@ -48,6 +48,11 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/rule-evaluation/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/rule-evaluation/
group-alert-notifications:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/notifications/group-alert-notifications/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/
---
# Introduction to Alerting
@@ -56,14 +61,15 @@ Whether you’re just starting out or you're a more experienced user of Grafana
The following diagram gives you an overview of Grafana Alerting and introduces you to some of the fundamental features that are the principles of how Grafana Alerting works.
{{< figure src="/media/docs/alerting/how-alerting-works.png" max-width="750px" caption="How Alerting works" >}}
<br/>
{{< figure src="/media/docs/alerting/alerting-configure-notifications-v2.png" max-width="750px" alt="How Grafana Alerting works" >}}
## How it works at a glance
- Grafana alerting periodically queries data sources and evaluates the condition defined in the alert rule
- Grafana Alerting periodically queries data sources and evaluates the condition defined in the alert rule
- If the condition is breached, an alert instance fires
- Firing and resolved alert instances are routed to notification policies based on matching labels
- Notifications are sent out to the contact points specified in the notification policy
- Firing (and resolved) alert instances are sent for notifications, either directly to a contact point or through notification policies for more flexibility
## Fundamentals
@@ -73,9 +79,7 @@ The following concepts are key to your understanding of how Grafana Alerting wor
An [alert rule](ref:alert-rules) consists of one or more queries and expressions that select the data you want to measure. It also contains a condition, which is the threshold that an alert rule must meet or exceed to fire.
Add labels to uniquely identify your alert rule and configure alert routing. Labels link alert rules to notification policies, so you can easily manage which policy should handle which alerts and who gets notified.
After alert rules are created, they go through various states and transitions.
In the alert rule, choose the contact point or notification policies to determine how to receive the alert notifications.
### Alert instances
@@ -91,23 +95,27 @@ A rule using the PromQL expression above creates as many alert instances as the
{{< figure src="/static/img/docs/alerting/unified/multi-dimensional-alert.png" caption="Multiple alert instances from a single alert rule" >}}
[Alert rules are frequently evaluated](ref:alert-rule-evaluation) and the state of their alert instances is updated accordingly. Only alert instances that are in a firing or resolved state are routed to notification policies to be handled.
### Notification policies
[Notification policies](ref:notification-policies) group alerts and then route them to contact points. They determine when notifications are sent, and how often notifications should be repeated.
Alert instances are matched to notification policies using label matchers. This provides a flexible way to organize and route alerts to different receivers.
Each policy consists of a set of label matchers (0 or more) that specify which alert instances (identified by their labels) they handle. Notification policies are defined as a tree structure where the root of the notification policy tree is called the **Default notification policy**. Each policy can have child policies.
{{< figure src="/media/docs/alerting/notification-routing.png" max-width="750px" caption="Notification policy routing" >}}
[Alert rules are frequently evaluated](ref:alert-rule-evaluation) and the state of their alert instances is updated accordingly. Only alert instances that are in a firing or resolved state are sent in notifications.
### Contact points
[Contact points](ref:contact-points) determine where notifications are sent. For example, you might have a contact point that sends notifications to an email address, to Slack, to an incident management system (IRM) such as Grafana OnCall or Pagerduty, or to a webhook.
[Contact points](ref:contact-points) determine the notification message and where notifications are sent. For example, you might have a contact point that sends notifications to an email address, to Slack, to an incident management system (IRM) such as Grafana OnCall or Pagerduty, or to a webhook.
Notifications sent from contact points are customizable with notification templates, which can be shared between contact points.
In the alert rule, you can choose a contact point to receive the alert notifications or use notification policies instead.
### Notification policies
[Notification policies](ref:notification-policies) provide a flexible method to handle alert notifications for larger systems.
Notification policies routes alerts to contact points via label matching. Each notification policy consists of a set of label matchers (0 or more) that specify which alert instances (identified by their labels) they handle. Notification policies are defined in a tree structure, where the root of the notification policy tree is the **Default notification policy**, which ensures all alert instances are handled.
{{< figure src="/media/docs/alerting/notification-routing.png" max-width="750px" alt="A diagram displaying how the notification policy tree routes alerts" caption="Routing firing alert instances through notification policies" >}}
<br/>
Each notification policy decides where to send the alert (contact point) and when to send the notification (timing options). Additionally, it can [group multiple firing alert instances into a single notification](ref:group-alert-notifications) to reduce alert noise.
{{< figure src="/media/docs/alerting/alerting-notification-policy-diagram-v5.png" max-width="750px" alt="A diagram of the notification policy component" >}}
### Silences and mute timings
@@ -115,16 +123,14 @@ Notifications sent from contact points are customizable with notification templa
### Architecture
Grafana Alerting is built on the Prometheus model of designing alerting systems.
Prometheus-based alerting systems have two main components:
Grafana Alerting is built on the Prometheus model of designing alerting systems. Prometheus-based alerting systems have two main components:
- An alert generator that evaluates alert rules and sends firing and resolved alerts to the alert receiver.
- An alert receiver (also known as Alertmanager) that receives the alerts and is responsible for handling them and sending their notifications.
Grafana doesn’t use Prometheus as its default alert generator because Grafana Alerting needs to work with many other data sources in addition to Prometheus.
Grafana Alerting doesn’t use Prometheus as its default alert generator because it works with many other data sources; not just Prometheus.
However, Grafana can also use Prometheus as an alert generator as well as external Alertmanagers. For more information about how to use distinct alerting systems, refer to the [Grafana alert rule types](ref:alert-rules).
However, Grafana can also use Prometheus as an alert generator (refer to [alert rule types](ref:alert-rules)), as well as external Alertmanagers.
## Design your Alerting system
@@ -68,7 +68,9 @@ Choosing how, when, and where to send your alert notifications is an important p
Start defining your [contact points](ref:contact-points) to specify how to receive your alert notifications. Then, configure your alert rules to send their alerts to either a contact point or use the [Notification Policy Tree](#notification-policies) to flexibly route alerts to contact points.
{{< figure src="/media/docs/alerting/alerting-configure-notifications.svg" max-width="750px" alt="Configure alerts to send notifications to a contact point or via notification policies" caption="Configure alerts to send notifications to a contact point or via notification policies" >}}
<br/>
{{< figure src="/media/docs/alerting/alerting-configure-notifications-v2.png" max-width="750px" alt="Configure alert rules to forward firing alerts directly to a contact point or through notification policies" caption="Configure alert rules to forward firing alerts directly to a contact point or through notification policies" >}}
## How it works at a glance
@@ -72,6 +72,8 @@ By default, once a matching policy is found, the system does not continue to loo
The default notification policy matches all alert instances. It always handles alert instances if there are no child policies or if none of the child policies match the alert instance's labels—this prevents any alerts from being missed.
If alerts use multiple labels, these labels must also be present in a notification policy to match and route notifications to a specific contact point.
{{% /admonition %}}
{{< collapse title="Routing example" >}}
@@ -84,6 +86,10 @@ The `team=security` policy is not a match and **Continue matching siblings** was
**Disk Usage – 80%** has both a `team` and `severity` label, and matches a child policy of the operations team.
{{% admonition type="note" %}}
When an alert matches both a parent policy and a child policy (like it does in this case), the routing follows the child policy (`severity`) as it provides a more specific match.
{{% /admonition %}}
**Unauthorized log entry** has a `team` label but does not match the first policy (`team=operations`) since the values are not the same, so it will continue searching and match the `team=security` policy. It does not have any child policies, so the additional `severity=high` label is ignored.
{{< /collapse >}}
@@ -106,6 +106,10 @@ Here are some commonly used built-in [variables](ref:variables-label-annotation)
CPU usage for instance1 has exceeded 80% for the last 5 minutes: 81.2345
{{% admonition type="caution" %}}
Extra whitespace in label templates can break matches with notification policies.
{{% /admonition %}}
### Template annotations
Both labels and annotations have the same structure: a set of named values; however their intended uses are different. The purpose of annotations is to add additional information to existing alerts.
@@ -62,7 +62,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected | Yes |
| `lokiQueryHints` | Enables query hints for Loki | Yes |
| `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | |
| `betterPageScrolling` | Removes CustomScrollbar from the UI, relying on native browser scrollbars | Yes |
| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes |
| `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | |
@@ -101,6 +100,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `groupToNestedTableTransformation` | Enables the group to nested table transformation |
| `newPDFRendering` | New implementation for the dashboard-to-PDF rendering |
| `ssoSettingsSAML` | Use the new SSO Settings API to configure the SAML connector |
| `openSearchBackendFlowEnabled` | Enables the backend query flow for Open Search datasource plugin |
## Experimental feature toggles
@@ -190,6 +190,7 @@ Experimental features might be changed or removed without prior notice.
| `alertingCentralAlertHistory` | Enables the new central alert history. |
| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars |
| `pinNavItems` | Enables pinning of nav items |
| `databaseReadReplica` | Use a read replica for some database queries. |
## Development feature toggles
@@ -207,6 +207,47 @@ HTTP_PORT=0
}
```
#### HTTP protocol
{{% admonition type="note" %}}
HTTPS protocol is supported in the image renderer v3.11.0 and later.
{{% /admonition %}}
Change the protocol of the server, it can be `http` or `https`. Default is `http`.
```json
{
"service": {
"protocol": "http"
}
}
```
#### HTTPS certificate and key file
Path to the image renderer certificate and key file used to start an HTTPS server.
```json
{
"service": {
"certFile": "./path/to/cert",
"certKey": "./path/to/key"
}
}
```
#### HTTPS min TLS version
Minimum TLS version allowed. Accepted values are: `TLSv1.2`, `TLSv1.3`. Default is `TLSv1.2`.
```json
{
"service": {
"minTLSVersion": "TLSv1.2"
}
}
```
#### Enable Prometheus metrics
You can enable [Prometheus](https://prometheus.io/) metrics endpoint `/metrics` using the environment variable `ENABLE_METRICS`. Node.js and render request duration metrics are included, see [Enable Prometheus metrics endpoint]({{< relref "./monitoring#enable-prometheus-metrics-endpoint" >}}) for details.
@@ -25,7 +25,9 @@ A label matchers consists of 3 distinct parts, the **label**, the **value** and
| `=~` | Select labels that regex-match the value. |
| `!~` | Select labels that do not regex-match the value. |
{{% admonition type="note" %}}
If you are using multiple label matchers, they are combined using the AND logical operator. This means that all matchers must match in order to link a rule to a policy.
{{% /admonition %}}
**Label matching example**
+5
View File
@@ -4,6 +4,7 @@ const path = require('path');
const benchmarkPlugin = require('./benchmark');
const extendConfig = require('./extendConfig');
const readProvisions = require('./readProvisions');
const smtpTester = require('./smtpTester');
const typescriptPreprocessor = require('./typescriptPreprocessor');
module.exports = (on, config) => {
@@ -11,6 +12,10 @@ module.exports = (on, config) => {
benchmarkPlugin.initialize(on, config);
}
if (config.env['SMTP_PLUGIN_ENABLED'] === true) {
smtpTester.initialize(on, config);
}
on('file:preprocessor', typescriptPreprocessor);
on('task', {
log({ message, optional }) {
+34
View File
@@ -0,0 +1,34 @@
const ms = require('smtp-tester');
const PORT = 7777;
const initialize = (on, config) => {
// starts the SMTP server at localhost:7777
const mailServer = ms.init(PORT);
console.log('mail server at port %d', PORT);
let lastEmail = {};
// process all emails
mailServer.bind((addr, id, email) => {
lastEmail[email.headers.to] = email;
});
on('task', {
resetEmails(recipient) {
if (recipient) {
console.log('reset all emails for recipient %s', recipient);
delete lastEmail[recipient];
} else {
console.log('reset all emails');
lastEmail = {};
}
},
getLastEmail(email) {
return lastEmail[email] || null;
},
});
};
exports.initialize = initialize;
+6
View File
@@ -66,6 +66,7 @@ case "$1" in
;;
"enterprise")
echo "Enterprise"
env[SMTP_PLUGIN_ENABLED]=true
CLEANUP="rm -rf ./e2e/extensions-suite"
SETUP="cp -Lr ./e2e/extensions ./e2e/extensions-suite"
enterpriseSuite=$(basename "${args[1]}")
@@ -105,6 +106,11 @@ case "$1" in
;;
esac
;;
"enterprise-smtp")
env[SMTP_PLUGIN_ENABLED]=true
cypressConfig[specPattern]=./e2e/extensions/enterprise/smtp-suite/$testFilesForSingleSuite
cypressConfig[video]=${args[1]}
;;
*)
cypressConfig[specPattern]=./e2e/"${args[0]}"/$testFilesForSingleSuite
+31 -11
View File
@@ -2,11 +2,6 @@ module github.com/grafana/grafana
go 1.22.4
// Override docker/docker to avoid:
// go: github.com/drone-runners/drone-runner-docker@v1.8.2 requires
// github.com/docker/docker@v0.0.0-00010101000000-000000000000: invalid version: unknown revision 000000000000
replace github.com/docker/docker => github.com/moby/moby v23.0.4+incompatible
// contains openapi encoder fixes. remove ASAP
replace cuelang.org/go => github.com/grafana/cue v0.0.0-20230926092038-971951014e3f // @grafana/grafana-as-code
@@ -67,7 +62,7 @@ require (
github.com/go-openapi/strfmt v0.23.0 // @grafana/alerting-backend
github.com/go-redis/redis/v8 v8.11.5 // @grafana/grafana-backend-group
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // @grafana/grafana-backend-group
github.com/go-sql-driver/mysql v1.7.1 // @grafana/grafana-search-and-storage
github.com/go-sql-driver/mysql v1.8.1 // @grafana/grafana-search-and-storage
github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group
github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group
github.com/gogo/protobuf v1.3.2 // @grafana/alerting-backend
@@ -135,6 +130,8 @@ require (
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // @grafana/alerting-backend
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // @grafana/grafana-operator-experience-squad
github.com/olekukonko/tablewriter v0.0.5 // @grafana/grafana-backend-group
github.com/openfga/api/proto v0.0.0-20240529184453-5b0b4941f3e0 // @grafana/identity-access-team
github.com/openfga/openfga v1.5.4 // @grafana/identity-access-team
github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend
github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend
github.com/prometheus/client_golang v1.19.0 // @grafana/alerting-backend
@@ -264,7 +261,6 @@ require (
github.com/dennwc/varint v1.0.0 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
@@ -297,7 +293,7 @@ require (
github.com/golang/glog v1.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/cel-go v0.17.7 // indirect
github.com/google/cel-go v0.20.1 // indirect
github.com/google/flatbuffers v24.3.25+incompatible // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@@ -390,7 +386,7 @@ require (
github.com/shopspring/decimal v1.2.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
@@ -415,7 +411,7 @@ require (
go.opentelemetry.io/otel/metric v1.26.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
@@ -440,7 +436,25 @@ require (
modernc.org/token v1.1.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/yaml v1.3.0 // indirect; @grafana-app-platform-squad
sigs.k8s.io/yaml v1.4.0 // indirect; @grafana-app-platform-squad
)
require (
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/karlseguin/ccache/v3 v3.0.5 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/natefinch/wrap v0.2.0 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/mock v0.4.0 // indirect
)
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream
@@ -456,3 +470,9 @@ exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible
// Use our fork xorm. go.work currently overrides this and points to the local ./pkg/util/xorm directory.
replace xorm.io/xorm => github.com/grafana/grafana/pkg/util/xorm v0.0.1
// lock for k8s api server compat
replace github.com/google/cel-go => github.com/google/cel-go v0.17.7
// lock for mysql tsdb compat
replace github.com/go-sql-driver/mysql => github.com/go-sql-driver/mysql v1.7.1
+122 -16
View File
@@ -1343,6 +1343,8 @@ cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9p
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
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=
@@ -1404,6 +1406,8 @@ github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fw
github.com/Azure/go-amqp v0.16.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
@@ -1469,9 +1473,13 @@ github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYr
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
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.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
@@ -1523,6 +1531,8 @@ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/apache/arrow/go/arrow v0.0.0-20210223225224-5bea62493d91/go.mod h1:c9sxoIT3YgLxH4UhLOCKaBlEojuMhVYpk4Ntv3opUTQ=
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6ICHXqG5hm0ZW5IHyeEJXoIJSOZeBLmWPNeIQ=
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
@@ -1753,6 +1763,9 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE
github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0=
github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
@@ -1767,6 +1780,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -1818,6 +1833,7 @@ github.com/digitalocean/godo v1.113.0 h1:CLtCxlP4wDAjKIQ+Hshht/UNbgAp8/J/XBH1ZtD
github.com/digitalocean/godo v1.113.0/go.mod h1:Z2mTP848Vi3IXXl5YbPekUgr4j4tOePomA+OE1Ag98w=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlmiddlecote/sqlstats v1.0.2 h1:gSU11YN23D/iY50A2zVYwgXgy072khatTsIW6UPjUtI=
github.com/dlmiddlecote/sqlstats v1.0.2/go.mod h1:0CWaIh/Th+z2aI6Q9Jpfg/o21zmGxWhbByHgQSCUQvY=
@@ -1825,10 +1841,14 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v26.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE=
github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -1906,8 +1926,9 @@ github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZ
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
@@ -1969,6 +1990,8 @@ 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/analysis v0.21.5/go.mod h1:25YcZosX9Lwz2wBsrFrrsL8bmjjXdlyP6zsr2AMy29M=
github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0=
github.com/go-openapi/analysis v0.22.2 h1:ZBmNoP2h5omLKr/srIC9bfqrUGzT6g6gNv03HE9Vpj0=
@@ -2021,9 +2044,6 @@ github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJ
github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -2396,8 +2416,9 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
@@ -2423,6 +2444,7 @@ github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
@@ -2500,6 +2522,7 @@ github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bY
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
@@ -2510,21 +2533,29 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY=
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
@@ -2590,6 +2621,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/karlseguin/ccache/v3 v3.0.5 h1:hFX25+fxzNjsRlREYsoGNa2LoVEw5mPF8wkWq/UnevQ=
github.com/karlseguin/ccache/v3 v3.0.5/go.mod h1:qxC372+Qn+IBj8Pe3KvGjHPj0sWwEF7AeZVhsNPZ6uY=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -2635,6 +2668,10 @@ github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLg
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4=
github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U=
@@ -2652,6 +2689,8 @@ github.com/linkedin/goavro/v2 v2.10.0 h1:eTBIRoInBM88gITGXYtUSqqxLTFXfOsJBiX8ZMW
github.com/linkedin/goavro/v2 v2.10.0/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
github.com/linode/linodego v1.32.0 h1:OmZzB3iON6uu84VtLFf64uKmAQqJJarvmsVguroioPI=
github.com/linode/linodego v1.32.0/go.mod h1:y8GDP9uLVH4jTB9qyrgw79qfKdYJmNCGUOJmfuiOcmI=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
@@ -2666,6 +2705,8 @@ github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
@@ -2716,6 +2757,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/microsoft/go-mssqldb v1.6.1-0.20240214161942-b65008136246 h1:KT4vTYcHqj5C5hMK5kSpyAk7MnFqfHVWLL4VqMq66S8=
github.com/microsoft/go-mssqldb v1.6.1-0.20240214161942-b65008136246/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -2768,12 +2811,19 @@ github.com/mithrandie/go-text v1.6.0 h1:8gOXTMPbMY8DJbKMTv8kHhADcJlDWXqS/YQH4SyW
github.com/mithrandie/go-text v1.6.0/go.mod h1:xCgj1xiNbI/d4xA9sLVvXkjh5B2tNx2ZT2/3rpmh8to=
github.com/mithrandie/ternary v1.1.1 h1:k/joD6UGVYxHixYmSR8EGgDFNONBMqyD373xT4QRdC4=
github.com/mithrandie/ternary v1.1.1/go.mod h1:0D9Ba3+09K2TdSZO7/bFCC0GjSXetCvYuYq0u8FY/1g=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/moby v23.0.4+incompatible h1:A/pe8vi9KIKhNbzR0G3wW4ACKDsMgXILBveMqiJNa8M=
github.com/moby/moby v23.0.4+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -2788,6 +2838,7 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
@@ -2802,6 +2853,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA=
github.com/natefinch/wrap v0.2.0 h1:IXzc/pw5KqxJv55gV0lSOcKHYuEZPGbQrOOXr/bamRk=
github.com/natefinch/wrap v0.2.0/go.mod h1:6gMHlAl12DwYEfKP3TkuykYUfLSEAvHw67itm4/KAS8=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=
@@ -2828,6 +2881,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
@@ -2881,9 +2936,15 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.3-0.20220512140940-7b36cea86235 h1:DxS3bbeUSCpMQr3mTez5PIDrS+yBeBsoDsftOhqB1Fg=
github.com/opencontainers/image-spec v1.0.3-0.20220512140940-7b36cea86235/go.mod h1:K/JAU0m27RFhDRX4PcFdIKntROP6y5Ed6O91aZYDQfs=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/openfga/api/proto v0.0.0-20240529184453-5b0b4941f3e0 h1:tSJl/atdqsDACjRQQPCbXe2GfEvcOkhdrUNmDToAjTA=
github.com/openfga/api/proto v0.0.0-20240529184453-5b0b4941f3e0/go.mod h1:XnvYrdU//9i70Aou6n4H5DJ0bdRPB3IlmE/Vx6qhnm8=
github.com/openfga/language/pkg/go v0.0.0-20240409225820-a53ea2892d6d h1:n44DfITs+CLCYJIgsryJkG2ElwOZJ3huekPZKydPi7U=
github.com/openfga/language/pkg/go v0.0.0-20240409225820-a53ea2892d6d/go.mod h1:wkI4GcY3yNNuFMU2ncHPWqBaF7XylQTkJYfBi2pIpK8=
github.com/openfga/openfga v1.5.4 h1:mVrp0uB9jNWX/5+OtZLM6YOx5Y9Y4r/D/O+LNBF/FGQ=
github.com/openfga/openfga v1.5.4/go.mod h1:+PoZg9BJeq+h3L0eR52tqNTwghSapCFtmaVKHsUK7QM=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w=
github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
@@ -2907,10 +2968,13 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
@@ -2945,7 +3009,11 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/pressly/goose/v3 v3.20.0 h1:uPJdOxF/Ipj7ABVNOAMJXSxwFXZGwMGHNqjC8e61VA0=
github.com/pressly/goose/v3 v3.20.0/go.mod h1:BRfF2GcG4FTG12QfdBVy3q1yveaf4ckL9vWwEcIO3lA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@@ -3059,6 +3127,10 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26 h1:F+GIVtGqCFxPxO46ujf8cEOP574MBoRm3gNbPXECbxs=
@@ -3074,6 +3146,12 @@ github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCR
github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec=
github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw=
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v1.7.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
@@ -3102,6 +3180,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -3110,10 +3190,13 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
@@ -3126,6 +3209,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/spyzhov/ajson v0.9.0 h1:tF46gJGOenYVj+k9K1U1XpCxVWhmiyY5PsVCAs1+OJ0=
github.com/spyzhov/ajson v0.9.0/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
@@ -3161,10 +3246,22 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/testcontainers/testcontainers-go v0.30.0 h1:jmn/XS22q4YRrcMwWg0pAwlClzs/abopbsBzrepyc4E=
github.com/testcontainers/testcontainers-go v0.30.0/go.mod h1:K+kHNGiM5zjklKjgTtcrEetF3uhWbMUyqAQoyoh8Pf0=
github.com/testcontainers/testcontainers-go/modules/mysql v0.30.0 h1:wrePvxfU/2HFALKyBqpNs6VoPPvThzHy9aN+PCxse9g=
github.com/testcontainers/testcontainers-go/modules/mysql v0.30.0/go.mod h1:Srnlf7wwA7s6K4sKKhjAoBHJcKorRINR/i5dCA4ZyGk=
github.com/testcontainers/testcontainers-go/modules/postgres v0.30.0 h1:D3HFqpZS90iRGAN7M85DFiuhPfvYvFNnx8urQ6mPAvo=
github.com/testcontainers/testcontainers-go/modules/postgres v0.30.0/go.mod h1:e1sKxwUOkqzvaqdHl/oV9mUtFmkDPTfBGp0po2tnWQU=
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
@@ -3234,6 +3331,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
@@ -3366,11 +3465,14 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
@@ -3384,10 +3486,11 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
gocloud.dev v0.25.0 h1:Y7vDq8xj7SyM848KXf32Krda2e6jQ4CLh/mTeCSqXtk=
gocloud.dev v0.25.0/go.mod h1:7HegHVCYZrMiU3IE1qtnzf/vRrDwLYnRNR3EhWX8x9Y=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -3795,6 +3898,7 @@ golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -3937,6 +4041,7 @@ golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -4656,8 +4761,9 @@ sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+s
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
+15 -19
View File
@@ -423,6 +423,7 @@ github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/KimMachineGun/automemlimit v0.6.0 h1:p/BXkH+K40Hax+PuWWPQ478hPjsp9h1CPDhLlA3Z37E=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
@@ -642,7 +643,6 @@ github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5A
github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4=
github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
@@ -743,7 +743,6 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
@@ -827,8 +826,6 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -858,6 +855,8 @@ github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd h1:hSkbZ9XSyjyBirMeqSqUrK+9HboWrweVlzRNqoBi2d4=
github.com/gobuffalo/depgen v0.1.0 h1:31atYa/UW9V5q8vMJ+W6wd64OaaTHUrCUXER358zLM4=
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
@@ -916,6 +915,7 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
@@ -965,7 +965,6 @@ github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
@@ -1001,14 +1000,11 @@ github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGU
github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY=
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw=
github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 h1:9Xm8CKtMZIXgcopfdWk/qZ1rt0HjMgfMR9nxxSeK6vk=
github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1/go.mod h1:zuHl3Hh+e9P6gmBPvcqR1HjkaWHC/csgyskg6IaFKFo=
github.com/jaegertracing/jaeger v1.41.0 h1:vVNky8dP46M2RjGaZ7qRENqylW+tBFay3h57N16Ip7M=
@@ -1025,6 +1021,7 @@ github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52Cu
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg5ax6YQEe1I0f6vtBuao=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o=
github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0=
@@ -1085,8 +1082,6 @@ github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmt
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo=
github.com/lightstep/lightstep-tracer-go v0.18.1 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk=
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M=
github.com/lyft/protoc-gen-star/v2 v2.0.3 h1:/3+/2sWyXeMLzKd1bX+ixWKgEMsULrIivpDsuaF441o=
github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA=
@@ -1132,7 +1127,6 @@ github.com/mithrandie/readline-csvq v1.2.1 h1:4cfeYeVSrqKEWi/1t7CjyhFD2yS6fm+l+o
github.com/mithrandie/readline-csvq v1.2.1/go.mod h1:ydD9Eyp3/wn8KPSNbKmMZe4RQQauCuxi26yEo4N40dk=
github.com/mithrandie/readline-csvq v1.3.0 h1:VTJEOGouJ8j27jJCD4kBBbNTxM0OdBvE1aY1tMhlqE8=
github.com/mithrandie/readline-csvq v1.3.0/go.mod h1:FKyYqDgf/G4SNov7SMFXRWO6LQLXIOeTog/NB97FZl0=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
@@ -1153,15 +1147,15 @@ github.com/nats-io/jwt/v2 v2.5.0/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+v
github.com/nats-io/nats-server/v2 v2.5.0 h1:wsnVaaXH9VRSg+A2MVg5Q727/CqxnmPLGFQ3YZYKTQg=
github.com/nats-io/nats.go v1.12.1 h1:+0ndxwUPz3CmQ2vjbXdkC1fo3FdiOQDim4gl3Mge8Qo=
github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM=
github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw=
github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -1260,9 +1254,8 @@ github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs=
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
@@ -1293,12 +1286,15 @@ github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4 h1:BN/Nyn2nWMo
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/sagikazarmark/crypt v0.6.0 h1:REOEXCs/NFY/1jOCEouMuT4zEniE5YoXbvpC5X/TLF8=
github.com/sagikazarmark/crypt v0.17.0/go.mod h1:SMtHTvdmsZMuY/bpZoqokSoChIrcJ/epOxZN58PbZDg=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
@@ -1323,7 +1319,6 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.6 h1:Oe8TPH9wAbv++YPNDKJWUnI8Q4PPWCx3UbOfH+FxiMU=
github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
@@ -1367,6 +1362,7 @@ github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvF
github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
@@ -1376,10 +1372,8 @@ github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
@@ -1432,9 +1426,12 @@ github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=
@@ -1444,7 +1441,6 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+2
View File
@@ -165,6 +165,7 @@
"css-minimizer-webpack-plugin": "6.0.0",
"cypress": "13.10.0",
"cypress-file-upload": "5.0.8",
"cypress-recurse": "^1.35.3",
"esbuild": "0.20.2",
"esbuild-loader": "4.1.0",
"esbuild-plugin-browserslist": "^0.12.0",
@@ -217,6 +218,7 @@
"rudder-sdk-js": "2.48.8",
"sass": "1.77.4",
"sass-loader": "14.2.1",
"smtp-tester": "^2.1.0",
"style-loader": "4.0.0",
"stylelint": "16.6.1",
"stylelint-config-sass-guidelines": "11.1.0",
@@ -169,7 +169,6 @@ export interface FeatureToggles {
kubernetesAggregator?: boolean;
expressionParser?: boolean;
groupByVariable?: boolean;
betterPageScrolling?: boolean;
authAPIAccessTokenAuth?: boolean;
scopeFilters?: boolean;
ssoSettingsSAML?: boolean;
@@ -194,4 +193,7 @@ export interface FeatureToggles {
azureMonitorPrometheusExemplars?: boolean;
pinNavItems?: boolean;
authZGRPCServer?: boolean;
openSearchBackendFlowEnabled?: boolean;
ssoSettingsLDAP?: boolean;
databaseReadReplica?: boolean;
}
@@ -41,12 +41,14 @@ export function usePluginLinks(options: GetPluginExtensionsOptions): {
export function usePluginComponents<Props = {}>(
options: GetPluginExtensionsOptions
): { components: Array<PluginExtensionComponent<Props>>; isLoading: boolean } {
): { components: Array<React.ComponentType<Props>>; isLoading: boolean } {
const { extensions, isLoading } = usePluginExtensions(options);
return useMemo(
() => ({
components: extensions.filter(isPluginExtensionComponent) as Array<PluginExtensionComponent<Props>>,
components: extensions
.filter(isPluginExtensionComponent)
.map(({ component }) => component as React.ComponentType<Props>),
isLoading,
}),
[extensions, isLoading]
@@ -231,9 +231,9 @@ class DataSourceWithBackend<
if (request.dashboardUID) {
headers[PluginRequestHeaders.DashboardUID] = request.dashboardUID;
}
if (request.panelId) {
headers[PluginRequestHeaders.PanelID] = `${request.panelId}`;
if (request.panelId) {
headers[PluginRequestHeaders.PanelID] = `${request.panelId}`;
}
}
if (request.panelPluginId) {
headers[PluginRequestHeaders.PanelPluginId] = `${request.panelPluginId}`;
@@ -17,6 +17,7 @@ export const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => {
option: css({
label: 'grafana-select-option',
padding: '8px',
position: 'relative',
display: 'flex',
alignItems: 'center',
flexDirection: 'row',
@@ -64,6 +65,17 @@ export const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => {
}),
optionSelected: css({
background: theme.colors.action.selected,
'&::before': {
backgroundImage: theme.colors.gradients.brandVertical,
borderRadius: theme.shape.radius.default,
content: '" "',
display: 'block',
height: '100%',
position: 'absolute',
transform: 'translateX(-50%)',
width: theme.spacing(0.5),
left: 0,
},
}),
optionDisabled: css({
label: 'grafana-select-option-disabled',
@@ -15,7 +15,7 @@ import { TableCellProps, CustomCellRendererProps, TableCellOptions } from './typ
import { getCellColors, getCellOptions } from './utils';
export const DefaultCell = (props: TableCellProps) => {
const { field, cell, tableStyles, row, cellProps, frame, rowStyled, textWrapped, height } = props;
const { field, cell, tableStyles, row, cellProps, frame, rowStyled, rowExpanded, textWrapped, height } = props;
const inspectEnabled = Boolean(field.config.custom?.inspect);
const displayValue = field.display!(cell.value);
@@ -60,7 +60,8 @@ export const DefaultCell = (props: TableCellProps) => {
isStringValue,
textShouldWrap,
textWrapped,
rowStyled
rowStyled,
rowExpanded
);
if (isStringValue) {
@@ -122,7 +123,8 @@ function getCellStyle(
isStringValue = false,
shouldWrapText = false,
textWrapped = false,
rowStyled = false
rowStyled = false,
rowExpanded = false
) {
// Setup color variables
let textColor: string | undefined = undefined;
@@ -145,7 +147,8 @@ function getCellStyle(
isStringValue,
shouldWrapText,
textWrapped,
rowStyled
rowStyled,
rowExpanded
);
}
@@ -267,6 +267,7 @@ export const RowsList = (props: RowsListProps) => {
prepareRow(row);
const expandedRowStyle = tableState.expanded[row.id] ? css({ '&:hover': { background: 'inherit' } }) : {};
const rowExpanded = nestedDataField && tableState.expanded[row.id];
if (rowHighlightIndex !== undefined && row.index === rowHighlightIndex) {
style = { ...style, backgroundColor: theme.components.table.rowHoverBackground };
@@ -304,7 +305,7 @@ export const RowsList = (props: RowsListProps) => {
onMouseLeave={onRowLeave}
>
{/*add the nested data to the DOM first to prevent a 1px border CSS issue on the last cell of the row*/}
{nestedDataField && tableState.expanded[row.id] && (
{rowExpanded && (
<ExpandedRow
nestedData={nestedDataField}
tableStyles={tableStyles}
@@ -326,6 +327,7 @@ export const RowsList = (props: RowsListProps) => {
timeRange={timeRange}
frame={data}
rowStyled={rowBg !== undefined}
rowExpanded={rowExpanded}
textWrapped={textWrapField !== undefined}
height={Number(style.height)}
/>
@@ -16,6 +16,7 @@ export interface Props {
userProps?: object;
frame: DataFrame;
rowStyled?: boolean;
rowExpanded?: boolean;
textWrapped?: boolean;
height?: number;
}
@@ -28,6 +29,7 @@ export const TableCell = ({
userProps,
frame,
rowStyled,
rowExpanded,
textWrapped,
height,
}: Props) => {
@@ -57,6 +59,7 @@ export const TableCell = ({
userProps,
frame,
rowStyled,
rowExpanded,
textWrapped,
height,
})}
@@ -19,14 +19,15 @@ export function useTableStyles(theme: GrafanaTheme2, cellHeightOption: TableCell
asCellText?: boolean,
textShouldWrap?: boolean,
textWrapped?: boolean,
rowStyled?: boolean
rowStyled?: boolean,
rowExpanded?: boolean
) => {
return css({
label: overflowOnHover ? 'cellContainerOverflow' : 'cellContainerNoOverflow',
padding: `${cellPadding}px`,
width: '100%',
// Cell height need to account for row border
height: `${rowHeight - 1}px`,
height: rowExpanded ? 'auto !important' : `${rowHeight - 1}px`,
wordBreak: textWrapped ? 'break-all' : 'inherit',
display: 'flex',
+4
View File
@@ -165,6 +165,10 @@ func (hs *HTTPServer) registerRoutes() {
)
}
if hs.Features.IsEnabledGlobally(featuremgmt.FlagDashboardRestore) {
r.Get("/dashboard/recently-deleted", reqSignedIn, hs.Index)
}
r.Get("/explore", authorize(ac.EvalPermission(ac.ActionDatasourcesExplore)), hs.Index)
r.Get("/playlists/", reqSignedIn, hs.Index)
+1
View File
@@ -36,4 +36,5 @@ require (
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
+1 -1
View File
@@ -33,4 +33,4 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+4 -4
View File
@@ -22,7 +22,7 @@ require (
require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/apache/arrow/go/v15 v15.0.2 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -53,7 +53,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/cel-go v0.17.7 // indirect
github.com/google/cel-go v0.20.1 // indirect
github.com/google/flatbuffers v24.3.25+incompatible // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
@@ -128,7 +128,7 @@ require (
go.opentelemetry.io/otel/sdk v1.26.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/net v0.26.0 // indirect
@@ -154,5 +154,5 @@ require (
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
+5 -7
View File
@@ -4,7 +4,7 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE=
github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
@@ -95,8 +95,8 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.17.7 h1:6ebJFzu1xO2n7TLtN+UBqShGBhlD85bhvglh5DpcfqQ=
github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
@@ -321,8 +321,7 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -452,5 +451,4 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMm
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+4 -4
View File
@@ -156,8 +156,8 @@ func publishPackages(cfg packaging.PublishConfig) error {
pth = path.Join(pth, product)
baseArchiveURL := fmt.Sprintf("https://dl.grafana.com/%s", pth)
var builds []buildRepr
for _, ba := range packaging.ArtifactConfigs {
builds := make([]buildRepr, len(packaging.ArtifactConfigs))
for i, ba := range packaging.ArtifactConfigs {
u := ba.GetURL(baseArchiveURL, cfg)
sha256, err := getSHA256(u)
@@ -165,12 +165,12 @@ func publishPackages(cfg packaging.PublishConfig) error {
return err
}
builds = append(builds, buildRepr{
builds[i] = buildRepr{
OS: ba.Os,
URL: u,
SHA256: string(sha256),
Arch: ba.Arch,
})
}
}
r := releaseRepr{
+5 -4
View File
@@ -23,7 +23,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.0 // @grafana/grafana-release-guild
github.com/aws/aws-sdk-go v1.51.31 // @grafana/aws-datasources
github.com/blang/semver/v4 v4.0.0 // @grafana/grafana-release-guild
github.com/docker/docker v26.0.1+incompatible // @grafana/grafana-release-guild
github.com/docker/docker v26.0.2+incompatible // @grafana/grafana-release-guild
github.com/drone/drone-cli v1.6.1 // @grafana/grafana-release-guild
github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend
github.com/golang/protobuf v1.5.4 // indirect; @grafana/grafana-backend-group
@@ -66,7 +66,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/drone-runners/drone-runner-docker v1.8.2 // indirect
github.com/drone/drone-go v1.7.1 // indirect
@@ -81,7 +81,7 @@ require (
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220512140940-7b36cea86235 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -98,8 +98,9 @@ require (
)
require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gotest.tools/v3 v3.5.1 // indirect
+4 -14
View File
@@ -14,8 +14,7 @@ cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHX
github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d/go.mod h1:3cARGAK9CfW3HoxCy1a0G4TKrdiKke8ftOMEOHyySYs=
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
@@ -53,8 +52,7 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
@@ -154,8 +152,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/moby/moby v23.0.4+incompatible h1:A/pe8vi9KIKhNbzR0G3wW4ACKDsMgXILBveMqiJNa8M=
github.com/moby/moby v23.0.4+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38=
@@ -163,8 +160,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.3-0.20220512140940-7b36cea86235 h1:DxS3bbeUSCpMQr3mTez5PIDrS+yBeBsoDsftOhqB1Fg=
github.com/opencontainers/image-spec v1.0.3-0.20220512140940-7b36cea86235/go.mod h1:K/JAU0m27RFhDRX4PcFdIKntROP6y5Ed6O91aZYDQfs=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -176,7 +172,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -187,7 +182,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@@ -198,9 +192,6 @@ github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM=
github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0=
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -332,7 +323,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+5 -3
View File
@@ -558,8 +558,9 @@ func (e *State) biSeriesSeries(labels data.Labels, op string, aSeries, bSeries S
func (e *State) walkFunc(node *parse.FuncNode) (Results, error) {
var res Results
var err error
var in []reflect.Value
for _, a := range node.Args {
in := make([]reflect.Value, len(node.Args))
for i, a := range node.Args {
var v any
switch t := a.(type) {
case *parse.StringNode:
@@ -580,7 +581,8 @@ func (e *State) walkFunc(node *parse.FuncNode) (Results, error) {
if err != nil {
return res, err
}
in = append(in, reflect.ValueOf(v))
in[i] = reflect.ValueOf(v)
}
f := reflect.ValueOf(node.F.F)
+9
View File
@@ -62,6 +62,15 @@ func InitTestDB(t sqlutil.ITestDB, opts ...InitTestDBOpt) *sqlstore.SQLStore {
return db
}
func InitTestReplDBWithCfg(t sqlutil.ITestDB, opts ...InitTestDBOpt) (*sqlstore.ReplStore, *setting.Cfg) {
return sqlstore.InitTestReplDB(t, opts...)
}
func InitTestReplDB(t sqlutil.ITestDB, opts ...InitTestDBOpt) *sqlstore.ReplStore {
db, _ := InitTestReplDBWithCfg(t, opts...)
return db
}
func InitTestDBWithCfg(t sqlutil.ITestDB, opts ...InitTestDBOpt) (*sqlstore.SQLStore, *setting.Cfg) {
return sqlstore.InitTestDB(t, opts...)
}
+9
View File
@@ -0,0 +1,9 @@
package db
import "github.com/grafana/grafana/pkg/services/sqlstore"
type ReplDB interface {
// DB is the primary database connection.
DB() *sqlstore.SQLStore
ReadReplica() *sqlstore.SQLStore
}
+2 -1
View File
@@ -444,7 +444,7 @@ func ReadLoggingConfig(modes []string, logsPath string, cfg *ini.File) error {
defaultLevelName, _ := getLogLevelFromConfig("log", "info", cfg)
defaultFilters := getFilters(util.SplitString(cfg.Section("log").Key("filters").String()))
var configLoggers []logWithFilters
configLoggers := make([]logWithFilters, 0, len(modes))
for _, mode := range modes {
mode = strings.TrimSpace(mode)
sec, err := cfg.GetSection("log." + mode)
@@ -505,6 +505,7 @@ func ReadLoggingConfig(modes []string, logsPath string, cfg *ini.File) error {
handler.filters = modeFilters
handler.maxLevel = leveloption
configLoggers = append(configLoggers, handler)
}
if len(configLoggers) > 0 {
@@ -24,7 +24,7 @@ func TestMain(m *testing.M) {
}
func TestConcurrentUsersMetrics(t *testing.T) {
sqlStore, cfg := db.InitTestDBWithCfg(t)
sqlStore, cfg := db.InitTestReplDBWithCfg(t)
statsService := statsimpl.ProvideService(&setting.Cfg{}, sqlStore)
s := createService(t, cfg, sqlStore, statsService)
@@ -42,7 +42,7 @@ func TestConcurrentUsersMetrics(t *testing.T) {
}
func TestConcurrentUsersStats(t *testing.T) {
sqlStore, cfg := db.InitTestDBWithCfg(t)
sqlStore, cfg := db.InitTestReplDBWithCfg(t)
statsService := statsimpl.ProvideService(&setting.Cfg{}, sqlStore)
s := createService(t, cfg, sqlStore, statsService)
@@ -14,7 +14,6 @@ import (
"github.com/grafana/grafana/pkg/login/social/connectors"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/featuremgmt"
ldap "github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/licensing"
secretsfake "github.com/grafana/grafana/pkg/services/secrets/fakes"
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
@@ -82,7 +81,6 @@ func TestSocialService_ProvideService(t *testing.T) {
nil,
&setting.OSSImpl{Cfg: cfg},
&licensing.OSSLicensingService{},
ldap.ProvideService(cfg),
)
for _, tc := range testCases {
@@ -195,7 +193,6 @@ func TestSocialService_ProvideService_GrafanaComGrafanaNet(t *testing.T) {
nil,
nil,
&licensing.OSSLicensingService{},
ldap.ProvideService(cfg),
)
for _, tc := range testCases {
+2
View File
@@ -7,12 +7,14 @@ const (
Core string = "core"
GrafanaAPIServer string = "grafana-apiserver"
StorageServer string = "storage-server"
ZanzanaServer string = "zanzana-server"
InstrumentationServer string = "instrumentation-server"
)
var dependencyMap = map[string][]string{
GrafanaAPIServer: {InstrumentationServer},
StorageServer: {InstrumentationServer},
ZanzanaServer: {InstrumentationServer},
Core: {},
All: {Core},
}
+1 -1
View File
@@ -71,7 +71,7 @@ func (m *service) Run(ctx context.Context) error {
return nil
}
var svcs []services.Service
svcs := make([]services.Service, 0, len(m.serviceMap))
for _, s := range m.serviceMap {
svcs = append(svcs, s)
}
@@ -93,6 +93,8 @@ func TestDefaultStaticDetectorsInspector(t *testing.T) {
plugin *plugins.Plugin
exp bool
}
//nolint:prealloc // just a test, and it'd require too much refactoring to preallocate
var tcs []tc
// Angular imports
@@ -381,11 +381,13 @@ func TestFSPathSeparatorFiles(t *testing.T) {
}
func fileList(manifest *PluginManifest) []string {
var keys []string
keys := make([]string, 0, len(manifest.Files))
for k := range manifest.Files {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
@@ -62,9 +62,10 @@ func DirAsLocalSources(pluginsPath string, class plugins.Class) ([]*LocalSource,
}
slices.Sort(pluginDirs)
var sources []*LocalSource
for _, dir := range pluginDirs {
sources = append(sources, NewLocalSource(class, []string{dir}))
sources := make([]*LocalSource, len(pluginDirs))
for i, dir := range pluginDirs {
sources[i] = NewLocalSource(class, []string{dir})
}
return sources, nil
}
@@ -46,7 +46,7 @@ func TestDirAsLocalSources(t *testing.T) {
{
name: "Directory with no subdirectories",
pluginsPath: filepath.Join(testdataDir, "pluginRootWithDist", "datasource"),
expected: nil,
expected: []*LocalSource{},
},
{
name: "Directory with a symlink to a directory",
+7 -4
View File
@@ -38,22 +38,25 @@ func (s *Service) externalPluginSources() []plugins.PluginSource {
return []plugins.PluginSource{}
}
var srcs []plugins.PluginSource
for _, src := range localSrcs {
srcs = append(srcs, src)
srcs := make([]plugins.PluginSource, len(localSrcs))
for i, src := range localSrcs {
srcs[i] = src
}
return srcs
}
func (s *Service) pluginSettingSources() []plugins.PluginSource {
var sources []plugins.PluginSource
sources := make([]plugins.PluginSource, 0, len(s.cfg.PluginSettings))
for _, ps := range s.cfg.PluginSettings {
path, exists := ps["path"]
if !exists || path == "" {
continue
}
sources = append(sources, NewLocalSource(plugins.ClassExternal, []string{path}))
}
return sources
}
+4 -4
View File
@@ -197,9 +197,8 @@ type versionArg struct {
}
func createPluginVersions(versions ...versionArg) []Version {
var vs []Version
for _, version := range versions {
vs := make([]Version, len(versions))
for i, version := range versions {
ver := Version{
Version: version.version,
}
@@ -211,7 +210,8 @@ func createPluginVersions(versions ...versionArg) []Version {
}
}
}
vs = append(vs, ver)
vs[i] = ver
}
return vs
+5
View File
@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/modules"
"github.com/grafana/grafana/pkg/services/authz"
"github.com/grafana/grafana/pkg/services/featuremgmt"
storageServer "github.com/grafana/grafana/pkg/services/store/entity/server"
"github.com/grafana/grafana/pkg/setting"
@@ -133,6 +134,10 @@ func (s *ModuleServer) Run() error {
return storageServer.ProvideService(s.cfg, s.features, s.log)
})
m.RegisterModule(modules.ZanzanaServer, func() (services.Service, error) {
return authz.ProvideZanzanaService(s.cfg, s.features)
})
m.RegisterModule(modules.All, nil)
return m.Run(s.context)
+3
View File
@@ -15,6 +15,7 @@ import (
func ProvideTestEnv(
server *Server,
db db.DB,
repldb db.ReplDB,
cfg *setting.Cfg,
ns *notifications.NotificationServiceMock,
grpcServer grpcserver.Provider,
@@ -26,6 +27,7 @@ func ProvideTestEnv(
return &TestEnv{
Server: server,
SQLStore: db,
ReadReplStore: repldb,
Cfg: cfg,
NotificationService: ns,
GRPCServer: grpcServer,
@@ -39,6 +41,7 @@ func ProvideTestEnv(
type TestEnv struct {
Server *Server
SQLStore db.DB
ReadReplStore db.ReplDB
Cfg *setting.Cfg
NotificationService *notifications.NotificationServiceMock
GRPCServer grpcserver.Provider
+5 -1
View File
@@ -390,6 +390,7 @@ var wireSet = wire.NewSet(
wireBasicSet,
metrics.WireSet,
sqlstore.ProvideService,
sqlstore.ProvideServiceWithReadReplica,
ngmetrics.ProvideService,
wire.Bind(new(notifications.Service), new(*notifications.NotificationService)),
wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationService)),
@@ -405,6 +406,7 @@ var wireCLISet = wire.NewSet(
wireBasicSet,
metrics.WireSet,
sqlstore.ProvideService,
sqlstore.ProvideServiceWithReadReplica,
ngmetrics.ProvideService,
wire.Bind(new(notifications.Service), new(*notifications.NotificationService)),
wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationService)),
@@ -420,12 +422,14 @@ var wireTestSet = wire.NewSet(
ProvideTestEnv,
metrics.WireSetForTest,
sqlstore.ProvideServiceForTests,
sqlstore.ProvideServiceWithReadReplicaForTests,
ngmetrics.ProvideServiceForTest,
notifications.MockNotificationService,
wire.Bind(new(notifications.Service), new(*notifications.NotificationServiceMock)),
wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationServiceMock)),
wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationServiceMock)),
wire.Bind(new(db.DB), new(*sqlstore.SQLStore)),
wire.Bind(new(db.ReplDB), new(*sqlstore.ReplStore)),
prefimpl.ProvideService,
oauthtoken.ProvideService,
oauthtokentest.ProvideService,
@@ -439,7 +443,7 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser
func InitializeForTest(t sqlutil.ITestDB, cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*TestEnv, error) {
wire.Build(wireExtsTestSet)
return &TestEnv{Server: &Server{}, SQLStore: &sqlstore.SQLStore{}, Cfg: &setting.Cfg{}}, nil
return &TestEnv{Server: &Server{}, SQLStore: &sqlstore.SQLStore{}, ReadReplStore: &sqlstore.ReplStore{}, Cfg: &setting.Cfg{}}, nil
}
func InitializeForCLI(cfg *setting.Cfg) (Runner, error) {
@@ -326,7 +326,7 @@ func (e DatasourcePermissionsService) SetBuiltInRolePermission(ctx context.Conte
// if an OSS/unlicensed instance is upgraded to Enterprise/licensed.
// https://github.com/grafana/identity-access-team/issues/672
func (e DatasourcePermissionsService) SetPermissions(ctx context.Context, orgID int64, resourceID string, commands ...accesscontrol.SetResourcePermissionCommand) ([]accesscontrol.ResourcePermission, error) {
var dbCommands []resourcepermissions.SetResourcePermissionsCommand
dbCommands := make([]resourcepermissions.SetResourcePermissionsCommand, 0, len(commands))
for _, cmd := range commands {
// Only set query permissions for built-in roles; do not set permissions for data sources with * as UID, as this would grant wildcard permissions
if cmd.Permission != "Query" || cmd.BuiltinRole == "" || resourceID == "*" {
@@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/tag"
@@ -27,6 +28,7 @@ func ProvideService(
cfg *setting.Cfg,
features featuremgmt.FeatureToggles,
tagService tag.Service,
tracer tracing.Tracer,
) *RepositoryImpl {
l := log.New("annotations")
l.Debug("Initializing annotations service")
@@ -35,7 +37,7 @@ func ProvideService(
write := xormStore
var read readStore
historianStore := loki.NewLokiHistorianStore(cfg.UnifiedAlerting.StateHistory, features, db, log.New("annotations.loki"))
historianStore := loki.NewLokiHistorianStore(cfg.UnifiedAlerting.StateHistory, features, db, log.New("annotations.loki"), tracer)
if historianStore != nil {
l.Debug("Using composite read store")
read = NewCompositeStore(log.New("annotations.composite"), xormStore, historianStore)
@@ -48,7 +48,7 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
features := featuremgmt.WithFeatures()
tagService := tagimpl.ProvideService(sql)
repo := ProvideService(sql, cfg, features, tagService)
repo := ProvideService(sql, cfg, features, tagService, tracing.InitializeTracerForTest())
dashboard1 := testutil.CreateDashboard(t, sql, cfg, features, dashboards.SaveDashboardCommand{
UserID: 1,
@@ -317,7 +317,7 @@ func TestIntegrationAnnotationListingWithInheritedRBAC(t *testing.T) {
cfg := setting.NewCfg()
cfg.AnnotationMaximumTagsLength = 60
repo := ProvideService(sql, cfg, tc.features, tagimpl.ProvideService(sql))
repo := ProvideService(sql, cfg, tc.features, tagimpl.ProvideService(sql), tracing.InitializeTracerForTest())
usr.Permissions = map[int64]map[string][]string{1: tc.permissions}
testutil.SetupRBACPermission(t, sql, role, usr)
@@ -17,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
ngmetrics "github.com/grafana/grafana/pkg/services/ngalert/metrics"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state"
@@ -53,7 +54,7 @@ type LokiHistorianStore struct {
log log.Logger
}
func NewLokiHistorianStore(cfg setting.UnifiedAlertingStateHistorySettings, ft featuremgmt.FeatureToggles, db db.DB, log log.Logger) *LokiHistorianStore {
func NewLokiHistorianStore(cfg setting.UnifiedAlertingStateHistorySettings, ft featuremgmt.FeatureToggles, db db.DB, log log.Logger, tracer tracing.Tracer) *LokiHistorianStore {
if !useStore(cfg, ft) {
return nil
}
@@ -64,7 +65,7 @@ func NewLokiHistorianStore(cfg setting.UnifiedAlertingStateHistorySettings, ft f
}
return &LokiHistorianStore{
client: historian.NewLokiClient(lokiCfg, historian.NewRequester(), ngmetrics.NewHistorianMetrics(prometheus.DefaultRegisterer, subsystem), log),
client: historian.NewLokiClient(lokiCfg, historian.NewRequester(), ngmetrics.NewHistorianMetrics(prometheus.DefaultRegisterer, subsystem), log, tracer),
db: db,
log: log,
}
+48
View File
@@ -1,3 +1,51 @@
# Authorization
This package contains the authorization server implementation.
## Feature toggles
The following feature toggles need to be activated:
```ini
[feature_toggles]
authZGRPCServer = true
grpcServer = true
```
## Configuration
To configure the authorization server and client, use the "authorization" section of the configuration ini file.
The `remote_address` setting, specifies the address where the authorization server is located (ex: `server.example.org:10000`).
The `mode` setting can be set to either `grpc` or `inproc`. When set to `grpc`, the client will connect to the specified address. When set to `inproc` the client will use inprocgrpc (relying on go channels) to wrap a local instantiation of the server.
The `listen` setting determines whether the authorization server should listen for incoming requests. When set to `true`, the authorization service will be registered to the Grafana GRPC server.
The default configuration does not register the authorization service on the Grafana GRPC server and binds the client to it `inproc`:
```ini
[authorization]
remote_address = ""
listen = false
mode = "inproc"
```
### Example
Here is an example to connect the authorization client to a remote grpc server.
```ini
[authorization]
remote_address = "server.example.org:10000"
mode = "grpc"
```
Here is an example to register the authorization service on the Grafana GRPC server and connect the client to it through grpc
```ini
[authorization]
remote_address = "localhost:10000"
listen = true
mode = "grpc"
```
+95 -3
View File
@@ -1,10 +1,21 @@
package authz
import (
"context"
"github.com/fullstorydev/grpchan"
"github.com/fullstorydev/grpchan/inprocgrpc"
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/grpcserver"
grpcUtils "github.com/grafana/grafana/pkg/services/store/entity/grpc"
"github.com/grafana/grafana/pkg/setting"
)
@@ -13,8 +24,10 @@ type Client interface {
}
type LegacyClient struct {
clientV1 authzv1.AuthzServiceClient
}
// ProvideAuthZClient provides an AuthZ client and creates the AuthZ service.
func ProvideAuthZClient(
cfg *setting.Cfg, features featuremgmt.FeatureToggles, acSvc accesscontrol.Service,
grpcServer grpcserver.Provider, tracer tracing.Tracer,
@@ -23,11 +36,90 @@ func ProvideAuthZClient(
return nil, nil
}
_, err := newLegacyServer(acSvc, features, grpcServer, tracer)
authCfg, err := ReadCfg(cfg)
if err != nil {
return nil, err
}
// TODO differentiate run local from run remote grpc
return &LegacyClient{}, nil
var client *LegacyClient
// Register the server
server, err := newLegacyServer(acSvc, features, grpcServer, tracer, authCfg)
if err != nil {
return nil, err
}
switch authCfg.mode {
case ModeInProc:
client = newInProcLegacyClient(server)
case ModeGRPC:
client, err = newGrpcLegacyClient(authCfg.remoteAddress)
if err != nil {
return nil, err
}
}
return client, err
}
// ProvideStandaloneAuthZClient provides a standalone AuthZ client, without registering the AuthZ service.
// You need to provide a remote address in the configuration
func ProvideStandaloneAuthZClient(
cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer,
) (Client, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) {
return nil, nil
}
authCfg, err := ReadCfg(cfg)
if err != nil {
return nil, err
}
return newGrpcLegacyClient(authCfg.remoteAddress)
}
func newInProcLegacyClient(server *legacyServer) *LegacyClient {
channel := &inprocgrpc.Channel{}
// TODO (gamab): change this once it's clear how to authenticate the client
// Choices are:
// - noAuth given it's in proc and we don't need the user
// - access_token verif only as it's consistent with when it's remote (we check the service is allowed to call the authz service)
// - access_token and id_token ? the id_token being only necessary when the user is trying to access the service straight away
// auth := grpcUtils.ProvideAuthenticator(cfg)
noAuth := func(ctx context.Context) (context.Context, error) {
return ctx, nil
}
channel.RegisterService(
grpchan.InterceptServer(
&authzv1.AuthzService_ServiceDesc,
grpcAuth.UnaryServerInterceptor(noAuth),
grpcAuth.StreamServerInterceptor(noAuth),
),
server,
)
conn := grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)
client := authzv1.NewAuthzServiceClient(conn)
return &LegacyClient{
clientV1: client,
}
}
func newGrpcLegacyClient(address string) (*LegacyClient, error) {
// Create a connection to the gRPC server
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
client := authzv1.NewAuthzServiceClient(conn)
return &LegacyClient{
clientV1: client,
}, nil
}
+43
View File
@@ -0,0 +1,43 @@
package authz
import (
"fmt"
"github.com/grafana/grafana/pkg/setting"
)
type Mode string
func (s Mode) IsValid() bool {
switch s {
case ModeGRPC, ModeInProc:
return true
}
return false
}
const (
ModeGRPC Mode = "grpc"
ModeInProc Mode = "inproc"
)
type Cfg struct {
remoteAddress string
listen bool
mode Mode
}
func ReadCfg(cfg *setting.Cfg) (*Cfg, error) {
section := cfg.SectionWithEnvOverrides("authorization")
mode := Mode(section.Key("mode").MustString(string(ModeInProc)))
if !mode.IsValid() {
return nil, fmt.Errorf("authorization: invalid mode %q", mode)
}
return &Cfg{
remoteAddress: section.Key("remote_address").MustString(""),
listen: section.Key("listen").MustBool(false),
mode: mode,
}, nil
}
+4 -2
View File
@@ -24,7 +24,7 @@ type legacyServer struct {
func newLegacyServer(
acSvc accesscontrol.Service, features featuremgmt.FeatureToggles,
grpcServer grpcserver.Provider, tracer tracing.Tracer,
grpcServer grpcserver.Provider, tracer tracing.Tracer, cfg *Cfg,
) (*legacyServer, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) {
return nil, nil
@@ -36,7 +36,9 @@ func newLegacyServer(
tracer: tracer,
}
grpcServer.GetServer().RegisterService(&authzv1.AuthzService_ServiceDesc, s)
if cfg.listen {
grpcServer.GetServer().RegisterService(&authzv1.AuthzService_ServiceDesc, s)
}
return s, nil
}
+1
View File
@@ -6,4 +6,5 @@ import (
var WireSet = wire.NewSet(
ProvideAuthZClient,
ProvideZanzana,
)
+127
View File
@@ -0,0 +1,127 @@
package authz
import (
"context"
"errors"
"fmt"
"github.com/fullstorydev/grpchan/inprocgrpc"
"github.com/grafana/dskit/services"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/grpcserver"
"github.com/grafana/grafana/pkg/setting"
)
type ZanzanaClient interface{}
func ProvideZanzana(cfg *setting.Cfg) (ZanzanaClient, error) {
var client *zanzana.Client
switch cfg.Zanzana.Mode {
case setting.ZanzanaModeClient:
conn, err := grpc.NewClient(cfg.Zanzana.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err)
}
client = zanzana.NewClient(openfgav1.NewOpenFGAServiceClient(conn))
case setting.ZanzanaModeEmbedded:
srv, err := zanzana.NewServer(zanzana.NewStore())
if err != nil {
return nil, fmt.Errorf("failed to start zanzana: %w", err)
}
channel := &inprocgrpc.Channel{}
openfgav1.RegisterOpenFGAServiceServer(channel, srv)
client = zanzana.NewClient(openfgav1.NewOpenFGAServiceClient(channel))
default:
return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.Zanzana.Mode)
}
return client, nil
}
type ZanzanaService interface {
services.NamedService
}
var _ ZanzanaService = (*Zanzana)(nil)
// ProvideZanzanaService is used to register zanzana as a module so we can run it seperatly from grafana.
func ProvideZanzanaService(cfg *setting.Cfg, features featuremgmt.FeatureToggles) (*Zanzana, error) {
s := &Zanzana{
cfg: cfg,
features: features,
logger: log.New("zanzana"),
}
s.BasicService = services.NewBasicService(s.start, s.running, s.stopping).WithName("zanzana")
return s, nil
}
type Zanzana struct {
*services.BasicService
cfg *setting.Cfg
logger log.Logger
handle grpcserver.Provider
features featuremgmt.FeatureToggles
}
func (z *Zanzana) start(ctx context.Context) error {
srv, err := zanzana.NewServer(zanzana.NewStore())
if err != nil {
return fmt.Errorf("failed to start zanzana: %w", err)
}
tracingCfg, err := tracing.ProvideTracingConfig(z.cfg)
if err != nil {
return err
}
tracingCfg.ServiceName = "zanzana"
tracer, err := tracing.ProvideService(tracingCfg)
if err != nil {
return err
}
// FIXME(kalleep): For now we use noopAuthenticator but we should create an authenticator that can be shared
// between different services.
z.handle, err = grpcserver.ProvideService(z.cfg, z.features, noopAuthenticator{}, tracer, prometheus.DefaultRegisterer)
if err != nil {
return fmt.Errorf("failed to create zanzana grpc server: %w", err)
}
openfgav1.RegisterOpenFGAServiceServer(z.handle.GetServer(), srv)
if _, err := grpcserver.ProvideReflectionService(z.cfg, z.handle); err != nil {
return fmt.Errorf("failed to register reflection for zanzana: %w", err)
}
return nil
}
func (z *Zanzana) running(ctx context.Context) error {
// Run is blocking so we can just run it here
return z.handle.Run(ctx)
}
func (z *Zanzana) stopping(err error) error {
if err != nil && !errors.Is(err, context.Canceled) {
z.logger.Error("Stopping zanzana due to unexpected error", "err", err)
}
return nil
}
type noopAuthenticator struct{}
func (n noopAuthenticator) Authenticate(ctx context.Context) (context.Context, error) {
return ctx, nil
}
+14
View File
@@ -0,0 +1,14 @@
package zanzana
import (
openfgav1 "github.com/openfga/api/proto/openfga/v1"
)
// FIXME(kalleep): Build out our wrapper client for openFGA
type Client struct {
c openfgav1.OpenFGAServiceClient
}
func NewClient(c openfgav1.OpenFGAServiceClient) *Client {
return &Client{c}
}
+26
View File
@@ -0,0 +1,26 @@
package zanzana
import (
"github.com/openfga/openfga/pkg/logger"
"github.com/openfga/openfga/pkg/server"
"github.com/openfga/openfga/pkg/storage"
)
func NewServer(store storage.OpenFGADatastore) (*server.Server, error) {
// FIXME(kalleep): add support for more options, configure logging, tracing etc
opts := []server.OpenFGAServiceV1Option{
server.WithDatastore(store),
// FIXME(kalleep): Write and log adapter for open fga logging interface
server.WithLogger(logger.NewNoopLogger()),
}
// FIXME(kalleep): Interceptors
// We probably need to at least need to add store id interceptor also
// would be nice to inject our own requestid?
srv, err := server.NewServerWithOpts(opts...)
if err != nil {
return nil, err
}
return srv, nil
}
+13
View File
@@ -0,0 +1,13 @@
package zanzana
import (
"github.com/openfga/openfga/pkg/storage"
"github.com/openfga/openfga/pkg/storage/memory"
)
// FIXME(kalleep): Add support for postgres, mysql and sqlite data stores.
// Postgres and mysql is already implmented by openFGA so we just need to hook up migartions for them.
// There is no support for sqlite atm but we are working on adding it: https://github.com/openfga/openfga/pull/1615
func NewStore() storage.OpenFGADatastore {
return memory.New()
}
@@ -420,21 +420,12 @@ func (s *Service) RunMigration(ctx context.Context, uid string) (*cloudmigration
}
func (s *Service) getMigrationDataJSON(ctx context.Context) (*cloudmigration.MigrateDataRequest, error) {
var migrationDataSlice []cloudmigration.MigrateDataRequestItem
// Data sources
dataSources, err := s.getDataSources(ctx)
if err != nil {
s.log.Error("Failed to get datasources", "err", err)
return nil, err
}
for _, ds := range dataSources {
migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{
Type: cloudmigration.DatasourceDataType,
RefID: ds.UID,
Name: ds.Name,
Data: ds,
})
}
// Dashboards
dashboards, err := s.getDashboards(ctx)
@@ -443,6 +434,26 @@ func (s *Service) getMigrationDataJSON(ctx context.Context) (*cloudmigration.Mig
return nil, err
}
// Folders
folders, err := s.getFolders(ctx)
if err != nil {
s.log.Error("Failed to get folders", "err", err)
return nil, err
}
migrationDataSlice := make(
[]cloudmigration.MigrateDataRequestItem, 0,
len(dataSources)+len(dashboards)+len(folders),
)
for _, ds := range dataSources {
migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{
Type: cloudmigration.DatasourceDataType,
RefID: ds.UID,
Name: ds.Name,
Data: ds,
})
}
for _, dashboard := range dashboards {
dashboard.Data.Del("id")
migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{
@@ -453,13 +464,6 @@ func (s *Service) getMigrationDataJSON(ctx context.Context) (*cloudmigration.Mig
})
}
// Folders
folders, err := s.getFolders(ctx)
if err != nil {
s.log.Error("Failed to get folders", "err", err)
return nil, err
}
for _, f := range folders {
migrationDataSlice = append(migrationDataSlice, cloudmigration.MigrateDataRequestItem{
Type: cloudmigration.FolderDataType,
@@ -468,6 +472,7 @@ func (s *Service) getMigrationDataJSON(ctx context.Context) (*cloudmigration.Mig
Data: f,
})
}
migrationData := &cloudmigration.MigrateDataRequest{
Items: migrationDataSlice,
}
@@ -521,9 +526,9 @@ func (s *Service) getFolders(ctx context.Context) ([]folder.Folder, error) {
return nil, err
}
var result []folder.Folder
for _, folder := range folders {
result = append(result, *folder)
result := make([]folder.Folder, len(folders))
for i, folder := range folders {
result[i] = *folder
}
return result, nil
@@ -535,10 +540,11 @@ func (s *Service) getDashboards(ctx context.Context) ([]dashboards.Dashboard, er
return nil, err
}
var result []dashboards.Dashboard
for _, dashboard := range dashs {
result = append(result, *dashboard)
result := make([]dashboards.Dashboard, len(dashs))
for i, dashboard := range dashs {
result[i] = *dashboard
}
return result, nil
}
@@ -536,6 +536,10 @@ func (d *dashboardStore) GetSoftDeletedDashboard(ctx context.Context, orgID int6
func (d *dashboardStore) RestoreDashboard(ctx context.Context, orgID int64, dashboardUID string, folder *folder.Folder) error {
return d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
if folder == nil || folder.UID == "" {
_, err := sess.Exec("UPDATE dashboard SET deleted=NULL, folder_id=0, folder_uid=NULL WHERE org_id=? AND uid=?", orgID, dashboardUID)
return err
}
// nolint:staticcheck
_, err := sess.Exec("UPDATE dashboard SET deleted=NULL, folder_id = ?, folder_uid=? WHERE org_id=? AND uid=?", folder.ID, folder.UID, orgID, dashboardUID)
return err
@@ -741,7 +741,9 @@ func makeQueryResult(query *dashboards.FindPersistedDashboardsQuery, res []dashb
hit.Tags = append(hit.Tags, item.Term)
}
if item.Deleted != nil {
hit.RemainingTrashAtAge = util.RemainingDaysUntil((*item.Deleted).Add(daysInTrash))
deletedDate := (*item.Deleted).Add(daysInTrash)
hit.IsDeleted = true
hit.PermanentlyDeleteDate = &deletedDate
}
}
return hitList
+21 -8
View File
@@ -1126,14 +1126,6 @@ var (
HideFromDocs: true,
HideFromAdminPage: true,
},
{
Name: "betterPageScrolling",
Description: "Removes CustomScrollbar from the UI, relying on native browser scrollbars",
Stage: FeatureStageGeneralAvailability,
FrontendOnly: true,
Owner: grafanaFrontendPlatformSquad,
Expression: "true", // enabled by default
},
{
Name: "authAPIAccessTokenAuth",
Description: "Enables the use of Auth API access tokens for authentication",
@@ -1317,6 +1309,27 @@ var (
HideFromAdminPage: true,
HideFromDocs: true,
},
{
Name: "openSearchBackendFlowEnabled",
Description: "Enables the backend query flow for Open Search datasource plugin",
Stage: FeatureStagePublicPreview,
Owner: awsDatasourcesSquad,
},
{
Name: "ssoSettingsLDAP",
Description: "Use the new SSO Settings API to configure LDAP",
Stage: FeatureStageExperimental,
Owner: identityAccessTeam,
HideFromDocs: true,
HideFromAdminPage: true,
},
{
Name: "databaseReadReplica",
Description: "Use a read replica for some database queries.",
Stage: FeatureStageExperimental,
Owner: grafanaBackendServicesSquad,
Expression: "false", // enabled by default
},
}
)
+3 -1
View File
@@ -150,7 +150,6 @@ tlsMemcached,experimental,@grafana/grafana-operator-experience-squad,false,false
kubernetesAggregator,experimental,@grafana/grafana-app-platform-squad,false,true,false
expressionParser,experimental,@grafana/grafana-app-platform-squad,false,true,false
groupByVariable,experimental,@grafana/dashboards-squad,false,false,false
betterPageScrolling,GA,@grafana/grafana-frontend-platform,false,false,true
authAPIAccessTokenAuth,experimental,@grafana/identity-access-team,false,false,false
scopeFilters,experimental,@grafana/dashboards-squad,false,false,false
ssoSettingsSAML,preview,@grafana/identity-access-team,false,false,false
@@ -175,3 +174,6 @@ pluginProxyPreserveTrailingSlash,GA,@grafana/plugins-platform-backend,false,fals
azureMonitorPrometheusExemplars,experimental,@grafana/partner-datasources,false,false,false
pinNavItems,experimental,@grafana/grafana-frontend-platform,false,false,false
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
openSearchBackendFlowEnabled,preview,@grafana/aws-datasources,false,false,false
ssoSettingsLDAP,experimental,@grafana/identity-access-team,false,false,false
databaseReadReplica,experimental,@grafana/grafana-backend-services-squad,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
150 kubernetesAggregator experimental @grafana/grafana-app-platform-squad false true false
151 expressionParser experimental @grafana/grafana-app-platform-squad false true false
152 groupByVariable experimental @grafana/dashboards-squad false false false
betterPageScrolling GA @grafana/grafana-frontend-platform false false true
153 authAPIAccessTokenAuth experimental @grafana/identity-access-team false false false
154 scopeFilters experimental @grafana/dashboards-squad false false false
155 ssoSettingsSAML preview @grafana/identity-access-team false false false
174 azureMonitorPrometheusExemplars experimental @grafana/partner-datasources false false false
175 pinNavItems experimental @grafana/grafana-frontend-platform false false false
176 authZGRPCServer experimental @grafana/identity-access-team false false false
177 openSearchBackendFlowEnabled preview @grafana/aws-datasources false false false
178 ssoSettingsLDAP experimental @grafana/identity-access-team false false false
179 databaseReadReplica experimental @grafana/grafana-backend-services-squad false false false
+12 -4
View File
@@ -611,10 +611,6 @@ const (
// Enable groupBy variable support in scenes dashboards
FlagGroupByVariable = "groupByVariable"
// FlagBetterPageScrolling
// Removes CustomScrollbar from the UI, relying on native browser scrollbars
FlagBetterPageScrolling = "betterPageScrolling"
// FlagAuthAPIAccessTokenAuth
// Enables the use of Auth API access tokens for authentication
FlagAuthAPIAccessTokenAuth = "authAPIAccessTokenAuth"
@@ -710,4 +706,16 @@ const (
// FlagAuthZGRPCServer
// Enables the gRPC server for authorization
FlagAuthZGRPCServer = "authZGRPCServer"
// FlagOpenSearchBackendFlowEnabled
// Enables the backend query flow for Open Search datasource plugin
FlagOpenSearchBackendFlowEnabled = "openSearchBackendFlowEnabled"
// FlagSsoSettingsLDAP
// Use the new SSO Settings API to configure LDAP
FlagSsoSettingsLDAP = "ssoSettingsLDAP"
// FlagDatabaseReadReplica
// Use a read replica for some database queries.
FlagDatabaseReadReplica = "databaseReadReplica"
)
+40 -1
View File
@@ -463,7 +463,8 @@
"metadata": {
"name": "betterPageScrolling",
"resourceVersion": "1717578796182",
"creationTimestamp": "2024-03-06T15:06:47Z"
"creationTimestamp": "2024-03-06T15:06:47Z",
"deletionTimestamp": "2024-06-18T09:25:56Z"
},
"spec": {
"description": "Removes CustomScrollbar from the UI, relying on native browser scrollbars",
@@ -654,6 +655,18 @@
"frontend": true
}
},
{
"metadata": {
"name": "databaseReadReplica",
"resourceVersion": "1718308641844",
"creationTimestamp": "2024-06-13T19:57:21Z"
},
"spec": {
"description": "Use a read replica for some database queries.",
"stage": "experimental",
"codeowner": "@grafana/grafana-backend-services-squad"
}
},
{
"metadata": {
"name": "dataplaneFrontendFallback",
@@ -1599,6 +1612,18 @@
"codeowner": "@grafana/grafana-operator-experience-squad"
}
},
{
"metadata": {
"name": "openSearchBackendFlowEnabled",
"resourceVersion": "1718357852240",
"creationTimestamp": "2024-06-14T09:37:32Z"
},
"spec": {
"description": "Enables the backend query flow for Open Search datasource plugin",
"stage": "preview",
"codeowner": "@grafana/aws-datasources"
}
},
{
"metadata": {
"name": "panelFilterVariable",
@@ -2114,6 +2139,20 @@
"allowSelfServe": true
}
},
{
"metadata": {
"name": "ssoSettingsLDAP",
"resourceVersion": "1718624569822",
"creationTimestamp": "2024-06-17T11:42:49Z"
},
"spec": {
"description": "Use the new SSO Settings API to configure LDAP",
"stage": "experimental",
"codeowner": "@grafana/identity-access-team",
"hideFromAdminPage": true,
"hideFromDocs": true
}
},
{
"metadata": {
"name": "ssoSettingsSAML",
+4 -1
View File
@@ -209,7 +209,10 @@ func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Fo
var dashFolder *folder.Folder
var err error
switch {
case q.UID != nil && *q.UID != "":
case q.UID != nil:
if *q.UID == "" {
return &folder.GeneralFolder, nil
}
dashFolder, err = s.getFolderByUID(ctx, q.OrgID, *q.UID)
if err != nil {
return nil, err
@@ -395,6 +395,21 @@ func TestIntegrationFolderService(t *testing.T) {
"For error '%s' expected error '%s', actual '%s'", tc.ActualError, tc.ExpectedError, actualError)
}
})
t.Run("Returns root folder", func(t *testing.T) {
t.Run("When the folder UID is blank should return the root folder", func(t *testing.T) {
emptyString := ""
actual, err := service.Get(context.Background(), &folder.GetFolderQuery{
UID: &emptyString,
OrgID: 1,
SignedInUser: usr,
})
assert.NoError(t, err)
assert.Equal(t, folder.GeneralFolder.UID, actual.UID)
assert.Equal(t, folder.GeneralFolder.Title, actual.Title)
})
})
})
}
+11 -9
View File
@@ -29,7 +29,8 @@ import (
)
type Service struct {
cfg *setting.Cfg
cfg *ldap.Config
adminUser string
userService user.Service
authInfoService login.AuthInfoService
ldapGroupsService ldap.Groups
@@ -47,7 +48,8 @@ func ProvideService(
sessionService auth.UserTokenService, bundleRegistry supportbundles.Service,
) *Service {
s := &Service{
cfg: cfg,
cfg: ldap.GetLDAPConfig(cfg),
adminUser: cfg.AdminUser,
userService: userService,
authInfoService: authInfoService,
ldapGroupsService: ldapGroupsService,
@@ -96,7 +98,7 @@ func ProvideService(
// 403: forbiddenError
// 500: internalServerError
func (s *Service) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Response {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
@@ -122,7 +124,7 @@ func (s *Service) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Response {
// 403: forbiddenError
// 500: internalServerError
func (s *Service) GetLDAPStatus(c *contextmodel.ReqContext) response.Response {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
@@ -169,7 +171,7 @@ func (s *Service) GetLDAPStatus(c *contextmodel.ReqContext) response.Response {
// 403: forbiddenError
// 500: internalServerError
func (s *Service) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Response {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
@@ -206,7 +208,7 @@ func (s *Service) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Resp
userInfo, _, err := ldapClient.User(usr.Login)
if err != nil {
if errors.Is(err, multildap.ErrDidNotFindUser) { // User was not in the LDAP server - we need to take action:
if s.cfg.AdminUser == usr.Login { // User is *the* Grafana Admin. We cannot disable it.
if s.adminUser == usr.Login { // User is *the* Grafana Admin. We cannot disable it.
errMsg := fmt.Sprintf(`Refusing to sync grafana super admin "%s" - it would be disabled`, usr.Login)
s.log.Error(errMsg)
return response.Error(http.StatusBadRequest, errMsg, err)
@@ -250,7 +252,7 @@ func (s *Service) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Resp
// 403: forbiddenError
// 500: internalServerError
func (s *Service) GetUserFromLDAP(c *contextmodel.ReqContext) response.Response {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
@@ -330,8 +332,8 @@ func (s *Service) identityFromLDAPUser(user *login.ExternalUserInfo) *authn.Iden
SyncUser: true,
SyncTeams: true,
EnableUser: true,
SyncOrgRoles: !s.cfg.LDAPSkipOrgRoleSync,
AllowSignUp: s.cfg.LDAPAllowSignup,
SyncOrgRoles: !s.cfg.SkipOrgRoleSync,
AllowSignUp: s.cfg.AllowSignUp,
},
}
}
+12 -12
View File
@@ -95,7 +95,7 @@ func TestGetUserFromLDAPAPIEndpoint_UserNotFound(t *testing.T) {
ExpectedClient: &LDAPMock{
UserSearchResult: nil,
},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -160,7 +160,7 @@ func TestGetUserFromLDAPAPIEndpoint_OrgNotfound(t *testing.T) {
UserSearchResult: userSearchResult,
UserSearchConfig: userSearchConfig,
},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -229,7 +229,7 @@ func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
UserSearchResult: userSearchResult,
UserSearchConfig: userSearchConfig,
},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -314,7 +314,7 @@ func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
UserSearchResult: userSearchResult,
UserSearchConfig: userSearchConfig,
},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -368,7 +368,7 @@ func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
_, server := setupAPITest(t, func(a *Service) {
a.ldapService = &service.LDAPFakeService{
ExpectedClient: &LDAPMock{},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -407,7 +407,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
ExpectedClient: &LDAPMock{UserSearchResult: &login.ExternalUserInfo{
Login: "ldap-daniel",
}},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -442,7 +442,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
a.userService = userServiceMock
a.ldapService = &service.LDAPFakeService{
ExpectedClient: &LDAPMock{},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -475,10 +475,10 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
_, server := setupAPITest(t, func(a *Service) {
a.userService = userServiceMock
a.cfg.AdminUser = "ldap-daniel"
a.adminUser = "ldap-daniel"
a.ldapService = &service.LDAPFakeService{
ExpectedClient: &LDAPMock{UserSearchError: multildap.ErrDidNotFindUser},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -511,7 +511,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
a.authInfoService = &authinfotest.FakeService{ExpectedExternalUser: &login.ExternalUserInfo{IsDisabled: true, UserId: 34}}
a.ldapService = &service.LDAPFakeService{
ExpectedClient: &LDAPMock{UserSearchError: multildap.ErrDidNotFindUser},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
@@ -641,12 +641,12 @@ search_base_dns = ["dc=grafana,dc=org"]`)
t.Run(tt.desc, func(t *testing.T) {
_, server := setupAPITest(t, func(a *Service) {
a.userService = &usertest.FakeUserService{ExpectedUser: &user.User{Login: "ldap-daniel", ID: 1}}
a.cfg.LDAPConfigFilePath = ldapConfigFile
a.cfg.ConfigFilePath = ldapConfigFile
a.ldapService = &service.LDAPFakeService{
ExpectedClient: &LDAPMock{UserSearchResult: &login.ExternalUserInfo{
Login: "ldap-daniel",
}},
ExpectedConfig: &ldap.Config{},
ExpectedConfig: &ldap.ServersConfig{},
}
})
// Add minimal setup to pass handler
+6 -6
View File
@@ -73,12 +73,12 @@ func (s *Service) supportBundleCollector(context.Context) (*supportbundles.Suppo
bWriter.WriteString("```ini\n")
bWriter.WriteString(fmt.Sprintf("enabled = %v\n", s.cfg.LDAPAuthEnabled))
bWriter.WriteString(fmt.Sprintf("config_file = %s\n", s.cfg.LDAPConfigFilePath))
bWriter.WriteString(fmt.Sprintf("allow_sign_up = %v\n", s.cfg.LDAPAllowSignup))
bWriter.WriteString(fmt.Sprintf("sync_cron = %s\n", s.cfg.LDAPSyncCron))
bWriter.WriteString(fmt.Sprintf("active_sync_enabled = %v\n", s.cfg.LDAPActiveSyncEnabled))
bWriter.WriteString(fmt.Sprintf("skip_org_role_sync = %v\n", s.cfg.LDAPSkipOrgRoleSync))
bWriter.WriteString(fmt.Sprintf("enabled = %v\n", s.cfg.Enabled))
bWriter.WriteString(fmt.Sprintf("config_file = %s\n", s.cfg.ConfigFilePath))
bWriter.WriteString(fmt.Sprintf("allow_sign_up = %v\n", s.cfg.AllowSignUp))
bWriter.WriteString(fmt.Sprintf("sync_cron = %s\n", s.cfg.SyncCron))
bWriter.WriteString(fmt.Sprintf("active_sync_enabled = %v\n", s.cfg.ActiveSyncEnabled))
bWriter.WriteString(fmt.Sprintf("skip_org_role_sync = %v\n", s.cfg.SkipOrgRoleSync))
bWriter.WriteString("```\n\n")
+4 -5
View File
@@ -18,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -45,7 +44,7 @@ type IServer interface {
// Server is basic struct of LDAP authorization
type Server struct {
cfg *setting.Cfg
cfg *Config
Config *ServerConfig
Connection IConnection
log log.Logger
@@ -86,7 +85,7 @@ var (
)
// New creates the new LDAP connection
func New(config *ServerConfig, cfg *setting.Cfg) IServer {
func New(config *ServerConfig, cfg *Config) IServer {
return &Server{
Config: config,
cfg: cfg,
@@ -414,7 +413,7 @@ func (server *Server) users(logins []string) (
// If there are no ldap group mappings access is true
// otherwise a single group must match
func (server *Server) validateGrafanaUser(user *login.ExternalUserInfo) error {
if !server.cfg.LDAPSkipOrgRoleSync && len(server.Config.Groups) > 0 &&
if !server.cfg.SkipOrgRoleSync && len(server.Config.Groups) > 0 &&
(len(user.OrgRoles) == 0 && (user.IsGrafanaAdmin == nil || !*user.IsGrafanaAdmin)) {
server.log.Warn(
"User does not belong in any of the specified LDAP groups",
@@ -499,7 +498,7 @@ func (server *Server) buildGrafanaUser(user *ldap.Entry) (*login.ExternalUserInf
}
// Skipping org role sync
if server.cfg.LDAPSkipOrgRoleSync {
if server.cfg.SkipOrgRoleSync {
server.log.Debug("Skipping organization role mapping.")
return extUser, nil
}
+15 -11
View File
@@ -10,7 +10,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/setting"
)
var defaultLogin = &login.LoginUserQuery{
@@ -31,8 +30,9 @@ func TestServer_Login_UserBind_Fail(t *testing.T) {
}
}
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
Config: &ServerConfig{
@@ -105,8 +105,9 @@ func TestServer_Login_ValidCredentials(t *testing.T) {
return nil
}
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -142,8 +143,9 @@ func TestServer_Login_UnauthenticatedBind(t *testing.T) {
return nil
}
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -189,8 +191,9 @@ func TestServer_Login_AuthenticatedBind(t *testing.T) {
return nil
}
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -232,8 +235,9 @@ func TestServer_Login_UserWildcardBind(t *testing.T) {
return nil
}
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
+18 -13
View File
@@ -10,7 +10,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
)
func TestServer_getSearchRequest(t *testing.T) {
@@ -53,8 +52,9 @@ func TestServer_getSearchRequest(t *testing.T) {
func TestSerializeUsers(t *testing.T) {
t.Run("simple case", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -92,8 +92,9 @@ func TestSerializeUsers(t *testing.T) {
})
t.Run("without lastname", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -129,8 +130,9 @@ func TestSerializeUsers(t *testing.T) {
})
t.Run("mark user without matching group as disabled", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -163,8 +165,9 @@ func TestSerializeUsers(t *testing.T) {
func TestServer_validateGrafanaUser(t *testing.T) {
t.Run("no group config", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -183,8 +186,9 @@ func TestServer_validateGrafanaUser(t *testing.T) {
})
t.Run("user in group", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -210,8 +214,9 @@ func TestServer_validateGrafanaUser(t *testing.T) {
})
t.Run("user not in group", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
+23 -21
View File
@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
)
const (
@@ -55,7 +54,7 @@ func TestNew(t *testing.T) {
result := New(&ServerConfig{
Attr: AttributeMap{},
SearchBaseDNs: []string{"BaseDNHere"},
}, &setting.Cfg{})
}, &Config{})
assert.Implements(t, (*IServer)(nil), result)
}
@@ -68,7 +67,7 @@ func TestServer_Dial(t *testing.T) {
ClientCert: "./testdata/parsable.cert",
ClientKey: "./testdata/parsable.pem",
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -79,7 +78,7 @@ func TestServer_Dial(t *testing.T) {
serverConfig := &ServerConfig{
RootCACert: "./testdata/invalid.cert",
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -90,7 +89,7 @@ func TestServer_Dial(t *testing.T) {
serverConfig := &ServerConfig{
RootCACert: "./testdata/nofile.cert",
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -102,7 +101,7 @@ func TestServer_Dial(t *testing.T) {
ClientCert: "./testdata/invalid.cert",
ClientKey: "./testdata/invalid.pem",
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -114,7 +113,7 @@ func TestServer_Dial(t *testing.T) {
ClientCert: "./testdata/nofile.cert",
ClientKey: "./testdata/parsable.pem",
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -128,7 +127,7 @@ func TestServer_Dial(t *testing.T) {
ClientCertValue: validCert,
ClientKeyValue: validKey,
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -139,7 +138,7 @@ func TestServer_Dial(t *testing.T) {
serverConfig := &ServerConfig{
RootCACertValue: []string{"invalid-certificate"},
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -150,7 +149,7 @@ func TestServer_Dial(t *testing.T) {
serverConfig := &ServerConfig{
RootCACertValue: []string{"aW52YWxpZC1jZXJ0aWZpY2F0ZQ=="},
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -162,7 +161,7 @@ func TestServer_Dial(t *testing.T) {
ClientCertValue: "invalid-certificate",
ClientKeyValue: validKey,
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -174,7 +173,7 @@ func TestServer_Dial(t *testing.T) {
ClientCertValue: validCert,
ClientKeyValue: "aW52YWxpZC1rZXk=",
}
server := New(serverConfig, &setting.Cfg{})
server := New(serverConfig, &Config{})
err := server.Dial()
require.Error(t, err)
@@ -226,8 +225,9 @@ func TestServer_Users(t *testing.T) {
conn.setSearchResult(&result)
// Set up attribute map without surname and email
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -323,7 +323,7 @@ func TestServer_Users(t *testing.T) {
})
server := &Server{
cfg: setting.NewCfg(),
cfg: &Config{},
Config: &ServerConfig{
Attr: AttributeMap{
Username: "username",
@@ -370,8 +370,9 @@ func TestServer_Users(t *testing.T) {
}
})
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -464,8 +465,9 @@ func TestServer_Users(t *testing.T) {
})
isGrafanaAdmin := true
cfg := setting.NewCfg()
cfg.LDAPAuthEnabled = true
cfg := &Config{
Enabled: true,
}
server := &Server{
cfg: cfg,
@@ -506,7 +508,7 @@ func TestServer_Users(t *testing.T) {
require.True(t, res[0].IsDisabled)
})
t.Run("skip org role sync", func(t *testing.T) {
server.cfg.LDAPSkipOrgRoleSync = true
server.cfg.SkipOrgRoleSync = true
res, err := server.Users([]string{"groot"})
require.NoError(t, err)
@@ -517,7 +519,7 @@ func TestServer_Users(t *testing.T) {
require.False(t, res[0].IsDisabled)
})
t.Run("sync org role", func(t *testing.T) {
server.cfg.LDAPSkipOrgRoleSync = false
server.cfg.SkipOrgRoleSync = false
res, err := server.Users([]string{"groot"})
require.NoError(t, err)
require.Len(t, res, 1)
+2 -3
View File
@@ -7,7 +7,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/setting"
)
// GetConfig gets LDAP config
@@ -54,12 +53,12 @@ type IMultiLDAP interface {
// MultiLDAP is basic struct of LDAP authorization
type MultiLDAP struct {
configs []*ldap.ServerConfig
cfg *setting.Cfg
cfg *ldap.Config
log log.Logger
}
// New creates the new LDAP auth
func New(configs []*ldap.ServerConfig, cfg *setting.Cfg) IMultiLDAP {
func New(configs []*ldap.ServerConfig, cfg *ldap.Config) IMultiLDAP {
return &MultiLDAP{
configs: configs,
cfg: cfg,
+24 -25
View File
@@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/setting"
//TODO(sh0rez): remove once import cycle resolved
_ "github.com/grafana/grafana/pkg/api/response"
@@ -19,7 +18,7 @@ func TestMultiLDAP(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{}, setting.NewCfg())
multi := New([]*ldap.ServerConfig{}, &ldap.Config{})
_, err := multi.Ping()
require.Error(t, err)
@@ -35,7 +34,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{Host: "10.0.0.1", Port: 361},
}, setting.NewCfg())
}, &ldap.Config{})
statuses, err := multi.Ping()
@@ -53,7 +52,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{Host: "10.0.0.1", Port: 361},
}, setting.NewCfg())
}, &ldap.Config{})
statuses, err := multi.Ping()
@@ -71,7 +70,7 @@ func TestMultiLDAP(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{}, setting.NewCfg())
multi := New([]*ldap.ServerConfig{}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
require.Error(t, err)
@@ -88,7 +87,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
@@ -104,7 +103,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -125,7 +124,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
result, err := multi.Login(&login.LoginUserQuery{})
require.Equal(t, 1, mock.dialCalledTimes)
@@ -145,7 +144,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -164,7 +163,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -184,7 +183,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -202,7 +201,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Login(&login.LoginUserQuery{})
require.Equal(t, 1, mock.dialCalledTimes)
@@ -219,7 +218,7 @@ func TestMultiLDAP(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{}, setting.NewCfg())
multi := New([]*ldap.ServerConfig{}, &ldap.Config{})
_, _, err := multi.User("test")
require.Error(t, err)
@@ -236,7 +235,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, _, err := multi.User("test")
@@ -251,7 +250,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, _, err := multi.User("test")
require.Equal(t, 2, mock.dialCalledTimes)
@@ -271,7 +270,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, _, err := multi.User("test")
require.Equal(t, 1, mock.dialCalledTimes)
@@ -298,7 +297,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
user, _, err := multi.User("test")
require.Equal(t, 1, mock.dialCalledTimes)
@@ -319,7 +318,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, _, err := multi.User("test")
require.Equal(t, 2, mock.dialCalledTimes)
@@ -338,7 +337,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Users([]string{"test"})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -349,7 +348,7 @@ func TestMultiLDAP(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{}, setting.NewCfg())
multi := New([]*ldap.ServerConfig{}, &ldap.Config{})
_, err := multi.Users([]string{"test"})
require.Error(t, err)
@@ -366,7 +365,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Users([]string{"test"})
@@ -381,7 +380,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Users([]string{"test"})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -401,7 +400,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
_, err := multi.Users([]string{"test"})
require.Equal(t, 1, mock.dialCalledTimes)
@@ -434,7 +433,7 @@ func TestMultiLDAP(t *testing.T) {
multi := New([]*ldap.ServerConfig{
{}, {},
}, setting.NewCfg())
}, &ldap.Config{})
users, err := multi.Users([]string{"test"})
require.Equal(t, 2, mock.dialCalledTimes)
@@ -512,7 +511,7 @@ func (mock *mockLDAP) Bind() error {
func setup() *mockLDAP {
mock := &mockLDAP{}
newLDAP = func(config *ldap.ServerConfig, cfg *setting.Cfg) ldap.IServer {
newLDAP = func(config *ldap.ServerConfig, cfg *ldap.Config) ldap.IServer {
return mock
}
+2 -2
View File
@@ -7,7 +7,7 @@ import (
)
type LDAPFakeService struct {
ExpectedConfig *ldap.Config
ExpectedConfig *ldap.ServersConfig
ExpectedClient multildap.IMultiLDAP
ExpectedError error
ExpectedUser *login.ExternalUserInfo
@@ -22,7 +22,7 @@ func (s *LDAPFakeService) ReloadConfig() error {
return s.ExpectedError
}
func (s *LDAPFakeService) Config() *ldap.Config {
func (s *LDAPFakeService) Config() *ldap.ServersConfig {
return s.ExpectedConfig
}
+28 -2
View File
@@ -1,8 +1,10 @@
package service
import (
"encoding/json"
"fmt"
"os"
"strconv"
"github.com/BurntSushi/toml"
@@ -13,8 +15,8 @@ import (
const defaultTimeout = 10
func readConfig(configFile string) (*ldap.Config, error) {
result := &ldap.Config{}
func readConfig(configFile string) (*ldap.ServersConfig, error) {
result := &ldap.ServersConfig{}
logger.Info("LDAP enabled, reading config file", "file", configFile)
@@ -85,3 +87,27 @@ func assertNotEmptyCfg(val any, propName string) error {
}
return nil
}
func resolveBool(input any, defaultValue bool) bool {
strInput := fmt.Sprintf("%v", input)
result, err := strconv.ParseBool(strInput)
if err != nil {
return defaultValue
}
return result
}
func resolveServerConfig(input any) (*ldap.ServersConfig, error) {
var ldapCfg ldap.ServersConfig
inputJson, err := json.Marshal(input)
if err != nil {
return nil, err
}
if err = json.Unmarshal(inputJson, &ldapCfg); err != nil {
return nil, err
}
return &ldapCfg, nil
}
+71 -22
View File
@@ -1,13 +1,19 @@
package service
import (
"context"
"errors"
"sync"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/ldap/multildap"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/ssosettings"
"github.com/grafana/grafana/pkg/services/ssosettings/models"
"github.com/grafana/grafana/pkg/setting"
)
@@ -19,7 +25,7 @@ var (
// LDAP is the interface for the LDAP service.
type LDAP interface {
ReloadConfig() error
Config() *ldap.Config
Config() *ldap.ServersConfig
Client() multildap.IMultiLDAP
// Login authenticates the user against the LDAP server.
@@ -29,48 +35,91 @@ type LDAP interface {
}
type LDAPImpl struct {
client multildap.IMultiLDAP
cfg *setting.Cfg
ldapCfg *ldap.Config
log log.Logger
client multildap.IMultiLDAP
cfg *ldap.Config
ldapCfg *ldap.ServersConfig
log log.Logger
features featuremgmt.FeatureToggles
ssoSettings ssosettings.Service
// loadingMutex locks the reading of the config so multiple requests for reloading are sequential.
loadingMutex *sync.Mutex
}
func ProvideService(cfg *setting.Cfg) *LDAPImpl {
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, ssoSettings ssosettings.Service) *LDAPImpl {
s := &LDAPImpl{
client: nil,
ldapCfg: nil,
cfg: cfg,
log: log.New("ldap.service"),
loadingMutex: &sync.Mutex{},
features: features,
ssoSettings: ssoSettings,
}
if !cfg.LDAPAuthEnabled {
return s
}
if s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) && s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsLDAP) {
s.ssoSettings.RegisterReloadable(social.LDAPProviderName, s)
ldapCfg, err := multildap.GetConfig(s.cfg)
if err != nil {
s.log.Error("Failed to get LDAP config", "error", err)
ldapSettings, err := s.ssoSettings.GetForProvider(context.Background(), social.LDAPProviderName)
if err != nil {
s.log.Error("Failed to retrieve LDAP settings from SSO settings service", "error", err)
return s
}
err = s.Reload(context.Background(), *ldapSettings)
if err != nil {
s.log.Error("Failed to load LDAP settings", "error", err)
return s
}
} else {
s.ldapCfg = ldapCfg
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
s.cfg = ldap.GetLDAPConfig(cfg)
if !cfg.LDAPAuthEnabled {
return s
}
ldapCfg, err := multildap.GetConfig(s.cfg)
if err != nil {
s.log.Error("Failed to get LDAP config", "error", err)
} else {
s.ldapCfg = ldapCfg
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
}
}
return s
}
func (s *LDAPImpl) Reload(ctx context.Context, settings models.SSOSettings) error {
cfg := &ldap.Config{}
cfg.Enabled = resolveBool(settings.Settings["enabled"], false)
cfg.SkipOrgRoleSync = resolveBool(settings.Settings["skip_org_role_sync"], false)
cfg.AllowSignUp = resolveBool(settings.Settings["allow_sign_up"], true)
ldapCfg, err := resolveServerConfig(settings.Settings["config"])
if err != nil {
return err
}
s.loadingMutex.Lock()
defer s.loadingMutex.Unlock()
s.cfg = cfg
s.ldapCfg = ldapCfg
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
return nil
}
func (s *LDAPImpl) Validate(ctx context.Context, settings models.SSOSettings, oldSettings models.SSOSettings, requester identity.Requester) error {
return nil
}
func (s *LDAPImpl) ReloadConfig() error {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return nil
}
s.loadingMutex.Lock()
defer s.loadingMutex.Unlock()
config, err := readConfig(s.cfg.LDAPConfigFilePath)
config, err := readConfig(s.cfg.ConfigFilePath)
if err != nil {
return err
}
@@ -90,12 +139,12 @@ func (s *LDAPImpl) Client() multildap.IMultiLDAP {
return s.client
}
func (s *LDAPImpl) Config() *ldap.Config {
func (s *LDAPImpl) Config() *ldap.ServersConfig {
return s.ldapCfg
}
func (s *LDAPImpl) Login(query *login.LoginUserQuery) (*login.ExternalUserInfo, error) {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return nil, ErrLDAPNotEnabled
}
@@ -108,7 +157,7 @@ func (s *LDAPImpl) Login(query *login.LoginUserQuery) (*login.ExternalUserInfo,
}
func (s *LDAPImpl) User(username string) (*login.ExternalUserInfo, error) {
if !s.cfg.LDAPAuthEnabled {
if !s.cfg.Enabled {
return nil, ErrLDAPNotEnabled
}
+299
View File
@@ -0,0 +1,299 @@
package service
import (
"context"
"sync"
"testing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/ssosettings/models"
"github.com/stretchr/testify/require"
)
const (
validCert = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURYVENDQWtXZ0F3SUJBZ0lKQUxtVlZ1RFd1NE5ZTUEwR0NTcUdTSWIzRFFFQkN
3VUFNRVV4Q3pBSkJnTlYKQkFZVEFrRlZNUk13RVFZRFZRUUlEQXBUYjIxbExWTjBZWFJsTVNFd0h3WURWUVFLREJoSmJuUmxjbTVsZENCWAphV1JuYVhSekl
GQjBlU0JNZEdRd0hoY05NVFl4TWpNeE1UUXpORFEzV2hjTk5EZ3dOakkxTVRRek5EUTNXakJGCk1Rc3dDUVlEVlFRR0V3SkJWVEVUTUJFR0ExVUVDQXdLVTI
5dFpTMVRkR0YwWlRFaE1COEdBMVVFQ2d3WVNXNTAKWlhKdVpYUWdWMmxrWjJsMGN5QlFkSGtnVEhSa01JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUTh
BTUlJQgpDZ0tDQVFFQXpVQ0ZvemdOYjFoMU0wanpOUlNDamhPQm5SK3VWYlZwYVdmWFlJUitBaFdEZEVlNXJ5WStDZ2F2Ck9nOGJmTHlieXpGZGVobFlkRFJ
na2VkRUIvR2pHOGFKdzA2bDBxRjRqRE9BdzBrRXlnV0N1Mm1jSDdYT3hSdCsKWUFIM1RWSGEvSHUxVzNXanprb2JxcXFMUThna0tXV00yN2ZPZ0FaNkdpZWF
KQk42VkJTTU1jUGV5M0hXTEJtYworVFlKbXYxZGJhTzJqSGhLaDhwZkt3MFcxMlZNOFAxUElPOGd2NFBodS91dUpZaWVCV0tpeEJFeXkwbEhqeWl4CllGQ1I
xMnhkaDRDQTQ3cTk1OFpSR25uRFVHRlZFMVFoZ1JhY0pDT1o5YmQ1dDltcjhLTGFWQllUQ0pvNUVSRTgKanltYWI1ZFBxZTVxS2ZKc0NaaXFXZ2xialVvOXR
3SURBUUFCbzFBd1RqQWRCZ05WSFE0RUZnUVV4cHV3Y3MvQwpZUU95dWkrcjFHKzNLeEJOaHhrd0h3WURWUjBqQkJnd0ZvQVV4cHV3Y3MvQ1lRT3l1aStyMUc
rM0t4Qk5oeGt3CkRBWURWUjBUQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQWlXVUtzLzJ4L3ZpTkNLaTNZNmIKbEV1Q3RBR2h6T09aOUV
qcnZKOCtDT0gzUmFnM3RWQldyY0JaMy91aGhQcTVneTlscXc0T2t2RXdzOTkvNWpGcwpYMUZKNk1LQmdxZnV5N3loNXMxWWZNMEFOSFljek1tWXBaZUFjUWY
yQ0dBYVZmd1RUZlNsek5Mc0YybFcvbHk3CnlhcEZ6bFlTSkxHb1ZFK09IRXU4ZzVTbE5BQ1VFZmtYdys1RWdoaCtLemxJTjdSNlE3cjJpeFdORkJDL2pXZjc
KTktVZkp5WDhxSUc1bWQxWVVlVDZHQlc5Qm0yLzEvUmlPMjRKVGFZbGZMZEtLOVRZYjhzRzVCK09MYWIyREltRwo5OUNKMjVSa0FjU29iV05GNXpEME82bGd
PbzNjRWRCL2tzQ3EzaG10bEMvRGxMWi9EOENKKzdWdVpuUzFyUjJuCmFRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==`
validKey = `LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFdlFJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLY3dnZ1NqQWdFQUFv
SUJBUUROUUlXak9BMXZXSFV6ClNQTTFGSUtPRTRHZEg2NVZ0V2xwWjlkZ2hINENGWU4wUjdtdkpqNEtCcTg2RHh0OHZKdkxNVjE2R1ZoME5HQ1IKNTBRSDhh
TWJ4b25EVHFYU29YaU1NNEREU1FUS0JZSzdhWndmdGM3RkczNWdBZmROVWRyOGU3VmJkYVBPU2h1cQpxb3REeUNRcFpZemJ0ODZBQm5vYUo1b2tFM3BVRkl3
eHc5N0xjZFlzR1p6NU5nbWEvVjF0bzdhTWVFcUh5bDhyCkRSYlhaVXp3L1U4Zzd5Qy9nK0c3KzY0bGlKNEZZcUxFRVRMTFNVZVBLTEZnVUpIWGJGMkhnSURq
dXIzbnhsRWEKZWNOUVlWVVRWQ0dCRnB3a0k1bjF0M20zMmF2d290cFVGaE1JbWprUkVUeVBLWnB2bDArcDdtb3A4bXdKbUtwYQpDVnVOU2oyM0FnTUJBQUVD
Z2dFQUJuNEkvQjIweHhYY056QVNpVlpKdnVhOURkUkh0bXhUbGtMem5CajB4Mm9ZCnkxL05iczNkM29GUm41dUV1aEJaT1RjcGhzZ3dkUlNIRFhac1AzZ1VP
YmV3K2QyTi96aWVVSWo4aExEVmx2SlAKclUvczRVL2w1M1EwTGlOQnlFOVRodkwrekpMUENLSnRkNXVIWmpCNWZGbTY5K1E3Z3U4eGc0eEhJdWIrMHBQNQpQ
SGFubUhDRHJiZ05OL29xbGFyNEZaMk1YVGdla1c2QW15Yy9rb0U5aEluNEJhYTJLZS9CL0FVR1k0cE1STHFwClRBcnQrR1RWZVdlb0ZZOVFBQ1VwYUhwSmhH
Yi9QaW91NnRsVTU3ZTQyY0xva2kxZjArU0FSc0JCS3lYQTdCQjEKMWZNSDEwS1FZRkE2OGRUWVdsS3pRYXUvSzR4YXFnNEZLbXR3RjY2R1FRS0JnUUQ5T3BO
VVM3b1J4TUhWSmFCUgpUTldXK1YxRlh5Y3FvamVrRnBEaWpQYjJYNUNXVjE2b2VXZ2FYcDBuT0hGZHk5RVdzM0d0R3BmWmFzYVJWSHNYClNIdFBoNE5iOEpx
SGRHRTAvQ0Q2dDArNERuczhCbjljU3F0ZFFCN1IzSm43SU1YaTlYL1U4TERLbytBMTgvSnEKVjhWZ1VuZ01ueTlZak1rUUliSzhUUldrWVFLQmdRRFBmNG54
TzZqdSt0T0hIT1JRdHkzYllERDArT1YzSTArTAoweXowdVByZXJ5QlZpOW5ZNDNLYWtINTJEN1VaRXd3c0JqakdYRCtXSDh4RXNtQldzR05YSnUwMjVQdnpJ
Sm96CmxBRWlYdk1wL05tWXArdFk0ckRtTzhSaHlWb2NCcVdIemgzOG0wSUZPZDRCeUZENW5MRURyQTNwRFZvMGFOZ1kKbjBHd1J5c1pGd0tCZ1FEa0NqM202
Wk1Vc1VXRXR5K2FSMEVKaG1LeU9EQkRPblkwOUlWaEgyUy9GZXhWRnpVTgpMdGZLOTIwNmhwL0F3ZXozTG4ydVQ0WnpxcTVLN2ZNelVuaUpkQldkVkIwMDRs
OHZvZVhwSWU5T1p1d2ZjQko5CmdGaTF6eXB4L3VGRHY0MjFCelFwQk4rUWZPZEtidmJkUVZGam5xQ3hiU0RyODB5VmxHTXJJNWZid1FLQmdHMDkKb1JyZXBP
N0VJTzhHTi9HQ3J1TEsvcHRLR2t5aHkzUTZ4blZFbWRiNDdoWDduY0pBNUlvWlBtcmJsQ1ZTVU5zdwpuMTFYSGFia3NMOE9CZ2c5cnQ4b1FFVGhRdi9hRHpU
T1c5YURsSk5yYWdlamlCVHdxOTlhWWVaMWdqbzFDWnE0CjJqS3VicENmeVpDNHJHRHRySWZaWWkxcStTMlVjUWh0ZDhEZGh3UWJBb0dBQU00RXBEQTR5SEI1
eWllazFwL28KQ2JxUkN0YS9EeDZFeW8wS2xOQXlQdUZQQXNodXBHNE5CeDdtVDJBU2ZMKzJWQkhvaTZtSFNyaStCRFg1cnlZRgpmTVl2cDdVUllvcTd3N3Fp
dlJsdnZFZzV5b1lySzEzRjIrR2o2eEo0akVOOW0wS2RNL2czbUpHcTBIQlRJUXJwClNtNzVXWHNmbE94dVRuMDhMYmdHYzRzPQotLS0tLUVORCBSU0EgUFJJ
VkFURSBLRVktLS0tLQ==`
)
var (
isAdmin = true
)
func TestReload(t *testing.T) {
testCases := []struct {
description string
settings models.SSOSettings
expectedServersConfig *ldap.ServersConfig
expectedConfig *ldap.Config
}{
{
description: "basic flow with minimal config",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": true,
"skip_org_role_sync": false,
"allow_sign_up": true,
"config": map[string]any{
"servers": []any{
map[string]any{
"host": "127.0.0.1",
},
},
},
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{
Servers: []*ldap.ServerConfig{
{
Host: "127.0.0.1",
},
},
},
expectedConfig: &ldap.Config{
Enabled: true,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
{
description: "complete set of server parameters",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": true,
"skip_org_role_sync": false,
"allow_sign_up": true,
"config": map[string]any{
"servers": []any{
map[string]any{
"host": "127.0.0.1",
"port": 3389,
"bind_dn": "cn=ldapservice,ou=users,dc=ldap,dc=goauthentik,dc=io",
"bind_password": "grafana",
"search_filter": "(cn=%s)",
"ssl_skip_verify": false,
"use_ssl": true,
"start_tls": true,
"min_tls_version": "TLS1.3",
"timeout": 10,
"root_ca_cert": "/path/to/certificate.crt",
"root_ca_cert_value": []string{validCert},
"client_cert": "/path/to/client.crt",
"client_cert_value": validCert,
"client_key": "/path/to/client.key",
"client_key_value": validKey,
"group_search_filter": "(&(objectClass=posixGroup)(memberUid=%s))",
"group_search_filter_user_attribute": "distinguishedName",
"group_search_base_dns": []string{"ou=groups,dc=grafana,dc=org"},
"tls_ciphers": []string{
"TLS_AES_256_GCM_SHA384",
},
"attributes": map[string]string{
"email": "mail",
"member_of": "memberOf",
"name": "displayName",
"surname": "sn",
"username": "cn",
},
"search_base_dns": []string{
"DC=ldap,DC=goauthentik,DC=io",
},
"group_mappings": []any{
map[string]any{
"group_dn": "cn=admin,ou=groups,dc=ldap,dc=goauthentik,dc=io",
"org_id": 1,
"org_role": "Admin",
"grafana_admin": true,
},
map[string]any{
"group_dn": "cn=editor,ou=groups,dc=ldap,dc=goauthentik,dc=io",
"org_id": 1,
"org_role": "Editor",
},
map[string]any{
"group_dn": "cn=viewer,ou=groups,dc=ldap,dc=goauthentik,dc=io",
"org_id": 1,
"org_role": "Viewer",
},
},
},
},
},
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{
Servers: []*ldap.ServerConfig{
{
Host: "127.0.0.1",
Port: 3389,
UseSSL: true,
StartTLS: true,
SkipVerifySSL: false,
MinTLSVersion: "TLS1.3",
TLSCiphers: []string{
"TLS_AES_256_GCM_SHA384",
},
RootCACert: "/path/to/certificate.crt",
RootCACertValue: []string{validCert},
ClientCert: "/path/to/client.crt",
ClientCertValue: validCert,
ClientKey: "/path/to/client.key",
ClientKeyValue: validKey,
BindDN: "cn=ldapservice,ou=users,dc=ldap,dc=goauthentik,dc=io",
BindPassword: "grafana",
Timeout: 10,
Attr: ldap.AttributeMap{
Username: "cn",
Name: "displayName",
Surname: "sn",
Email: "mail",
MemberOf: "memberOf",
},
SearchFilter: "(cn=%s)",
SearchBaseDNs: []string{"DC=ldap,DC=goauthentik,DC=io"},
GroupSearchFilter: "(&(objectClass=posixGroup)(memberUid=%s))",
GroupSearchFilterUserAttribute: "distinguishedName",
GroupSearchBaseDNs: []string{"ou=groups,dc=grafana,dc=org"},
Groups: []*ldap.GroupToOrgRole{
{
GroupDN: "cn=admin,ou=groups,dc=ldap,dc=goauthentik,dc=io",
OrgId: 1,
OrgRole: "Admin",
IsGrafanaAdmin: &isAdmin,
},
{
GroupDN: "cn=editor,ou=groups,dc=ldap,dc=goauthentik,dc=io",
OrgId: 1,
OrgRole: "Editor",
},
{
GroupDN: "cn=viewer,ou=groups,dc=ldap,dc=goauthentik,dc=io",
OrgId: 1,
OrgRole: "Viewer",
},
},
},
},
},
expectedConfig: &ldap.Config{
Enabled: true,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
{
description: "no servers config",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": true,
"skip_org_role_sync": false,
"allow_sign_up": true,
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{Servers: nil},
expectedConfig: &ldap.Config{
Enabled: true,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
{
description: "invalid settings",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"invalid": "some-value",
"another_one": true,
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{Servers: nil},
expectedConfig: &ldap.Config{
AllowSignUp: true,
},
},
{
description: "config disabled",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": false,
"skip_org_role_sync": false,
"allow_sign_up": true,
"config": map[string]any{
"servers": []any{
map[string]any{
"host": "127.0.0.1",
},
},
},
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{
Servers: []*ldap.ServerConfig{
{
Host: "127.0.0.1",
},
},
},
expectedConfig: &ldap.Config{
Enabled: false,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
}
for _, tt := range testCases {
t.Run(tt.description, func(t *testing.T) {
ldapImpl := &LDAPImpl{
features: featuremgmt.WithManager(featuremgmt.FlagSsoSettingsApi),
loadingMutex: &sync.Mutex{},
}
err := ldapImpl.Reload(context.Background(), tt.settings)
require.NoError(t, err)
require.Equal(t, *tt.expectedServersConfig, *ldapImpl.ldapCfg)
require.Equal(t, *tt.expectedConfig, *ldapImpl.cfg)
})
}
}
+31 -10
View File
@@ -15,15 +15,25 @@ import (
const defaultTimeout = 10
// Config holds list of connections to LDAP
// Config holds parameters from the .ini config file
type Config struct {
Enabled bool
ConfigFilePath string
AllowSignUp bool
SkipOrgRoleSync bool
SyncCron string
ActiveSyncEnabled bool
}
// ServersConfig holds list of connections to LDAP
type ServersConfig struct {
Servers []*ServerConfig `toml:"servers" json:"servers"`
}
// ServerConfig holds connection data to LDAP
type ServerConfig struct {
Host string `toml:"host"`
Port int `toml:"port"`
Host string `toml:"host" json:"host"`
Port int `toml:"port" json:"port,omitempty"`
UseSSL bool `toml:"use_ssl" json:"use_ssl,omitempty"`
StartTLS bool `toml:"start_tls" json:"start_tls,omitempty"`
@@ -83,16 +93,27 @@ var loadingMutex = &sync.Mutex{}
// We need to define in this space so `GetConfig` fn
// could be defined as singleton
var config *Config
var config *ServersConfig
func GetLDAPConfig(cfg *setting.Cfg) *Config {
return &Config{
Enabled: cfg.LDAPAuthEnabled,
ConfigFilePath: cfg.LDAPConfigFilePath,
AllowSignUp: cfg.LDAPAllowSignup,
SkipOrgRoleSync: cfg.LDAPSkipOrgRoleSync,
SyncCron: cfg.LDAPSyncCron,
ActiveSyncEnabled: cfg.LDAPActiveSyncEnabled,
}
}
// GetConfig returns the LDAP config if LDAP is enabled otherwise it returns nil. It returns either cached value of
// the config or it reads it and caches it first.
func GetConfig(cfg *setting.Cfg) (*Config, error) {
func GetConfig(cfg *Config) (*ServersConfig, error) {
if cfg != nil {
if !cfg.LDAPAuthEnabled {
if !cfg.Enabled {
return nil, nil
}
} else if !cfg.LDAPAuthEnabled {
} else if !cfg.Enabled {
return nil, nil
}
@@ -104,11 +125,11 @@ func GetConfig(cfg *setting.Cfg) (*Config, error) {
loadingMutex.Lock()
defer loadingMutex.Unlock()
return readConfig(cfg.LDAPConfigFilePath)
return readConfig(cfg.ConfigFilePath)
}
func readConfig(configFile string) (*Config, error) {
result := &Config{}
func readConfig(configFile string) (*ServersConfig, error) {
result := &ServersConfig{}
logger.Info("LDAP enabled, reading config file", "file", configFile)
+6 -1
View File
@@ -332,12 +332,14 @@ func setupRedisLiveEngine(g *GrafanaLive, node *centrifuge.Node) error {
redisShardConfigs := []centrifuge.RedisShardConfig{
{Address: redisAddress, Password: redisPassword},
}
var redisShards []*centrifuge.RedisShard
redisShards := make([]*centrifuge.RedisShard, 0, len(redisShardConfigs))
for _, redisConf := range redisShardConfigs {
redisShard, err := centrifuge.NewRedisShard(node, redisConf)
if err != nil {
return fmt.Errorf("error connecting to Live Redis: %v", err)
}
redisShards = append(redisShards, redisShard)
}
@@ -348,6 +350,7 @@ func setupRedisLiveEngine(g *GrafanaLive, node *centrifuge.Node) error {
if err != nil {
return fmt.Errorf("error creating Live Redis broker: %v", err)
}
node.SetBroker(broker)
presenceManager, err := centrifuge.NewRedisPresenceManager(node, centrifuge.RedisPresenceManagerConfig{
@@ -357,7 +360,9 @@ func setupRedisLiveEngine(g *GrafanaLive, node *centrifuge.Node) error {
if err != nil {
return fmt.Errorf("error creating Live Redis presence manager: %v", err)
}
node.SetPresenceManager(presenceManager)
return nil
}
+6 -2
View File
@@ -116,17 +116,21 @@ func (s *Manager) handleDatasourceEvent(orgID int64, dsUID string, resubmit bool
s.mu.RUnlock()
return nil
}
var resubmitRequests []streamRequest
var waitChannels []chan struct{}
resubmitRequests := make([]streamRequest, 0, len(dsStreams))
waitChannels := make([]chan struct{}, 0, len(dsStreams))
for channel := range dsStreams {
streamCtx, ok := s.streams[channel]
if !ok {
continue
}
streamCtx.cancelFn()
waitChannels = append(waitChannels, streamCtx.CloseCh)
resubmitRequests = append(resubmitRequests, streamCtx.streamRequest)
}
s.mu.RUnlock()
// Wait for all streams to stop.
+1 -1
View File
@@ -43,7 +43,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
}
if s.features.IsEnabled(ctx, featuremgmt.FlagFeatureToggleAdminPage) && hasAccess(ac.EvalPermission(ac.ActionFeatureManagementRead)) {
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
Text: "Feature Toggles",
Text: "Feature toggles",
SubTitle: "View and edit feature toggles",
Id: "feature-toggles",
Url: s.cfg.AppSubURL + "/admin/featuretoggles",
+12 -2
View File
@@ -358,8 +358,8 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
Text: "Recently Deleted",
SubTitle: "Any items listed here for more than 30 days will be automatically deleted.",
Id: "dashboards/recentlyDeleted",
Url: s.cfg.AppSubURL + "/dashboard/recentlyDeleted",
Id: "dashboards/recently-deleted",
Url: s.cfg.AppSubURL + "/dashboard/recently-deleted",
})
}
}
@@ -408,6 +408,16 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Alert groups", SubTitle: "See grouped alerts from an Alertmanager instance", Id: "groups", Url: s.cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
}
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingCentralAlertHistory) {
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
Text: "History",
SubTitle: "History of events that were generated by your Grafana-managed alert rules. Silences and Mute timings are ignored.",
Id: "alerts-history",
Url: s.cfg.AppSubURL + "/alerting/history",
Icon: "history",
})
}
if c.SignedInUser.GetOrgRole() == org.RoleAdmin {
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
Text: "Settings", Id: "alerting-admin", Url: s.cfg.AppSubURL + "/alerting/admin",
+7 -7
View File
@@ -97,8 +97,8 @@ func PrepareAlertStatuses(manager state.AlertInstanceManager, opts AlertStatuses
}
alertResponse.Data.Alerts = append(alertResponse.Data.Alerts, &apimodels.Alert{
Labels: alertState.GetLabels(labelOptions...),
Annotations: alertState.Annotations,
Labels: apimodels.LabelsFromMap(alertState.GetLabels(labelOptions...)),
Annotations: apimodels.LabelsFromMap(alertState.Annotations),
// TODO: or should we make this two fields? Using one field lets the
// frontend use the same logic for parsing text on annotations and this.
@@ -444,12 +444,12 @@ func toRuleGroup(log log.Logger, manager state.AlertInstanceManager, groupKey ng
Name: rule.Title,
Query: ruleToQuery(log, rule),
Duration: rule.For.Seconds(),
Annotations: rule.Annotations,
Annotations: apimodels.LabelsFromMap(rule.Annotations),
}
newRule := apimodels.Rule{
Name: rule.Title,
Labels: rule.GetLabels(labelOptions...),
Labels: apimodels.LabelsFromMap(rule.GetLabels(labelOptions...)),
Health: "ok",
Type: rule.Type().String(),
LastEvaluation: time.Time{},
@@ -471,8 +471,8 @@ func toRuleGroup(log log.Logger, manager state.AlertInstanceManager, groupKey ng
totals["error"] += 1
}
alert := apimodels.Alert{
Labels: alertState.GetLabels(labelOptions...),
Annotations: alertState.Annotations,
Labels: apimodels.LabelsFromMap(alertState.GetLabels(labelOptions...)),
Annotations: apimodels.LabelsFromMap(alertState.Annotations),
// TODO: or should we make this two fields? Using one field lets the
// frontend use the same logic for parsing text on annotations and this.
@@ -563,8 +563,8 @@ func toRuleGroup(log log.Logger, manager state.AlertInstanceManager, groupKey ng
// Returns the whole JSON model as a string if it fails to extract a minimum of 1 query.
func ruleToQuery(logger log.Logger, rule *ngmodels.AlertRule) string {
var queryErr error
var queries []string
queries := make([]string, 0, len(rule.Data))
for _, q := range rule.Data {
q, err := q.GetQuery()
if err != nil {
@@ -46,156 +46,164 @@ func ContactPointToContactPointExport(cp definitions.ContactPoint) (notify.APIRe
// This is needed to keep the API models clean and convert from database model
j.RegisterExtension(&contactPointsExtension{})
var integration []*notify.GrafanaIntegrationConfig
contactPointsLength := len(cp.Alertmanager) + len(cp.Dingding) + len(cp.Discord) + len(cp.Email) +
len(cp.Googlechat) + len(cp.Kafka) + len(cp.Line) + len(cp.Opsgenie) +
len(cp.Pagerduty) + len(cp.OnCall) + len(cp.Pushover) + len(cp.Sensugo) +
len(cp.Sns) + len(cp.Slack) + len(cp.Teams) + len(cp.Telegram) +
len(cp.Threema) + len(cp.Victorops) + len(cp.Webhook) + len(cp.Wecom) +
len(cp.Webex)
integration := make([]*notify.GrafanaIntegrationConfig, 0, contactPointsLength)
var errs []error
for _, i := range cp.Alertmanager {
el, err := marshallIntegration(j, "prometheus-alertmanager", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Dingding {
el, err := marshallIntegration(j, "dingding", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Discord {
el, err := marshallIntegration(j, "discord", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Email {
el, err := marshallIntegration(j, "email", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Googlechat {
el, err := marshallIntegration(j, "googlechat", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Kafka {
el, err := marshallIntegration(j, "kafka", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Line {
el, err := marshallIntegration(j, "line", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Opsgenie {
el, err := marshallIntegration(j, "opsgenie", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Pagerduty {
el, err := marshallIntegration(j, "pagerduty", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.OnCall {
el, err := marshallIntegration(j, "oncall", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Pushover {
el, err := marshallIntegration(j, "pushover", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Sensugo {
el, err := marshallIntegration(j, "sensugo", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Sns {
el, err := marshallIntegration(j, "sns", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Slack {
el, err := marshallIntegration(j, "slack", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Teams {
el, err := marshallIntegration(j, "teams", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Telegram {
el, err := marshallIntegration(j, "telegram", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Threema {
el, err := marshallIntegration(j, "threema", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Victorops {
el, err := marshallIntegration(j, "victorops", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Webhook {
el, err := marshallIntegration(j, "webhook", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Wecom {
el, err := marshallIntegration(j, "wecom", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
for _, i := range cp.Webex {
el, err := marshallIntegration(j, "webex", i, i.DisableResolveMessage)
integration = append(integration, el)
if err != nil {
errs = append(errs, err)
}
integration = append(integration, el)
}
if len(errs) > 0 {
return notify.APIReceiver{}, errors.Join(errs...)
}
@@ -9,6 +9,7 @@ import (
"time"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
promlabels "github.com/prometheus/prometheus/model/labels"
)
// swagger:route GET /prometheus/grafana/api/v1/rules prometheus RouteGetGrafanaRuleStatuses
@@ -151,7 +152,7 @@ type AlertingRule struct {
Query string `json:"query,omitempty"`
Duration float64 `json:"duration,omitempty"`
// required: true
Annotations overrideLabels `json:"annotations,omitempty"`
Annotations promlabels.Labels `json:"annotations,omitempty"`
// required: true
ActiveAt *time.Time `json:"activeAt,omitempty"`
Alerts []Alert `json:"alerts,omitempty"`
@@ -166,8 +167,8 @@ type Rule struct {
// required: true
Name string `json:"name"`
// required: true
Query string `json:"query"`
Labels overrideLabels `json:"labels,omitempty"`
Query string `json:"query"`
Labels promlabels.Labels `json:"labels,omitempty"`
// required: true
Health string `json:"health"`
LastError string `json:"lastError,omitempty"`
@@ -181,9 +182,9 @@ type Rule struct {
// swagger:model
type Alert struct {
// required: true
Labels overrideLabels `json:"labels"`
Labels promlabels.Labels `json:"labels"`
// required: true
Annotations overrideLabels `json:"annotations"`
Annotations promlabels.Labels `json:"annotations"`
// required: true
State string `json:"state"`
ActiveAt *time.Time `json:"activeAt"`
@@ -300,31 +301,6 @@ func (by AlertsBy) TopK(alerts []Alert, k int) []Alert {
// is more important than "normal". If two alerts have the same importance
// then the ordering is based on their ActiveAt time and their labels.
func AlertsByImportance(a1, a2 *Alert) bool {
// labelsForComparison concatenates each key/value pair into a string and
// sorts them.
labelsForComparison := func(m map[string]string) []string {
s := make([]string, 0, len(m))
for k, v := range m {
s = append(s, k+v)
}
sort.Strings(s)
return s
}
// compareLabels returns true if labels1 are less than labels2. This happens
// when labels1 has fewer labels than labels2, or if the next label from
// labels1 is lexicographically less than the next label from labels2.
compareLabels := func(labels1, labels2 []string) bool {
if len(labels1) == len(labels2) {
for i := range labels1 {
if labels1[i] != labels2[i] {
return labels1[i] < labels2[i]
}
}
}
return len(labels1) < len(labels2)
}
// The importance of an alert is first based on the importance of their states.
// This ordering is intended to show the most important alerts first when
// using pagination.
@@ -345,9 +321,7 @@ func AlertsByImportance(a1, a2 *Alert) bool {
return true
}
// Both alerts are active since the same time so compare their labels
labels1 := labelsForComparison(a1.Labels)
labels2 := labelsForComparison(a2.Labels)
return compareLabels(labels1, labels2)
return promlabels.Compare(a1.Labels, a2.Labels) < 0
}
return importance1 < importance2
@@ -362,9 +336,16 @@ func (s AlertsSorter) Len() int { return len(s.alerts) }
func (s AlertsSorter) Swap(i, j int) { s.alerts[i], s.alerts[j] = s.alerts[j], s.alerts[i] }
func (s AlertsSorter) Less(i, j int) bool { return s.by(&s.alerts[i], &s.alerts[j]) }
// override the labels type with a map for generation.
// The custom marshaling for labels.Labels ends up doing this anyways.
type overrideLabels map[string]string
// LabelsFromMap creates Labels from a map. Note the Labels type requires the
// labels be sorted, so we make sure to do that.
func LabelsFromMap(m map[string]string) promlabels.Labels {
sb := promlabels.NewScratchBuilder(len(m))
for k, v := range m {
sb.Add(k, v)
}
sb.Sort()
return sb.Labels()
}
// swagger:parameters RouteGetGrafanaAlertStatuses
type GetGrafanaAlertStatusesParams struct {
@@ -21,10 +21,11 @@ func makeAlerts(amount int) []Alert {
alerts := make([]Alert, amount)
for i := 0; i < len(alerts); i++ {
alerts[i].Labels = make(map[string]string)
lbls := make(map[string]string)
for label := 0; label < numLabels; label++ {
alerts[i].Labels[fmt.Sprintf("label_%d", label)] = fmt.Sprintf("label_%d_value_%d", label, i%100)
lbls[fmt.Sprintf("label_%d", label)] = fmt.Sprintf("label_%d_value_%d", label, i%100)
}
alerts[i].Labels = LabelsFromMap(lbls)
if i%100 < percentAlerting {
alerts[i].State = "alerting"
@@ -69,32 +69,42 @@ func TestSortAlertsByImportance(t *testing.T) {
}, {
name: "inactive alerts with same importance are ordered by labels",
input: []Alert{
{State: "normal", Labels: map[string]string{"c": "d"}},
{State: "normal", Labels: map[string]string{"a": "b"}},
{State: "normal", Labels: LabelsFromMap(map[string]string{"c": "d"})},
{State: "normal", Labels: LabelsFromMap(map[string]string{"a": "b"})},
},
expected: []Alert{
{State: "normal", Labels: map[string]string{"a": "b"}},
{State: "normal", Labels: map[string]string{"c": "d"}},
{State: "normal", Labels: LabelsFromMap(map[string]string{"a": "b"})},
{State: "normal", Labels: LabelsFromMap(map[string]string{"c": "d"})},
},
}, {
name: "active alerts with same importance and active time are ordered fewest labels first",
name: "active alerts with same importance and active time are ordered by label names",
input: []Alert{
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"a": "b", "c": "d"}},
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"e": "f"}},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"c": "d", "e": "f"})},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"a": "b"})},
},
expected: []Alert{
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"e": "f"}},
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"a": "b", "c": "d"}},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"a": "b"})},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"c": "d", "e": "f"})},
},
}, {
name: "active alerts with same importance and active time are ordered by labels",
input: []Alert{
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"c": "d"}},
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"a": "b"}},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"c": "d"})},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"a": "b"})},
},
expected: []Alert{
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"a": "b"}},
{State: "alerting", ActiveAt: &tm1, Labels: map[string]string{"c": "d"}},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"a": "b"})},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"c": "d"})},
},
}, {
name: "active alerts with same importance and active time are ordered by label values",
input: []Alert{
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"x": "b"})},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"x": "a"})},
},
expected: []Alert{
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"x": "a"})},
{State: "alerting", ActiveAt: &tm1, Labels: LabelsFromMap(map[string]string{"x": "b"})},
},
}}
+54 -1
View File
@@ -7,6 +7,11 @@ import (
"strconv"
"github.com/grafana/dskit/instrument"
"github.com/grafana/grafana/pkg/infra/tracing"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"go.opentelemetry.io/otel/trace"
)
// Requester executes an HTTP request.
@@ -14,7 +19,7 @@ type Requester interface {
Do(req *http.Request) (*http.Response, error)
}
// TimedClient instruments a request. It implements Requester.
// TimedClient instruments a request with metrics. It implements Requester.
type TimedClient struct {
client Requester
collector instrument.Collector
@@ -70,3 +75,51 @@ func TimeRequest(ctx context.Context, operation string, coll instrument.Collecto
coll, toStatusCode, doRequest)
return response, err
}
// TracedClient instruments a request with tracing. It implements Requester.
type TracedClient struct {
client Requester
tracer tracing.Tracer
name string
}
func NewTracedClient(client Requester, tracer tracing.Tracer, name string) *TracedClient {
return &TracedClient{
client: client,
tracer: tracer,
name: name,
}
}
// Do executes the request.
func (c TracedClient) Do(r *http.Request) (*http.Response, error) {
ctx, span := c.tracer.Start(r.Context(), c.name, trace.WithSpanKind(trace.SpanKindClient))
defer span.End()
span.SetAttributes(semconv.HTTPURL(r.URL.String()))
span.SetAttributes(semconv.HTTPMethod(r.Method))
c.tracer.Inject(ctx, r.Header, span)
r = r.WithContext(ctx)
resp, err := c.client.Do(r)
if err != nil {
span.SetStatus(codes.Error, "request failed")
span.RecordError(err)
} else {
if resp.ContentLength > 0 {
span.SetAttributes(attribute.Int64("http.content_length", resp.ContentLength))
}
span.SetAttributes(semconv.HTTPStatusCode(resp.StatusCode))
if resp.StatusCode >= 400 && resp.StatusCode < 600 {
span.RecordError(fmt.Errorf("error with HTTP status code %d", resp.StatusCode))
}
}
return resp, err
}
// RoundTrip implements the RoundTripper interface.
func (c TracedClient) RoundTrip(r *http.Request) (*http.Response, error) {
return c.Do(r)
}
+10 -10
View File
@@ -200,7 +200,7 @@ func (ng *AlertNG) init() error {
PromoteConfig: true,
SyncInterval: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.SyncInterval,
}
remoteAM, err := createRemoteAlertmanager(cfg, ng.KVStore, ng.SecretsService.Decrypt, autogenFn, m)
remoteAM, err := createRemoteAlertmanager(cfg, ng.KVStore, ng.SecretsService.Decrypt, autogenFn, m, ng.tracer)
if err != nil {
moaLogger.Error("Failed to create remote Alertmanager", "err", err)
return nil, err
@@ -234,7 +234,7 @@ func (ng *AlertNG) init() error {
TenantID: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.TenantID,
URL: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.URL,
}
remoteAM, err := createRemoteAlertmanager(cfg, ng.KVStore, ng.SecretsService.Decrypt, autogenFn, m)
remoteAM, err := createRemoteAlertmanager(cfg, ng.KVStore, ng.SecretsService.Decrypt, autogenFn, m, ng.tracer)
if err != nil {
moaLogger.Error("Failed to create remote Alertmanager, falling back to using only the internal one", "err", err)
return internalAM, nil
@@ -270,7 +270,7 @@ func (ng *AlertNG) init() error {
URL: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.URL,
SyncInterval: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.SyncInterval,
}
remoteAM, err := createRemoteAlertmanager(cfg, ng.KVStore, ng.SecretsService.Decrypt, autogenFn, m)
remoteAM, err := createRemoteAlertmanager(cfg, ng.KVStore, ng.SecretsService.Decrypt, autogenFn, m, ng.tracer)
if err != nil {
moaLogger.Error("Failed to create remote Alertmanager, falling back to using only the internal one", "err", err)
return internalAM, nil
@@ -359,7 +359,7 @@ func (ng *AlertNG) init() error {
// There are a set of feature toggles available that act as short-circuits for common configurations.
// If any are set, override the config accordingly.
ApplyStateHistoryFeatureToggles(&ng.Cfg.UnifiedAlerting.StateHistory, ng.FeatureToggles, ng.Log)
history, err := configureHistorianBackend(initCtx, ng.Cfg.UnifiedAlerting.StateHistory, ng.annotationsRepo, ng.dashboardService, ng.store, ng.Metrics.GetHistorianMetrics(), ng.Log)
history, err := configureHistorianBackend(initCtx, ng.Cfg.UnifiedAlerting.StateHistory, ng.annotationsRepo, ng.dashboardService, ng.store, ng.Metrics.GetHistorianMetrics(), ng.Log, ng.tracer)
if err != nil {
return err
}
@@ -523,7 +523,7 @@ type Historian interface {
state.Historian
}
func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingStateHistorySettings, ar annotations.Repository, ds dashboards.DashboardService, rs historian.RuleStore, met *metrics.Historian, l log.Logger) (Historian, error) {
func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingStateHistorySettings, ar annotations.Repository, ds dashboards.DashboardService, rs historian.RuleStore, met *metrics.Historian, l log.Logger, tracer tracing.Tracer) (Historian, error) {
if !cfg.Enabled {
met.Info.WithLabelValues("noop").Set(0)
return historian.NewNopHistorian(), nil
@@ -538,7 +538,7 @@ func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingS
if backend == historian.BackendTypeMultiple {
primaryCfg := cfg
primaryCfg.Backend = cfg.MultiPrimary
primary, err := configureHistorianBackend(ctx, primaryCfg, ar, ds, rs, met, l)
primary, err := configureHistorianBackend(ctx, primaryCfg, ar, ds, rs, met, l, tracer)
if err != nil {
return nil, fmt.Errorf("multi-backend target \"%s\" was misconfigured: %w", cfg.MultiPrimary, err)
}
@@ -547,7 +547,7 @@ func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingS
for _, b := range cfg.MultiSecondaries {
secCfg := cfg
secCfg.Backend = b
sec, err := configureHistorianBackend(ctx, secCfg, ar, ds, rs, met, l)
sec, err := configureHistorianBackend(ctx, secCfg, ar, ds, rs, met, l, tracer)
if err != nil {
return nil, fmt.Errorf("multi-backend target \"%s\" was miconfigured: %w", b, err)
}
@@ -569,7 +569,7 @@ func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingS
}
req := historian.NewRequester()
lokiBackendLogger := log.New("ngalert.state.historian", "backend", "loki")
backend := historian.NewRemoteLokiBackend(lokiBackendLogger, lcfg, req, met)
backend := historian.NewRemoteLokiBackend(lokiBackendLogger, lcfg, req, met, tracer)
testConnCtx, cancelFunc := context.WithTimeout(ctx, 10*time.Second)
defer cancelFunc()
@@ -627,8 +627,8 @@ func ApplyStateHistoryFeatureToggles(cfg *setting.UnifiedAlertingStateHistorySet
}
}
func createRemoteAlertmanager(cfg remote.AlertmanagerConfig, kvstore kvstore.KVStore, decryptFn remote.DecryptFn, autogenFn remote.AutogenFn, m *metrics.RemoteAlertmanager) (*remote.Alertmanager, error) {
return remote.NewAlertmanager(cfg, notifier.NewFileStore(cfg.OrgID, kvstore), decryptFn, autogenFn, m)
func createRemoteAlertmanager(cfg remote.AlertmanagerConfig, kvstore kvstore.KVStore, decryptFn remote.DecryptFn, autogenFn remote.AutogenFn, m *metrics.RemoteAlertmanager, tracer tracing.Tracer) (*remote.Alertmanager, error) {
return remote.NewAlertmanager(cfg, notifier.NewFileStore(cfg.OrgID, kvstore), decryptFn, autogenFn, m, tracer)
}
func createRecordingWriter(featureToggles featuremgmt.FeatureToggles, settings setting.RecordingRuleSettings) (schedule.RecordingWriter, error) {
+12 -6
View File
@@ -62,12 +62,13 @@ func TestConfigureHistorianBackend(t *testing.T) {
t.Run("fail initialization if invalid backend", func(t *testing.T) {
met := metrics.NewHistorianMetrics(prometheus.NewRegistry(), metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: true,
Backend: "invalid-backend",
}
_, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger)
_, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer)
require.ErrorContains(t, err, "unrecognized")
})
@@ -75,13 +76,14 @@ func TestConfigureHistorianBackend(t *testing.T) {
t.Run("fail initialization if invalid multi-backend primary", func(t *testing.T) {
met := metrics.NewHistorianMetrics(prometheus.NewRegistry(), metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: true,
Backend: "multiple",
MultiPrimary: "invalid-backend",
}
_, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger)
_, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer)
require.ErrorContains(t, err, "multi-backend target")
require.ErrorContains(t, err, "unrecognized")
@@ -90,6 +92,7 @@ func TestConfigureHistorianBackend(t *testing.T) {
t.Run("fail initialization if invalid multi-backend secondary", func(t *testing.T) {
met := metrics.NewHistorianMetrics(prometheus.NewRegistry(), metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: true,
Backend: "multiple",
@@ -97,7 +100,7 @@ func TestConfigureHistorianBackend(t *testing.T) {
MultiSecondaries: []string{"annotations", "invalid-backend"},
}
_, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger)
_, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer)
require.ErrorContains(t, err, "multi-backend target")
require.ErrorContains(t, err, "unrecognized")
@@ -106,6 +109,7 @@ func TestConfigureHistorianBackend(t *testing.T) {
t.Run("do not fail initialization if pinging Loki fails", func(t *testing.T) {
met := metrics.NewHistorianMetrics(prometheus.NewRegistry(), metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: true,
Backend: "loki",
@@ -114,7 +118,7 @@ func TestConfigureHistorianBackend(t *testing.T) {
LokiWriteURL: "http://gone.invalid",
}
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger)
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer)
require.NotNil(t, h)
require.NoError(t, err)
@@ -124,12 +128,13 @@ func TestConfigureHistorianBackend(t *testing.T) {
reg := prometheus.NewRegistry()
met := metrics.NewHistorianMetrics(reg, metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: true,
Backend: "annotations",
}
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger)
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer)
require.NotNil(t, h)
require.NoError(t, err)
@@ -146,11 +151,12 @@ grafana_alerting_state_history_info{backend="annotations"} 1
reg := prometheus.NewRegistry()
met := metrics.NewHistorianMetrics(reg, metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: false,
}
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger)
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer)
require.NotNil(t, h)
require.NoError(t, err)

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