Compare commits
304 Commits
release-12
...
release-12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9eb5b52c9 | ||
|
|
38d0a2cf23 | ||
|
|
9de7f45b2f | ||
|
|
dd4e2e7dc5 | ||
|
|
7c94db649a | ||
|
|
3e59890d48 | ||
|
|
45537fe2b6 | ||
|
|
10648b91f4 | ||
|
|
ef3e1b4731 | ||
|
|
d351ebe919 | ||
|
|
5d655ccf37 | ||
|
|
cfd4902d33 | ||
|
|
4fdbb9e5d7 | ||
|
|
53d26612cf | ||
|
|
6aff5890c6 | ||
|
|
a909c838b5 | ||
|
|
8e1063cf0a | ||
|
|
26c1134326 | ||
|
|
31c4188746 | ||
|
|
da3717b315 | ||
|
|
3c845fd337 | ||
|
|
af54287df1 | ||
|
|
560be84dc8 | ||
|
|
6b87718cde | ||
|
|
0e620047b5 | ||
|
|
c0e87eeb98 | ||
|
|
550a339cab | ||
|
|
27a095a971 | ||
|
|
394f5d8ce4 | ||
|
|
323a4a24fa | ||
|
|
cd1a52454f | ||
|
|
58dd6d242a | ||
|
|
0d2ee90ff1 | ||
|
|
39ca690c5a | ||
|
|
1bac5badac | ||
|
|
b0356011e3 | ||
|
|
bcc78ed4eb | ||
|
|
cc150e5bb3 | ||
|
|
b53d333cbf | ||
|
|
5ac3d0c76a | ||
|
|
cd1e08734c | ||
|
|
5195a6d72e | ||
|
|
ab1e43b8b9 | ||
|
|
560f875837 | ||
|
|
791f3f3c1d | ||
|
|
7dff0d42ee | ||
|
|
4bc0eb967a | ||
|
|
5c007f3544 | ||
|
|
97f7cfa946 | ||
|
|
3bc640a6bb | ||
|
|
fd74988a1a | ||
|
|
3f6d86983d | ||
|
|
e4aace167f | ||
|
|
ce2e3b1693 | ||
|
|
b398dde373 | ||
|
|
a87e7da961 | ||
|
|
5e1298ba48 | ||
|
|
809a7078cf | ||
|
|
2a2df37b25 | ||
|
|
b119dff5ca | ||
|
|
3b49fbaa5c | ||
|
|
3574f03e54 | ||
|
|
b6513f5273 | ||
|
|
481751c145 | ||
|
|
dc6bd2a20a | ||
|
|
aee6b20c7f | ||
|
|
bf23c3c9e1 | ||
|
|
f5aa69691c | ||
|
|
6fcecaf20d | ||
|
|
cc98b5d67d | ||
|
|
1e822f8575 | ||
|
|
8f146eccb0 | ||
|
|
ad378babc5 | ||
|
|
9a40555ed3 | ||
|
|
586439044e | ||
|
|
42bee60987 | ||
|
|
222e329130 | ||
|
|
fb59ff62a8 | ||
|
|
716cf170fb | ||
|
|
bdc079ebcb | ||
|
|
05a2a067d0 | ||
|
|
1895ff6b3e | ||
|
|
ad9312b746 | ||
|
|
7a6e4f8fed | ||
|
|
99d1cfdff2 | ||
|
|
de20e74de0 | ||
|
|
b2825ffde9 | ||
|
|
e5cfceac59 | ||
|
|
be35f2dd83 | ||
|
|
e6b078bc0f | ||
|
|
5680880303 | ||
|
|
ea91e8b84c | ||
|
|
e45a1b539d | ||
|
|
c468f7842b | ||
|
|
b296a51d0d | ||
|
|
037fe5dfe9 | ||
|
|
3de18b7281 | ||
|
|
ff2ac27301 | ||
|
|
623302d055 | ||
|
|
384797ba3a | ||
|
|
e0b1e649b8 | ||
|
|
c0bf61f5e7 | ||
|
|
a4b56e7583 | ||
|
|
2dc0628b3d | ||
|
|
c6ebb91eb1 | ||
|
|
3ff82a3e6c | ||
|
|
74d69b50d1 | ||
|
|
60ecc8481c | ||
|
|
b0dad3f11b | ||
|
|
0989a8d89f | ||
|
|
d8f106637e | ||
|
|
960f7f9bc4 | ||
|
|
9cb8cd3a08 | ||
|
|
9ed9daa02b | ||
|
|
4682070384 | ||
|
|
6b963c16e2 | ||
|
|
1dcb62d369 | ||
|
|
03e074dc23 | ||
|
|
d7f26a5b8c | ||
|
|
be31ef48ce | ||
|
|
2990111377 | ||
|
|
75aa47e043 | ||
|
|
100068fdb2 | ||
|
|
cadc586916 | ||
|
|
25b41aabd5 | ||
|
|
6cb121d4e8 | ||
|
|
3df3c4b650 | ||
|
|
5739be39a4 | ||
|
|
06ea09bb6c | ||
|
|
fc19b95d23 | ||
|
|
40ee9e10e2 | ||
|
|
62c8b3e771 | ||
|
|
03b515d4d4 | ||
|
|
a924431998 | ||
|
|
675172ec54 | ||
|
|
f593454e54 | ||
|
|
c3ffd59702 | ||
|
|
b2712d5086 | ||
|
|
a9012e0444 | ||
|
|
c53915cb87 | ||
|
|
d6b7bb6759 | ||
|
|
1475ac930c | ||
|
|
febbfb1902 | ||
|
|
2a102f5b98 | ||
|
|
94ee07eebf | ||
|
|
9ca8c572e3 | ||
|
|
b03f1d6e21 | ||
|
|
8394986940 | ||
|
|
368c58b5bc | ||
|
|
f96a7461b2 | ||
|
|
6c0b0e8752 | ||
|
|
6b2c18d212 | ||
|
|
532877e133 | ||
|
|
af241422a0 | ||
|
|
2c3f53dd0e | ||
|
|
982238b4bd | ||
|
|
22b6da78a8 | ||
|
|
54200ca1cc | ||
|
|
577c29d0dc | ||
|
|
7a61c3d1b8 | ||
|
|
79786d3a82 | ||
|
|
7726a96386 | ||
|
|
1efb5a4160 | ||
|
|
b6dd2b70c6 | ||
|
|
7b2192ec76 | ||
|
|
94c960235f | ||
|
|
a522adbd10 | ||
|
|
0d308fe6d9 | ||
|
|
0a507ea3f8 | ||
|
|
a4fcc0f723 | ||
|
|
ac6f0d199f | ||
|
|
ddc0c74989 | ||
|
|
b352b8b4fb | ||
|
|
294465bc7c | ||
|
|
076b525477 | ||
|
|
2de93c5022 | ||
|
|
e8b522e6bf | ||
|
|
d4bfcd88f6 | ||
|
|
23df5760cb | ||
|
|
5abfb574c0 | ||
|
|
b785340e13 | ||
|
|
1b24e238aa | ||
|
|
1a440137c7 | ||
|
|
44fd033dd1 | ||
|
|
c07c752702 | ||
|
|
4404143bcc | ||
|
|
a36c8315fa | ||
|
|
ce22d40908 | ||
|
|
558412082d | ||
|
|
67fcf65cd2 | ||
|
|
8e6fcce432 | ||
|
|
2ee71232fe | ||
|
|
788833d756 | ||
|
|
4d300016b9 | ||
|
|
e1a378434b | ||
|
|
908a5c8afb | ||
|
|
7816d5f3d0 | ||
|
|
aaf39c0e3f | ||
|
|
3b4fd9f5e3 | ||
|
|
888e48b03e | ||
|
|
b34497d118 | ||
|
|
bf293c8ab6 | ||
|
|
a1c1523a41 | ||
|
|
2e6b7bf74a | ||
|
|
f792abc737 | ||
|
|
fdc10124f1 | ||
|
|
b1a4e09102 | ||
|
|
3689b41d7b | ||
|
|
95913e7bf6 | ||
|
|
0ae4f53b91 | ||
|
|
8721abf454 | ||
|
|
22cd1cf11d | ||
|
|
81de445637 | ||
|
|
57ab63e11d | ||
|
|
1f52eb2f52 | ||
|
|
a10f971609 | ||
|
|
7831e39e53 | ||
|
|
c4723691c6 | ||
|
|
4d1b11d4d5 | ||
|
|
60931b1393 | ||
|
|
daec1c0eba | ||
|
|
ab37272bd1 | ||
|
|
7ceda3cd2e | ||
|
|
2ae2420343 | ||
|
|
f37479e0d3 | ||
|
|
25f48a36bd | ||
|
|
c01be8220a | ||
|
|
35567cd7d6 | ||
|
|
f17e42fb40 | ||
|
|
c000fcd0b4 | ||
|
|
5906e2f7f1 | ||
|
|
994e644e62 | ||
|
|
c477381677 | ||
|
|
bc9e735df8 | ||
|
|
bd3d525a35 | ||
|
|
9a0f15e085 | ||
|
|
d991198778 | ||
|
|
6f806cb6f7 | ||
|
|
519ad181b9 | ||
|
|
5b795ae2e3 | ||
|
|
24bc8cc6bc | ||
|
|
3a479b49ef | ||
|
|
127b24e5c1 | ||
|
|
650f432eb4 | ||
|
|
f5b640e682 | ||
|
|
9357bda707 | ||
|
|
17df2d611f | ||
|
|
6db7f10bbc | ||
|
|
2e4020a93b | ||
|
|
c3aeda7a79 | ||
|
|
4112c80a7a | ||
|
|
0dccd77ec0 | ||
|
|
3956dc4141 | ||
|
|
92d0f18a5e | ||
|
|
2b67824505 | ||
|
|
6d8c492cbe | ||
|
|
96e46887d7 | ||
|
|
5ad33cb0a9 | ||
|
|
3b2165d787 | ||
|
|
5f3107c401 | ||
|
|
3c62214699 | ||
|
|
5631d9aace | ||
|
|
1da343ea5f | ||
|
|
9c339e7c7c | ||
|
|
a524a98367 | ||
|
|
f046259f91 | ||
|
|
6b7bba2b5a | ||
|
|
249bf8ac64 | ||
|
|
18a9cd3636 | ||
|
|
7287b4958f | ||
|
|
8bb4c806fb | ||
|
|
18fb652499 | ||
|
|
4620dc0b4e | ||
|
|
0716d2fc4f | ||
|
|
7dfb628cb4 | ||
|
|
549833e40d | ||
|
|
f04144c742 | ||
|
|
745015e74f | ||
|
|
cf21a9385b | ||
|
|
123351e3ac | ||
|
|
26b81feb85 | ||
|
|
8a4a68cf95 | ||
|
|
2fbb2d6f5d | ||
|
|
ed408985fa | ||
|
|
584593c411 | ||
|
|
b0d42f432a | ||
|
|
835516f832 | ||
|
|
2ce8b147d7 | ||
|
|
846f10afda | ||
|
|
384ce54148 | ||
|
|
c9956ffc59 | ||
|
|
92189eec7e | ||
|
|
cde4a9dabe | ||
|
|
2e878c4fdc | ||
|
|
4d6d46a181 | ||
|
|
b9ee6bae38 | ||
|
|
341a885a38 | ||
|
|
910ce8367a | ||
|
|
a6a5c77add | ||
|
|
e94d5f0119 | ||
|
|
9eb36ac222 | ||
|
|
2e6bfb76cb | ||
|
|
173db4aac7 | ||
|
|
44cfb7c6b4 |
26
.air.toml
26
.air.toml
@@ -1,26 +0,0 @@
|
||||
[build]
|
||||
bin = "./bin/grafana"
|
||||
args_bin = ["server", "-profile", "-profile-addr=127.0.0.1", "-profile-port=6000", "-profile-block-rate=1", "-profile-mutex-rate=5", "-packaging=dev", "cfg:app_mode=development"]
|
||||
cmd = "make GO_BUILD_DEV=1 build-backend"
|
||||
exclude_regex = ["_test.go", "_gen.go"]
|
||||
exclude_unchanged = true
|
||||
follow_symlink = true
|
||||
include_dir = ["apps", "conf", "devenv/dev-dashboards", "pkg", "public/views"]
|
||||
include_ext = ["go", "ini", "toml", "html", "json"]
|
||||
pre_cmd = ["make gen-go"]
|
||||
stop_on_error = true
|
||||
send_interrupt = true
|
||||
kill_delay = 500
|
||||
|
||||
[log]
|
||||
time = true
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
enabled = false
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
||||
@@ -10,7 +10,6 @@ const testingLibraryPlugin = require('eslint-plugin-testing-library');
|
||||
|
||||
const grafanaConfig = require('@grafana/eslint-config/flat');
|
||||
const grafanaPlugin = require('@grafana/eslint-plugin');
|
||||
const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin');
|
||||
|
||||
// Include the Grafana config and remove the rules,
|
||||
// as we just want to pull in all of the necessary configuration but not run the rules
|
||||
@@ -66,7 +65,6 @@ module.exports = [
|
||||
'no-barrel-files': barrelPlugin,
|
||||
'@grafana': grafanaPlugin,
|
||||
'testing-library': testingLibraryPlugin,
|
||||
'@grafana/i18n': grafanaI18nPlugin,
|
||||
},
|
||||
linterOptions: {
|
||||
// This reports unused disable directives that we can clean up but
|
||||
@@ -98,58 +96,33 @@ module.exports = [
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||
ignores: [
|
||||
'**/*.{test,spec}.{ts,tsx}',
|
||||
'**/__mocks__/**',
|
||||
'**/public/test/**',
|
||||
'**/mocks.{ts,tsx}',
|
||||
'**/mocks/**/*.{ts,tsx}',
|
||||
'**/spec/**/*.{ts,tsx}',
|
||||
],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
ignores: ['**/*.{test,spec}.{ts,tsx}', '**/__mocks__/**', '**/public/test/**', '**/mocks.{ts,tsx}'],
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||
ignores: [
|
||||
'**/*.{test,spec}.{ts,tsx}',
|
||||
'**/__mocks__/**',
|
||||
'**/public/test/**',
|
||||
'**/mocks.{ts,tsx}',
|
||||
'**/spec/**/*.{ts,tsx}',
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Identifier[name=localStorage]',
|
||||
message: 'Direct usage of localStorage is not allowed. import store from @grafana/data instead',
|
||||
},
|
||||
{
|
||||
selector: 'MemberExpression[object.name=localStorage]',
|
||||
message: 'Direct usage of localStorage is not allowed. import store from @grafana/data instead',
|
||||
},
|
||||
{
|
||||
selector:
|
||||
'Program:has(ImportDeclaration[source.value="@grafana/ui"] ImportSpecifier[imported.name="Card"]) JSXOpeningElement[name.name="Card"]:not(:has(JSXAttribute[name.name="noMargin"]))',
|
||||
message:
|
||||
'Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.',
|
||||
},
|
||||
{
|
||||
selector:
|
||||
'Program:has(ImportDeclaration[source.value="@grafana/ui"] ImportSpecifier[imported.name="Field"]) JSXOpeningElement[name.name="Field"]:not(:has(JSXAttribute[name.name="noMargin"]))',
|
||||
message:
|
||||
'Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['public/app/**/*.{ts,tsx}'],
|
||||
rules: {
|
||||
'no-barrel-files/no-barrel-files': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['public/**/*.tsx', 'packages/grafana-ui/**/*.tsx'],
|
||||
ignores: ['public/app/plugins/**', '**/*.story.tsx', '**/*.{test,spec}.{ts,tsx}', '**/__mocks__/', 'public/test'],
|
||||
rules: {
|
||||
'@grafana/no-untranslated-strings': [
|
||||
'error',
|
||||
{
|
||||
forceFix: [
|
||||
// Add paths here that are happy to be auto fixed by this rule,
|
||||
// for example
|
||||
// 'public/app/features/alerting'
|
||||
],
|
||||
},
|
||||
],
|
||||
'@grafana/no-translation-top-level': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
2265
.betterer.results
2265
.betterer.results
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
module bra
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool github.com/unknwon/bra
|
||||
|
||||
@@ -17,6 +17,6 @@ require (
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect
|
||||
github.com/urfave/cli v1.22.16 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
)
|
||||
|
||||
@@ -56,8 +56,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module cog
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool github.com/grafana/cog/cmd/cli
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // indirect
|
||||
github.com/grafana/cog v0.0.34 // indirect
|
||||
github.com/grafana/cog v0.0.28 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
@@ -40,11 +40,11 @@ require (
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/yalue/merged_fs v1.3.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/oauth2 v0.26.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -27,8 +27,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk=
|
||||
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
|
||||
github.com/grafana/cog v0.0.34 h1:tXtjIB0A0Y6jeZ5iAQBJIAz5vkAJsV0iEFfX94PH/+Q=
|
||||
github.com/grafana/cog v0.0.34/go.mod h1:UDstzYqMdgIROmbfkHL8fB9XWQO2lnf5z+4W/eJo4Dc=
|
||||
github.com/grafana/cog v0.0.28 h1:0+FNuxyfNWm1OBZPPjgBIno3nzR4gOpSxsVe34hdN7g=
|
||||
github.com/grafana/cog v0.0.28/go.mod h1:wZWsTLV7uX0jCbGpqvjawQ7JbaDVT9oW+PQhHwqanHc=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -85,20 +85,20 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY=
|
||||
github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module cue
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool cuelang.org/go/cmd/cue
|
||||
|
||||
@@ -25,13 +25,13 @@ require (
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.6.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/oauth2 v0.26.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -53,20 +53,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g=
|
||||
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -1,41 +1,48 @@
|
||||
module golangci-lint
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint
|
||||
|
||||
require (
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.2 // indirect
|
||||
github.com/4meepo/tagalign v1.4.2 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.3 // indirect
|
||||
github.com/Antonboom/errname v1.1.0 // indirect
|
||||
github.com/Antonboom/nilnil v1.1.0 // indirect
|
||||
github.com/Antonboom/testifylint v1.6.0 // indirect
|
||||
codeberg.org/chavacava/garif v0.2.0 // indirect
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
|
||||
dev.gaijin.team/go/golib v0.6.0 // indirect
|
||||
github.com/4meepo/tagalign v1.4.3 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.6 // indirect
|
||||
github.com/AdminBenni/iota-mixing v1.0.0 // indirect
|
||||
github.com/AlwxSin/noinlineerr v1.0.5 // indirect
|
||||
github.com/Antonboom/errname v1.1.1 // indirect
|
||||
github.com/Antonboom/nilnil v1.1.1 // indirect
|
||||
github.com/Antonboom/testifylint v1.6.4 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Crocmagnon/fatcontext v0.7.1 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect
|
||||
github.com/Djarvur/go-err113 v0.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
||||
github.com/MirrexOne/unqueryvet v1.2.1 // indirect
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 // indirect
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.5 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
|
||||
github.com/alexkohler/prealloc v1.0.0 // indirect
|
||||
github.com/alfatraining/structtag v1.0.0 // indirect
|
||||
github.com/alingse/asasalint v0.0.11 // indirect
|
||||
github.com/alingse/nilnesserr v0.1.2 // indirect
|
||||
github.com/ashanbrown/forbidigo v1.6.0 // indirect
|
||||
github.com/ashanbrown/makezero v1.2.0 // indirect
|
||||
github.com/alingse/nilnesserr v0.2.0 // indirect
|
||||
github.com/ashanbrown/forbidigo/v2 v2.1.0 // indirect
|
||||
github.com/ashanbrown/makezero/v2 v2.0.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.3 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.6.0 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
|
||||
github.com/bombsimon/wsl/v5 v5.2.0 // indirect
|
||||
github.com/breml/bidichk v0.3.3 // indirect
|
||||
github.com/breml/errchkjson v0.4.1 // indirect
|
||||
github.com/butuzov/ireturn v0.3.1 // indirect
|
||||
github.com/butuzov/ireturn v0.4.0 // indirect
|
||||
github.com/butuzov/mirror v1.3.0 // indirect
|
||||
github.com/catenacyber/perfsprint v0.9.1 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charithe/durationcheck v0.0.10 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
@@ -43,20 +50,20 @@ require (
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/ckaznocha/intrange v0.3.1 // indirect
|
||||
github.com/curioswitch/go-reassign v0.3.0 // indirect
|
||||
github.com/daixiang0/gci v0.13.6 // indirect
|
||||
github.com/daixiang0/gci v0.13.7 // indirect
|
||||
github.com/dave/dst v0.27.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/ettle/strcase v0.2.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.5 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.6 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.12 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.16 // indirect
|
||||
github.com/go-critic/go-critic v0.13.0 // indirect
|
||||
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
||||
@@ -68,46 +75,50 @@ require (
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/godoc-lint/godoc-lint v0.10.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golangci/asciicheck v0.5.0 // indirect
|
||||
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
|
||||
github.com/golangci/go-printf-func-name v0.1.0 // indirect
|
||||
github.com/golangci/go-printf-func-name v0.1.1 // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
|
||||
github.com/golangci/golangci-lint/v2 v2.0.2 // indirect
|
||||
github.com/golangci/golangci-lint/v2 v2.5.0 // indirect
|
||||
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect
|
||||
github.com/golangci/misspell v0.6.0 // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.1 // indirect
|
||||
github.com/golangci/misspell v0.7.0 // indirect
|
||||
github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.2 // indirect
|
||||
github.com/golangci/revgrep v0.8.0 // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/gordonklaus/ineffassign v0.1.0 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.2.0 // indirect
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
||||
github.com/gostaticanalysis/comment v1.5.0 // indirect
|
||||
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jgautheron/goconst v1.7.1 // indirect
|
||||
github.com/jgautheron/goconst v1.8.2 // indirect
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.4 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.5 // indirect
|
||||
github.com/julz/importas v0.2.0 // indirect
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect
|
||||
github.com/kisielk/errcheck v1.9.0 // indirect
|
||||
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
|
||||
github.com/kulti/thelper v0.6.3 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.10 // indirect
|
||||
github.com/kulti/thelper v0.7.1 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.14 // indirect
|
||||
github.com/lasiar/canonicalheader v1.1.2 // indirect
|
||||
github.com/ldez/exptostd v0.4.2 // indirect
|
||||
github.com/ldez/gomoddirectives v0.6.1 // indirect
|
||||
github.com/ldez/grignotin v0.9.0 // indirect
|
||||
github.com/ldez/tagliatelle v0.7.1 // indirect
|
||||
github.com/ldez/usetesting v0.4.2 // indirect
|
||||
github.com/ldez/exptostd v0.4.4 // indirect
|
||||
github.com/ldez/gomoddirectives v0.7.0 // indirect
|
||||
github.com/ldez/grignotin v0.10.1 // indirect
|
||||
github.com/ldez/tagliatelle v0.7.2 // indirect
|
||||
github.com/ldez/usetesting v0.5.0 // indirect
|
||||
github.com/leonklingele/grouper v1.1.2 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/macabu/inamedparam v0.2.0 // indirect
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
|
||||
github.com/manuelarte/funcorder v0.5.0 // indirect
|
||||
github.com/maratori/testableexamples v1.0.0 // indirect
|
||||
github.com/maratori/testpackage v1.1.1 // indirect
|
||||
github.com/matoous/godox v1.1.0 // indirect
|
||||
@@ -115,7 +126,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mgechev/revive v1.7.0 // indirect
|
||||
github.com/mgechev/revive v1.12.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moricho/tparallel v0.3.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
@@ -123,15 +134,14 @@ require (
|
||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.21.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.7.1 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.8.0 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.4.4 // indirect
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
|
||||
github.com/quasilyte/gogrep v0.5.0 // indirect
|
||||
@@ -144,58 +154,59 @@ require (
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.2 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.8 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
github.com/sonatard/noctx v0.1.0 // indirect
|
||||
github.com/sonatard/noctx v0.4.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/cobra v1.10.1 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.20.1 // indirect
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tdakkota/asciicheck v0.4.1 // indirect
|
||||
github.com/tetafro/godot v1.5.0 // indirect
|
||||
github.com/tetafro/godot v1.5.4 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
|
||||
github.com/timonwong/loggercheck v0.10.1 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect
|
||||
github.com/timonwong/loggercheck v0.11.0 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
||||
github.com/ultraware/funlen v0.2.0 // indirect
|
||||
github.com/ultraware/whitespace v0.2.0 // indirect
|
||||
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||
github.com/uudashr/iface v1.3.1 // indirect
|
||||
github.com/uudashr/iface v1.4.1 // indirect
|
||||
github.com/xen0n/gosmopolitan v1.3.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
github.com/yeya24/promlinter v0.3.0 // indirect
|
||||
github.com/ykadowak/zerologlint v0.1.5 // indirect
|
||||
gitlab.com/bosi/decorder v0.4.2 // indirect
|
||||
go-simpler.org/musttag v0.13.0 // indirect
|
||||
go-simpler.org/sloglint v0.9.0 // indirect
|
||||
go-simpler.org/musttag v0.14.0 // indirect
|
||||
go-simpler.org/sloglint v0.11.1 // indirect
|
||||
go.augendre.info/arangolint v0.2.0 // indirect
|
||||
go.augendre.info/fatcontext v0.8.1 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
honnef.co/go/tools v0.6.1 // indirect
|
||||
mvdan.cc/gofumpt v0.7.0 // indirect
|
||||
mvdan.cc/gofumpt v0.9.1 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
|
||||
)
|
||||
|
||||
@@ -2,46 +2,58 @@
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY=
|
||||
4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU=
|
||||
4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0=
|
||||
github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E=
|
||||
github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
|
||||
github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
|
||||
github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
|
||||
github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE=
|
||||
github.com/Antonboom/errname v1.1.0/go.mod h1:O1NMrzgUcVBGIfi3xlVuvX8Q/VP/73sseCaAppfjqZw=
|
||||
github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+ng=
|
||||
github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE=
|
||||
github.com/Antonboom/testifylint v1.6.0 h1:6rdILVPt4+rqcvhid8w9wJNynKLUgqHNpFyM67UeXyc=
|
||||
github.com/Antonboom/testifylint v1.6.0/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI=
|
||||
codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY=
|
||||
codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ=
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y=
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI=
|
||||
dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo=
|
||||
dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE=
|
||||
github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8=
|
||||
github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c=
|
||||
github.com/Abirdcfly/dupword v0.1.6 h1:qeL6u0442RPRe3mcaLcbaCi2/Y/hOcdtw6DE9odjz9c=
|
||||
github.com/Abirdcfly/dupword v0.1.6/go.mod h1:s+BFMuL/I4YSiFv29snqyjwzDp4b65W2Kvy+PKzZ6cw=
|
||||
github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo=
|
||||
github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY=
|
||||
github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY=
|
||||
github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc=
|
||||
github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q=
|
||||
github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ=
|
||||
github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ=
|
||||
github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II=
|
||||
github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ=
|
||||
github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM=
|
||||
github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg=
|
||||
github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g=
|
||||
github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/MirrexOne/unqueryvet v1.2.1 h1:M+zdXMq84g+E1YOLa7g7ExN3dWfZQrdDSTCM7gC+m/A=
|
||||
github.com/MirrexOne/unqueryvet v1.2.1/go.mod h1:IWwCwMQlSWjAIteW0t+28Q5vouyktfujzYznSIWiuOg=
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4=
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
|
||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q=
|
||||
github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
|
||||
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
|
||||
github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc=
|
||||
github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus=
|
||||
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
|
||||
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
|
||||
github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo=
|
||||
github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
|
||||
github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY=
|
||||
github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
|
||||
github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU=
|
||||
github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4=
|
||||
github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w=
|
||||
github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
|
||||
github.com/ashanbrown/forbidigo/v2 v2.1.0 h1:NAxZrWqNUQiDz19FKScQ/xvwzmij6BiOw3S0+QUQ+Hs=
|
||||
github.com/ashanbrown/forbidigo/v2 v2.1.0/go.mod h1:0zZfdNAuZIL7rSComLGthgc/9/n2FqspBOH90xlCHdA=
|
||||
github.com/ashanbrown/makezero/v2 v2.0.1 h1:r8GtKetWOgoJ4sLyUx97UTwyt2dO7WkGFHizn/Lo8TY=
|
||||
github.com/ashanbrown/makezero/v2 v2.0.1/go.mod h1:kKU4IMxmYW1M4fiEHMb2vc5SFoPzXvgbMR9gIp5pjSw=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -50,20 +62,22 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5
|
||||
github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
|
||||
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
|
||||
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
|
||||
github.com/bombsimon/wsl/v4 v4.6.0 h1:ew2R/N42su553DKTYqt3HSxaQN+uHQPv4xZ2MBmwaW4=
|
||||
github.com/bombsimon/wsl/v4 v4.6.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg=
|
||||
github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ=
|
||||
github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg=
|
||||
github.com/bombsimon/wsl/v5 v5.2.0 h1:PyCCwd3Q7abGs3e34IW4jLYlBS+FbsU6iK+Tb3NnDp4=
|
||||
github.com/bombsimon/wsl/v5 v5.2.0/go.mod h1:Gp8lD04z27wm3FANIUPZycXp+8huVsn0oxc+n4qfV9I=
|
||||
github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE=
|
||||
github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE=
|
||||
github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg=
|
||||
github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s=
|
||||
github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY=
|
||||
github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M=
|
||||
github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E=
|
||||
github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70=
|
||||
github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
|
||||
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
|
||||
github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD2z0vls0=
|
||||
github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4=
|
||||
@@ -78,15 +92,13 @@ github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0G
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
|
||||
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
|
||||
github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs=
|
||||
github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
|
||||
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
|
||||
github.com/daixiang0/gci v0.13.6 h1:RKuEOSkGpSadkGbvZ6hJ4ddItT3cVZ9Vn9Rybk6xjl8=
|
||||
github.com/daixiang0/gci v0.13.6/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
|
||||
github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ=
|
||||
github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ=
|
||||
github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY=
|
||||
github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
@@ -97,28 +109,28 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
|
||||
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
|
||||
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA=
|
||||
github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw=
|
||||
github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
|
||||
github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo=
|
||||
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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/ghostiam/protogetter v0.3.12 h1:xTPjH97iKph27vXRRKV0OCke5sAMoHPbVeVstdzmCLE=
|
||||
github.com/ghostiam/protogetter v0.3.12/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
|
||||
github.com/ghostiam/protogetter v0.3.16 h1:UkrisuJBYLnZW6FcYUNBDJOqY3X22RtoYMlCsiNlFFA=
|
||||
github.com/ghostiam/protogetter v0.3.16/go.mod h1:4SRRIv6PcjkIMpUkRUsP4TsUTqO/N3Fmvwivuc/sCHA=
|
||||
github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY=
|
||||
github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
@@ -148,47 +160,50 @@ github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUW
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/godoc-lint/godoc-lint v0.10.0 h1:OcyrziBi18sQSEpib6NesVHEJ/Xcng97NunePBA48g4=
|
||||
github.com/godoc-lint/godoc-lint v0.10.0/go.mod h1:KleLcHu/CGSvkjUH2RvZyoK1MBC7pDQg4NxMYLcBBsw=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0=
|
||||
github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ=
|
||||
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw=
|
||||
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E=
|
||||
github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU=
|
||||
github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
|
||||
github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U=
|
||||
github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss=
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
|
||||
github.com/golangci/golangci-lint/v2 v2.0.2 h1:dMCC8ikPiLDvHMFy3+XypSAuGDBOLzwWqqamer+bWsY=
|
||||
github.com/golangci/golangci-lint/v2 v2.0.2/go.mod h1:ptNNMeGBQrbves0Qq38xvfdJg18PzxmT+7KRCOpm6i8=
|
||||
github.com/golangci/golangci-lint/v2 v2.5.0 h1:BDRg4ASm4J1y/DSRY6zwJ5tr5Yy8ZqbZ79XrCeFxaQo=
|
||||
github.com/golangci/golangci-lint/v2 v2.5.0/go.mod h1:IJtWJBZkLbx7AVrIUzLd8Oi3ADtwaNpWbR3wthVWHcc=
|
||||
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 h1:AkK+w9FZBXlU/xUmBtSJN1+tAI4FIvy5WtnUnY8e4p8=
|
||||
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
|
||||
github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc=
|
||||
github.com/golangci/misspell v0.7.0 h1:4GOHr/T1lTW0hhR4tgaaV1WS/lJ+ncvYCoFKmqJsj0c=
|
||||
github.com/golangci/misspell v0.7.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg=
|
||||
github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe h1:F1pK9tBy41i7eesBFkSNMldwtiAaWiU+3fT/24sTnNI=
|
||||
github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe/go.mod h1:CtTxAluxD2ng9aIT9bPrVoMuISFWCD+SaxtvYtdWA2k=
|
||||
github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg=
|
||||
github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw=
|
||||
github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s=
|
||||
github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs=
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM=
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s=
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM=
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
||||
github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs=
|
||||
github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
|
||||
github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
|
||||
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
|
||||
github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8=
|
||||
github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
|
||||
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
|
||||
github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8=
|
||||
github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs=
|
||||
@@ -205,12 +220,12 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk=
|
||||
github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4=
|
||||
github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
|
||||
github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc=
|
||||
github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk=
|
||||
github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8=
|
||||
github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU=
|
||||
github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ=
|
||||
github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY=
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI=
|
||||
@@ -226,28 +241,32 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
|
||||
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
|
||||
github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs=
|
||||
github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
|
||||
github.com/kulti/thelper v0.7.1 h1:fI8QITAoFVLx+y+vSyuLBP+rcVIB8jKooNSCT2EiI98=
|
||||
github.com/kulti/thelper v0.7.1/go.mod h1:NsMjfQEy6sd+9Kfw8kCP61W1I0nerGSYSFnGaxQkcbs=
|
||||
github.com/kunwardeep/paralleltest v1.0.14 h1:wAkMoMeGX/kGfhQBPODT/BL8XhK23ol/nuQ3SwFaUw8=
|
||||
github.com/kunwardeep/paralleltest v1.0.14/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk=
|
||||
github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
|
||||
github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
|
||||
github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs=
|
||||
github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
|
||||
github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc=
|
||||
github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs=
|
||||
github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow=
|
||||
github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk=
|
||||
github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk=
|
||||
github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I=
|
||||
github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA=
|
||||
github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ=
|
||||
github.com/ldez/exptostd v0.4.4 h1:58AtQjnLcT/tI5W/1KU7xE/O7zW9RAWB6c/ScQAnfus=
|
||||
github.com/ldez/exptostd v0.4.4/go.mod h1:QfdzPw6oHjFVdNV7ILoPu5sw3OZ3OG1JS0I5JN3J4Js=
|
||||
github.com/ldez/gomoddirectives v0.7.0 h1:EOx8Dd56BZYSez11LVgdj025lKwlP0/E5OLSl9HDwsY=
|
||||
github.com/ldez/gomoddirectives v0.7.0/go.mod h1:wR4v8MN9J8kcwvrkzrx6sC9xe9Cp68gWYCsda5xvyGc=
|
||||
github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o=
|
||||
github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas=
|
||||
github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYUk=
|
||||
github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI=
|
||||
github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc=
|
||||
github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ=
|
||||
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
|
||||
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE=
|
||||
github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U=
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww=
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM=
|
||||
github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8=
|
||||
github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA=
|
||||
github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
|
||||
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
|
||||
github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
|
||||
@@ -261,11 +280,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY=
|
||||
github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4=
|
||||
github.com/mgechev/revive v1.12.0 h1:Q+/kkbbwerrVYPv9d9efaPGmAO/NsxwW/nE6ahpQaCU=
|
||||
github.com/mgechev/revive v1.12.0/go.mod h1:VXsY2LsTigk8XU9BpZauVLjVrhICMOV3k1lpB3CXrp8=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI=
|
||||
@@ -280,14 +298,12 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
|
||||
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/nunnatsa/ginkgolinter v0.21.0 h1:IYwuX+ajy3G1MezlMLB1BENRtFj16+Evyi4uki1NOOQ=
|
||||
github.com/nunnatsa/ginkgolinter v0.21.0/go.mod h1:QlzY9UP9zaqu58FjYxhp9bnjuwXwG1bfW5rid9ChNMw=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
|
||||
github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
|
||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||
@@ -295,13 +311,13 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA=
|
||||
github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8=
|
||||
github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q=
|
||||
github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
@@ -310,8 +326,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM=
|
||||
github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/quasilyte/go-ruleguard v0.4.4 h1:53DncefIeLX3qEpjzlS1lyUmQoUEeOWPFWqaTJq9eAQ=
|
||||
github.com/quasilyte/go-ruleguard v0.4.4/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
|
||||
@@ -338,14 +354,14 @@ github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsF
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
|
||||
github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g=
|
||||
github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8=
|
||||
github.com/securego/gosec/v2 v2.22.8 h1:3NMpmfXO8wAVFZPNsd3EscOTa32Jyo6FLLlW53bexMI=
|
||||
github.com/securego/gosec/v2 v2.22.8/go.mod h1:ZAw8K2ikuH9qDlfdV87JmNghnVfKB1XC7+TVzk6Utto=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
@@ -354,21 +370,22 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
|
||||
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
|
||||
github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM=
|
||||
github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c=
|
||||
github.com/sonatard/noctx v0.4.0 h1:7MC/5Gg4SQ4lhLYR6mvOP6mQVSxCrdyiExo7atBs27o=
|
||||
github.com/sonatard/noctx v0.4.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas=
|
||||
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/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
|
||||
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
@@ -376,35 +393,27 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8=
|
||||
github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
|
||||
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
|
||||
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
|
||||
github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw=
|
||||
github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
|
||||
github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg=
|
||||
github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU=
|
||||
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk=
|
||||
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
|
||||
github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg=
|
||||
github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.10.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
|
||||
github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M=
|
||||
github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.11.0 h1:BJSt36snX9+4WTIXeJ7nvHBQBcm1h2SjQMSlmQ6aFSU=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.11.0/go.mod h1:wFL9pDWDAbXhhPZZt+nG8Fu+h29TtnZ2MW6Lx4BRXIU=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
|
||||
github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI=
|
||||
@@ -413,8 +422,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW
|
||||
github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
|
||||
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
|
||||
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
|
||||
github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U=
|
||||
github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg=
|
||||
github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU=
|
||||
github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
|
||||
github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM=
|
||||
github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
@@ -435,10 +444,14 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
|
||||
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
|
||||
go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
|
||||
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
|
||||
go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE=
|
||||
go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM=
|
||||
go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE=
|
||||
go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww=
|
||||
go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo=
|
||||
go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE=
|
||||
go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s=
|
||||
go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ=
|
||||
go.augendre.info/arangolint v0.2.0 h1:2NP/XudpPmfBhQKX4rMk+zDYIj//qbt4hfZmSSTcpj8=
|
||||
go.augendre.info/arangolint v0.2.0/go.mod h1:Vx4KSJwu48tkE+8uxuf0cbBnAPgnt8O1KWiT7bljq7w=
|
||||
go.augendre.info/fatcontext v0.8.1 h1:/T4+cCjpL9g71gJpcFAgVo/K5VFpqlN+NPU7QXxD5+A=
|
||||
go.augendre.info/fatcontext v0.8.1/go.mod h1:r3Qz4ZOzex66wfyyj5VZ1xUcl81vzvHQ6/GWzzlMEwA=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -453,25 +466,23 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 h1:Yl4H5w2RV7L/dvSHp2GerziT5K2CORgFINPaMFxWGWw=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -481,14 +492,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -498,8 +507,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -515,19 +524,16 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
@@ -536,33 +542,29 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -574,14 +576,13 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
|
||||
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
|
||||
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
|
||||
mvdan.cc/gofumpt v0.9.1 h1:p5YT2NfFWsYyTieYgwcQ8aKV3xRvFH4uuN/zB2gBbMQ=
|
||||
mvdan.cc/gofumpt v0.9.1/go.mod h1:3xYtNemnKiXaTh6R4VtlqDATFwBbdXI8lJvH/4qk7mw=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module jb
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
|
||||
|
||||
@@ -15,6 +15,6 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
|
||||
)
|
||||
|
||||
@@ -54,8 +54,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module lefthook
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool github.com/evilmartians/lefthook
|
||||
|
||||
@@ -42,11 +42,10 @@ require (
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
@@ -88,17 +88,17 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 h1:8ajkpB4hXVftY5ko905id+dOnmorcS2CHNxxHLLDcFM=
|
||||
gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61/go.mod h1:IfMagxm39Ys4ybJrDb7W3Ob8RwxftP0Yy+or/NVz1O8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module swagger
|
||||
|
||||
go 1.24.4
|
||||
go 1.25.3
|
||||
|
||||
tool github.com/go-swagger/go-swagger/cmd/swagger
|
||||
|
||||
@@ -51,12 +51,12 @@ require (
|
||||
github.com/toqueteos/webbrowser v1.2.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.16.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -101,19 +101,19 @@ go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4
|
||||
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
10
.drone.yml
10
.drone.yml
@@ -197,11 +197,11 @@ steps:
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
- go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
|
||||
- dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
|
||||
-a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu
|
||||
-a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7
|
||||
-a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER
|
||||
--ubuntu-base=ubuntu-base --alpine-base=alpine-base --tag-format='{{ .version_base
|
||||
--ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base
|
||||
}}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID
|
||||
}}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt
|
||||
- find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i
|
||||
@@ -665,11 +665,11 @@ steps:
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
- go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
|
||||
- dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
|
||||
-a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu
|
||||
-a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7
|
||||
-a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER
|
||||
--ubuntu-base=ubuntu-base --alpine-base=alpine-base --tag-format='{{ .version_base
|
||||
--ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base
|
||||
}}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID
|
||||
}}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt
|
||||
- find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i
|
||||
@@ -3007,6 +3007,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: e7227aeb1bbea13606266ce540b5f0e0a63f05f56a3eb072954d54527dcc5a11
|
||||
hmac: dcbbfb33fcbd1546e1bf3346f64fe0fd1d7c8b18bc148a93b45389273e90f6d2
|
||||
|
||||
...
|
||||
|
||||
21
.github/CODEOWNERS
vendored
21
.github/CODEOWNERS
vendored
@@ -70,7 +70,6 @@
|
||||
/scripts/go-workspace @grafana/grafana-app-platform-squad
|
||||
/scripts/ci/backend-tests @grafana/grafana-operator-experience-squad
|
||||
/hack/ @grafana/grafana-app-platform-squad
|
||||
/.air.toml @macabu
|
||||
|
||||
/pkg/apis/provisioning @grafana/grafana-git-ui-sync-team
|
||||
/public/app/features/provisioning @grafana/grafana-git-ui-sync-team
|
||||
@@ -82,7 +81,6 @@
|
||||
/apps/playlist/ @grafana/grafana-app-platform-squad
|
||||
/apps/investigations/ @fcjack @matryer @svennergr
|
||||
/apps/advisor/ @grafana/plugins-platform-backend
|
||||
/apps/iam/ @grafana/access-squad
|
||||
/pkg/api/ @grafana/grafana-backend-group
|
||||
/pkg/apis/ @grafana/grafana-app-platform-squad
|
||||
/pkg/apis/query @grafana/grafana-datasources-core-services
|
||||
@@ -144,7 +142,6 @@
|
||||
/pkg/services/dashboardversion/ @grafana/grafana-backend-group
|
||||
/pkg/services/encryption/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/folder/ @grafana/grafana-search-and-storage
|
||||
/pkg/services/frontend/ @grafana/grafana-frontend-platform
|
||||
/pkg/services/apiserver @grafana/grafana-app-platform-squad
|
||||
/pkg/services/hooks/ @grafana/grafana-backend-group
|
||||
/pkg/services/kmsproviders/ @grafana/grafana-operator-experience-squad
|
||||
@@ -171,7 +168,6 @@
|
||||
/pkg/services/tag/ @grafana/grafana-search-and-storage
|
||||
/pkg/services/team/ @grafana/access-squad
|
||||
/pkg/services/temp_user/ @grafana/grafana-backend-group
|
||||
/pkg/services/updatemanager/ @grafana/grafana-backend-group
|
||||
/pkg/services/user/ @grafana/access-squad
|
||||
/pkg/services/validations/ @grafana/grafana-backend-group
|
||||
/pkg/setting/ @grafana/grafana-backend-services-squad
|
||||
@@ -265,7 +261,6 @@
|
||||
/devenv/docker/blocks/influxdb/ @grafana/partner-datasources
|
||||
/devenv/docker/blocks/influxdb1/ @grafana/partner-datasources
|
||||
/devenv/docker/blocks/jaeger/ @grafana/oss-big-tent
|
||||
/devenv/docker/blocks/jaegeronly/ @grafana/grafana-operator-experience-squad
|
||||
/devenv/docker/blocks/maildev/ @grafana/alerting-frontend
|
||||
/devenv/docker/blocks/mariadb/ @grafana/oss-big-tent
|
||||
/devenv/docker/blocks/memcached/ @grafana/grafana-backend-group
|
||||
@@ -400,8 +395,6 @@
|
||||
/public/app/core/internationalization/ @grafana/grafana-frontend-platform
|
||||
/e2e/ @grafana/grafana-frontend-platform
|
||||
/e2e/cloud-plugins-suite/ @grafana/partner-datasources
|
||||
/e2e/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
|
||||
/e2e/test-plugins/grafana-extensionstest-app/ @grafana/plugins-platform-frontend
|
||||
|
||||
# Packages
|
||||
/packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
@@ -442,9 +435,6 @@
|
||||
/packages/grafana-ui/src/graveyard/GraphNG/ @grafana/dataviz-squad
|
||||
/packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad
|
||||
/packages/grafana-ui/src/utils/storybook/ @grafana/grafana-frontend-platform
|
||||
/packages/grafana-alerting/ @grafana/alerting-frontend
|
||||
/packages/grafana-i18n/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
/packages/grafana-test-utils @grafana/grafana-frontend-platform
|
||||
|
||||
# root files, mostly frontend
|
||||
/.browserslistrc @grafana/frontend-ops
|
||||
@@ -488,7 +478,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/core/components/TimelineChart/ @grafana/dataviz-squad
|
||||
/public/app/core/components/Form/ @grafana/grafana-frontend-platform
|
||||
/public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad
|
||||
/public/app/dev-utils.ts @grafana/grafana-frontend-platform
|
||||
|
||||
/public/app/core/history/ @grafana/observability-traces-and-profiling
|
||||
/public/app/features/admin/ @grafana/identity-access-team
|
||||
@@ -609,7 +598,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/dev.ts @grafana/frontend-ops
|
||||
/public/app/core/utils/metrics.ts @grafana/plugins-platform-frontend
|
||||
/public/app/index.ts @grafana/frontend-ops
|
||||
/public/app/initApp.ts @grafana/frontend-ops
|
||||
/public/app/AppWrapper.tsx @grafana/frontend-ops
|
||||
/public/app/partials/ @grafana/grafana-frontend-platform
|
||||
|
||||
@@ -642,7 +630,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/scripts/levitate-parse-json-report.js @grafana/plugins-platform-frontend
|
||||
/scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend
|
||||
/scripts/codemods/explicit-barrel-imports.cjs @grafana/frontend-ops
|
||||
/scripts/rtk-client-generator/ @grafana/grafana-search-navigate-organise
|
||||
|
||||
/scripts/**/generate-transformations* @grafana/datapro
|
||||
/scripts/webpack/ @grafana/frontend-ops
|
||||
@@ -710,7 +697,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/pkg/services/anonymous/ @grafana/identity-access-team
|
||||
/pkg/services/auth/ @grafana/identity-squad
|
||||
/pkg/services/authn/ @grafana/identity-squad
|
||||
/pkg/services/scimutil/ @grafana/identity-squad
|
||||
/pkg/services/authz/ @grafana/access-squad
|
||||
/pkg/services/signingkeys/ @grafana/identity-squad
|
||||
/pkg/services/dashboards/accesscontrol.go @grafana/access-squad
|
||||
@@ -747,11 +733,11 @@ embed.go @grafana/grafana-as-code
|
||||
/pkg/kinds/ @grafana/grafana-as-code
|
||||
/pkg/registry/ @grafana/grafana-as-code
|
||||
/pkg/registry/apis/ @grafana/grafana-app-platform-squad
|
||||
/pkg/registry/apis/folders @grafana/grafana-search-and-storage
|
||||
/pkg/registry/apis/query @grafana/grafana-datasources-core-services
|
||||
/pkg/registry/apis/secret @grafana/grafana-operator-experience-squad
|
||||
/pkg/registry/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend
|
||||
/pkg/registry/apps/advisor @grafana/plugins-platform-backend
|
||||
/pkg/registry/apps/alerting @grafana/alerting-backend
|
||||
/pkg/codegen/ @grafana/grafana-as-code
|
||||
/pkg/codegen/generators @grafana/grafana-as-code
|
||||
/pkg/kinds/*/*_gen.go @grafana/grafana-as-code
|
||||
@@ -770,6 +756,7 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/pr-checks.json @tolzhabayev
|
||||
/.github/pr-commands.json @tolzhabayev
|
||||
/.github/renovate.json5 @grafana/frontend-ops
|
||||
/.github/actions/check-jobs/action.yml @grafana/grafana-frontend-platform
|
||||
/.github/actions/setup-enterprise/action.yml @grafana/grafana-backend-group
|
||||
/.github/actions/setup-grafana-bench/ @Proximyst
|
||||
/.github/actions/build-package @grafana/grafana-developer-enablement-squad
|
||||
@@ -817,6 +804,7 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend
|
||||
/.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/storybook-verification.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/storybook-verification-playwright.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/update-make-docs.yml @grafana/docs-tooling
|
||||
/.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring
|
||||
/.github/workflows/scripts/create-security-branch/create-security-branch.sh @grafana/grafana-developer-enablement-squad
|
||||
@@ -876,3 +864,6 @@ embed.go @grafana/grafana-as-code
|
||||
/conf/provisioning/datasources/ @grafana/plugins-platform-backend
|
||||
/conf/provisioning/plugins/ @grafana/plugins-platform-backend
|
||||
/conf/provisioning/sample/ @grafana/grafana-git-ui-sync-team
|
||||
|
||||
# Security
|
||||
/.github/workflows/relyance-scan.yml @grafana/security-team
|
||||
|
||||
48
.github/actions/check-jobs/action.yml
vendored
Normal file
48
.github/actions/check-jobs/action.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Check jobs results
|
||||
description: Checks if any jobs have failed and exits with error if failures are found. Use to check the results of matrix test runs.
|
||||
inputs:
|
||||
needs:
|
||||
description: JSON string containing the needs context from the workflow
|
||||
required: true
|
||||
failure-message:
|
||||
description: Custom message to display when failures are found
|
||||
required: false
|
||||
default: "One or more jobs have failed"
|
||||
success-message:
|
||||
description: Custom message to display when all jobs pass
|
||||
required: false
|
||||
default: "All jobs passed successfully"
|
||||
outputs:
|
||||
any-failed:
|
||||
description: Whether any jobs failed
|
||||
value: ${{ steps.check-jobs.outputs.any-failed }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Check test suites
|
||||
id: check-jobs
|
||||
shell: bash
|
||||
env:
|
||||
NEEDS: ${{ inputs.needs }}
|
||||
FAILURE_MSG: ${{ inputs.failure-message }}
|
||||
SUCCESS_MSG: ${{ inputs.success-message }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Print the needs context, debugging
|
||||
echo "$NEEDS" | jq
|
||||
|
||||
# Extract failures
|
||||
FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')"
|
||||
|
||||
# Check if there are any failures
|
||||
if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then
|
||||
echo "❌ $FAILURE_MSG"
|
||||
echo "Failed suites:"
|
||||
echo "$FAILURES" | jq -r 'to_entries[] | "- \(.key): \(.value)"'
|
||||
echo "any-failed=true" >> "$GITHUB_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ $SUCCESS_MSG"
|
||||
11
.github/actions/setup-node/action.yml
vendored
Normal file
11
.github/actions/setup-node/action.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: Setup Node.js
|
||||
description: Sets up a node.js environment with presets for the Grafana repository.
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
1
.github/dependabot.yml
vendored
1
.github/dependabot.yml
vendored
@@ -8,6 +8,7 @@ updates:
|
||||
directories:
|
||||
- "/"
|
||||
- "/apps/playlist"
|
||||
- "/apps/secret"
|
||||
- "/apps/investigations"
|
||||
- "/pkg/aggregator"
|
||||
- "/pkg/apimachinery"
|
||||
|
||||
2
.github/workflows/actionlint.yml
vendored
2
.github/workflows/actionlint.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
- release-*.*.*
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
|
||||
1
.github/workflows/backend-code-checks.yml
vendored
1
.github/workflows/backend-code-checks.yml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*.*.*
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- 'docs/**'
|
||||
|
||||
2
.github/workflows/codeowners-validator.yml
vendored
2
.github/workflows/codeowners-validator.yml
vendored
@@ -2,7 +2,7 @@ name: "Codeowners Validator"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
branches: [ main, release-* ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
|
||||
2
.github/workflows/documentation-ci.yml
vendored
2
.github/workflows/documentation-ci.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Documentation CI
|
||||
on:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: ["main", "release-*"]
|
||||
paths: ["docs/sources/**"]
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
5
.github/workflows/frontend-lint.yml
vendored
5
.github/workflows/frontend-lint.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
contents: read
|
||||
outputs:
|
||||
changed: ${{ steps.detect-changes.outputs.frontend }}
|
||||
prettier: ${{ steps.detect-changes.outputs.frontend == 'true' || steps.detect-changes.outputs.docs == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -34,7 +35,7 @@ jobs:
|
||||
id-token: write
|
||||
# Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`,
|
||||
# the `lint-frontend-prettier-enterprise` workflow will run instead
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.changed == 'true'
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.prettier == 'true'
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -55,7 +56,7 @@ jobs:
|
||||
contents: read
|
||||
id-token: write
|
||||
# Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks)
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.changed == 'true'
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.prettier == 'true'
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
3
.github/workflows/go-lint.yml
vendored
3
.github/workflows/go-lint.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
- go.*
|
||||
branches:
|
||||
- main
|
||||
- release-*.*.*
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
@@ -25,7 +26,7 @@ jobs:
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd
|
||||
with:
|
||||
version: v2.0.2
|
||||
version: v2.5.0
|
||||
args: |
|
||||
--verbose $(go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}/...')
|
||||
install-mode: binary
|
||||
|
||||
@@ -3,7 +3,7 @@ name: "CodeQL for PR / javascript"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, release-*]
|
||||
paths:
|
||||
- '**/*.js'
|
||||
- '**/*.ts'
|
||||
|
||||
@@ -3,7 +3,7 @@ name: "CodeQL for PR / python"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, release-*]
|
||||
paths:
|
||||
- '**/*.py'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: "Update Go Workspace for Dependabot PRs"
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, release-*]
|
||||
paths:
|
||||
- .github/workflows/pr-dependabot-update-go-workspace.yml
|
||||
- go.mod
|
||||
|
||||
8
.github/workflows/pr-e2e-tests.yml
vendored
8
.github/workflows/pr-e2e-tests.yml
vendored
@@ -111,16 +111,16 @@ jobs:
|
||||
path: e2e/panels-suite
|
||||
- suite: various-suite (old arch)
|
||||
path: e2e/old-arch/various-suite
|
||||
flags: --flags="--env dashboardScene=false"
|
||||
flags: --flags="--env DISABLE_SCENES=true"
|
||||
- suite: dashboards-suite (old arch)
|
||||
path: e2e/old-arch/dashboards-suite
|
||||
flags: --flags="--env dashboardScene=false"
|
||||
flags: --flags="--env DISABLE_SCENES=true"
|
||||
- suite: smoke-tests-suite (old arch)
|
||||
path: e2e/old-arch/smoke-tests-suite
|
||||
flags: --flags="--env dashboardScene=false"
|
||||
flags: --flags="--env DISABLE_SCENES=true"
|
||||
- suite: panels-suite (old arch)
|
||||
path: e2e/old-arch/panels-suite
|
||||
flags: --flags="--env dashboardScene=false"
|
||||
flags: --flags="--env DISABLE_SCENES=true"
|
||||
name: ${{ matrix.suite }}
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
permissions:
|
||||
|
||||
2
.github/workflows/pr-go-workspace-check.yml
vendored
2
.github/workflows/pr-go-workspace-check.yml
vendored
@@ -3,7 +3,7 @@ name: "Go Workspace Check"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, release-*]
|
||||
paths:
|
||||
- .github/workflows/pr-go-workspace-check.yml
|
||||
- go.mod
|
||||
|
||||
2
.github/workflows/pr-k8s-codegen-check.yml
vendored
2
.github/workflows/pr-k8s-codegen-check.yml
vendored
@@ -3,7 +3,7 @@ name: "K8s Codegen Check"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, release-*]
|
||||
paths:
|
||||
- "pkg/apis/**"
|
||||
- "pkg/aggregator/apis/**"
|
||||
|
||||
4
.github/workflows/reject-gh-secrets.yml
vendored
4
.github/workflows/reject-gh-secrets.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
- release-*.*.*
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -28,4 +28,4 @@ jobs:
|
||||
echo "Found secrets access in the codebase. Please remove it in favour of Vault secrets."
|
||||
echo "If you are sure this is correct, add '# nolint:reject-gh-secrets' to the end of the line. Be VERY careful with this."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
29
.github/workflows/release-build.yml
vendored
29
.github/workflows/release-build.yml
vendored
@@ -271,21 +271,30 @@ jobs:
|
||||
docker manifest push "grafana/grafana-dev:${VERSION}"
|
||||
docker manifest push "grafana/grafana-dev:${VERSION}-ubuntu"
|
||||
|
||||
publish-npm-canaries:
|
||||
dispatch-npm-canaries:
|
||||
if: github.ref_name == 'main'
|
||||
name: Publish NPM canaries
|
||||
uses: ./.github/workflows/release-npm.yml
|
||||
name: Dispatch publish NPM canaries
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
id-token: write
|
||||
runs-on: ubuntu-x64-small
|
||||
needs:
|
||||
- setup
|
||||
- build
|
||||
with:
|
||||
grafana_commit: ${{ needs.setup.outputs.grafana-commit }}
|
||||
version: ${{ needs.setup.outputs.version }}
|
||||
build_id: ${{ github.run_id }}
|
||||
version_type: "canary"
|
||||
steps:
|
||||
- name: Dispatch action
|
||||
env:
|
||||
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
|
||||
VERSION: ${{ needs.setup.outputs.version }}
|
||||
BUILD_ID: ${{ github.run_id }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh workflow run release-npm.yml \
|
||||
--repo grafana/grafana \
|
||||
--ref main \
|
||||
--field grafana_commit="$GRAFANA_COMMIT" \
|
||||
--field version="$VERSION" \
|
||||
--field build_id="$BUILD_ID"\
|
||||
--field version_type="canary"
|
||||
|
||||
# notify-pr creates (or updates) a comment in a pull request to link to this workflow where the release artifacts are
|
||||
# being built.
|
||||
|
||||
31
.github/workflows/release-npm.yml
vendored
31
.github/workflows/release-npm.yml
vendored
@@ -39,12 +39,14 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
# If called with version_type 'canary' or 'stable', build + publish to NPM
|
||||
# If called with version_type 'nightly', just tag the given version with nightly tag. It was already published by the canary build.
|
||||
# If called with version_type 'nightly', do nothing (we're not yet tagging them with the nightly tag)
|
||||
|
||||
publish:
|
||||
name: Publish NPM packages
|
||||
runs-on: github-hosted-ubuntu-x64-small
|
||||
if: inputs.version_type == 'canary' || inputs.version_type == 'stable'
|
||||
# Required for this workflow to have permission to publish NPM packages
|
||||
environment: npm-publish
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -89,11 +91,8 @@ jobs:
|
||||
persist-credentials: false
|
||||
ref: ${{ inputs.grafana_commit }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
# Trusted Publishing is only available in npm v11.5.1 and later
|
||||
- name: Update npm
|
||||
@@ -121,26 +120,10 @@ jobs:
|
||||
- name: Debug packed files
|
||||
run: tree -a ./npm-artifacts
|
||||
|
||||
- name: Debug OIDC Claims
|
||||
uses: github/actions-oidc-debugger@2e9ba5d3f4bebaad1f91a2cede055115738b7ae8
|
||||
with:
|
||||
audience: '${{ github.server_url }}/${{ github.repository_owner }}'
|
||||
- name: Validate packages
|
||||
run: ./scripts/validate-npm-packages.sh
|
||||
|
||||
- name: Publish packages
|
||||
env:
|
||||
NPM_TAG: ${{ steps.npm-tag.outputs.NPM_TAG }}
|
||||
run: ./scripts/publish-npm-packages.sh --dist-tag "$NPM_TAG" --registry 'https://registry.npmjs.org/'
|
||||
|
||||
# TODO: finish this step
|
||||
tag-nightly:
|
||||
name: Tag nightly release
|
||||
runs-on: github-hosted-ubuntu-x64-small
|
||||
if: inputs.version_type == 'nightly'
|
||||
|
||||
steps:
|
||||
- name: Checkout workflow ref
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# TODO: tag the given release with nightly
|
||||
|
||||
33
.github/workflows/relyance-scan.yml
vendored
Normal file
33
.github/workflows/relyance-scan.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Relyance Compliance Inspection
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Run daily at 00:00 UTC
|
||||
workflow_dispatch: # Allow for manual trigger
|
||||
|
||||
jobs:
|
||||
relyance-compliance-inspector:
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Needed for Vault access
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get API key
|
||||
id: vault-secrets
|
||||
uses: grafana/shared-workflows/actions/get-vault-secrets@5d7e361bc7e0a183cde8afe9899fb7b596d2659b # get-vault-secrets-v1.2.0
|
||||
with:
|
||||
repo_secrets: |
|
||||
API_KEY=relyance:API_KEY
|
||||
|
||||
- name: Run Relyance scan
|
||||
env:
|
||||
API_KEY: "${{ fromJSON(steps.vault-secrets.outputs.secrets).API_KEY }}"
|
||||
run: |
|
||||
docker pull gcr.io/relyance-ext/compliance_inspector:release && \
|
||||
docker run --rm -v ${{ github.workspace }}:/repo --env "API_KEY=${{ env.API_KEY }}" gcr.io/relyance-ext/compliance_inspector:release
|
||||
1
.github/workflows/run-schema-v2-e2e.yml
vendored
1
.github/workflows/run-schema-v2-e2e.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*.*.*
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
2
.github/workflows/shellcheck.yml
vendored
2
.github/workflows/shellcheck.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
- release-*.*.*
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
|
||||
2
.github/workflows/swagger-gen.yml
vendored
2
.github/workflows/swagger-gen.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
- release-*.*.*
|
||||
pull_request:
|
||||
|
||||
permissions: {}
|
||||
|
||||
1
.github/workflows/trivy-scan.yml
vendored
1
.github/workflows/trivy-scan.yml
vendored
@@ -8,6 +8,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*.*.*
|
||||
paths:
|
||||
- go.*
|
||||
- .github/workflows/trivy-scan.yml
|
||||
|
||||
2
.github/workflows/verify-kinds.yml
vendored
2
.github/workflows/verify-kinds.yml
vendored
@@ -2,7 +2,7 @@ name: "verify-kinds"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
branches: [ main, release-* ]
|
||||
paths:
|
||||
- '**/*.cue'
|
||||
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -41,6 +41,7 @@ __debug_bin*
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
/devenv/docker/blocks/auth/saml-enterprise
|
||||
/devenv/docker/blocks/auth/signer
|
||||
/devenv/docker/blocks/spanner_tests
|
||||
/devenv/docker/blocks/mt-db
|
||||
|
||||
/tmp
|
||||
@@ -69,7 +70,6 @@ public/css/*.min.css
|
||||
!.vscode/launch.json
|
||||
.vs/
|
||||
.cursor/
|
||||
.devcontainer/
|
||||
|
||||
.eslintcache
|
||||
.stylelintcache
|
||||
@@ -115,7 +115,6 @@ profile.cov
|
||||
# Extensions
|
||||
/pkg/cmd/grafana-cli/runner/wireexts_enterprise.go
|
||||
/pkg/server/wireexts_enterprise.go
|
||||
/pkg/server/enterprise_wire_gen.go
|
||||
/pkg/build/cmd/enterprise.go
|
||||
/pkg/extensions/*
|
||||
!/pkg/extensions/.keep
|
||||
@@ -205,6 +204,9 @@ compilation-stats.json
|
||||
# auto generated frontend docs
|
||||
/docs/sources/packages_api
|
||||
|
||||
# wire generated files
|
||||
/pkg/server/enterprise_wire_gen.go
|
||||
|
||||
# Auto-generated internationalization files
|
||||
public/locales/_build/
|
||||
public/locales/*/*.js
|
||||
|
||||
@@ -101,12 +101,6 @@ linters:
|
||||
- '**/pkg/tsdb/tempo/**/*'
|
||||
- '**/pkg/tsdb/cloudwatch/*'
|
||||
- '**/pkg/tsdb/cloudwatch/**/*'
|
||||
- '**/pkg/tsdb/loki/*'
|
||||
- '**/pkg/tsdb/loki/**/*'
|
||||
- '**/pkg/tsdb/zipkin/*'
|
||||
- '**/pkg/tsdb/zipkin/**/*'
|
||||
- '**/pkg/tsdb/jaeger/*'
|
||||
- '**/pkg/tsdb/jaeger/**/*'
|
||||
deny:
|
||||
- pkg: github.com/grafana/grafana/pkg/api
|
||||
desc: Core plugins are not allowed to depend on Grafana core packages
|
||||
@@ -299,7 +293,6 @@ linters:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
- pkg/util/xorm
|
||||
issues:
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
|
||||
1
.ignore
1
.ignore
@@ -19,3 +19,4 @@
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
!/devenv/docker/blocks/auth/saml-enterprise
|
||||
!/devenv/docker/blocks/auth/signer
|
||||
!/devenv/docker/blocks/spanner_tests
|
||||
166
.pa11yci-pr.conf.js
Normal file
166
.pa11yci-pr.conf.js
Normal file
@@ -0,0 +1,166 @@
|
||||
var dashboardSettings = [
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=templating',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
// TODO: improve the accessibility of the permission tab https://github.com/grafana/grafana/issues/77203
|
||||
threshold: 5,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 2,
|
||||
},
|
||||
];
|
||||
|
||||
var config = {
|
||||
defaults: {
|
||||
concurrency: 1,
|
||||
runners: ['axe'],
|
||||
useIncognitoBrowserContext: false,
|
||||
standard: 'WCAG2AA',
|
||||
chromeLaunchConfig: {
|
||||
executablePath: '/usr/bin/google-chrome',
|
||||
args: ['--no-sandbox'],
|
||||
},
|
||||
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
|
||||
// on why we're ignoring singleValue/react-select-*-placeholder elements
|
||||
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
|
||||
},
|
||||
|
||||
urls: [
|
||||
{
|
||||
url: '${HOST}/login',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/login',
|
||||
wait: 500,
|
||||
actions: [
|
||||
"wait for element input[name='user'] to be added",
|
||||
"set field input[name='user'] to admin",
|
||||
"set field input[name='password'] to admin",
|
||||
"click element button[data-testid='data-testid Login button']",
|
||||
"wait for element button[data-testid='data-testid Skip change password button'] to be visible",
|
||||
],
|
||||
threshold: 2,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/?orgId=1',
|
||||
wait: 500,
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
...dashboardSettings,
|
||||
{
|
||||
url: '${HOST}/?orgId=1&search=open',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/alerting/list',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
// the unified alerting promotion alert's content contrast is too low
|
||||
// see https://github.com/grafana/grafana/pull/41829
|
||||
threshold: 7,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/datasources',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/users',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/teams',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/plugins',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 2,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/apikeys',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 4,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/dashboards',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 2,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function myPa11yCiConfiguration(urls, defaults) {
|
||||
const HOST_SERVER = process.env.HOST || 'localhost';
|
||||
const PORT_SERVER = process.env.PORT || '3001';
|
||||
for (var idx = 0; idx < urls.length; idx++) {
|
||||
urls[idx] = { ...urls[idx], url: urls[idx].url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`) };
|
||||
}
|
||||
|
||||
return {
|
||||
defaults: defaults,
|
||||
urls: urls,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = myPa11yCiConfiguration(config.urls, config.defaults);
|
||||
140
.pa11yci.conf.js
Normal file
140
.pa11yci.conf.js
Normal file
@@ -0,0 +1,140 @@
|
||||
var dashboardSettings = [
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=templating',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
];
|
||||
var config = {
|
||||
defaults: {
|
||||
concurrency: 1,
|
||||
runners: ['axe'],
|
||||
useIncognitoBrowserContext: false,
|
||||
standard: 'WCAG2AA',
|
||||
chromeLaunchConfig: {
|
||||
args: ['--no-sandbox'],
|
||||
},
|
||||
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
|
||||
// on why we're ignoring singleValue/react-select-*-placeholder elements
|
||||
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
|
||||
},
|
||||
|
||||
urls: [
|
||||
{
|
||||
url: '${HOST}/login',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/login', //skip password and login
|
||||
actions: [
|
||||
"wait for element input[name='user'] to be added",
|
||||
"set field input[name='user'] to admin",
|
||||
"set field input[name='password'] to admin",
|
||||
"click element button[data-testid='data-testid Login button']",
|
||||
"wait for element button[data-testid='data-testid Skip change password button'] to be visible",
|
||||
],
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/?orgId=1',
|
||||
wait: 500,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
...dashboardSettings,
|
||||
{
|
||||
url: '${HOST}/?orgId=1&search=open',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/alerting/list',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/datasources',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/users',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/teams',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/plugins',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/org/apikeys',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
{
|
||||
url: '${HOST}/dashboards',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function myPa11yCiConfiguration(urls, defaults) {
|
||||
const HOST_SERVER = process.env.HOST || 'localhost';
|
||||
const PORT_SERVER = process.env.PORT || '3001';
|
||||
for (var idx = 0; idx < urls.length; idx++) {
|
||||
urls[idx] = { ...urls[idx], url: urls[idx].url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`) };
|
||||
}
|
||||
return {
|
||||
defaults: defaults,
|
||||
urls: urls,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = myPa11yCiConfiguration(config.urls, config.defaults);
|
||||
@@ -35,8 +35,7 @@ public/openapi3.json
|
||||
public/mockServiceWorker.js
|
||||
|
||||
# Crowdin files
|
||||
# Ignore `locales` directory so we also catch enterprise files
|
||||
**/locales/**/*.json
|
||||
public/locales/**/*.json
|
||||
|
||||
/.nx/cache
|
||||
/.nx/workspace-data
|
||||
/.nx/workspace-data
|
||||
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@@ -82,16 +82,6 @@
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": ["server", "target", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
|
||||
},
|
||||
{
|
||||
"name": "Run Frontend Server",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/pkg/cmd/grafana/",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": { "GF_DEFAULT_TARGET": "frontend-server", "GF_SERVER_HTTP_PORT": "3003" },
|
||||
"args": ["server", "target", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
|
||||
},
|
||||
{
|
||||
"name": "Attach to Chrome",
|
||||
"port": 9222,
|
||||
|
||||
49
.yarn/plugins/@yarnpkg/plugin-licenses.cjs
vendored
49
.yarn/plugins/@yarnpkg/plugin-licenses.cjs
vendored
File diff suppressed because one or more lines are too long
934
.yarn/releases/yarn-4.6.0.cjs
vendored
Executable file
934
.yarn/releases/yarn-4.6.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
942
.yarn/releases/yarn-4.9.2.cjs
vendored
942
.yarn/releases/yarn-4.9.2.cjs
vendored
File diff suppressed because one or more lines are too long
16
.yarnrc.yml
16
.yarnrc.yml
@@ -7,7 +7,7 @@ enableTelemetry: false
|
||||
nodeLinker: node-modules
|
||||
|
||||
packageExtensions:
|
||||
croact-css-styled@1.1.9:
|
||||
'croact-css-styled@1.1.9':
|
||||
dependencies:
|
||||
croact: 1.0.4
|
||||
doctrine@3.0.0:
|
||||
@@ -20,9 +20,13 @@ packageExtensions:
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs
|
||||
spec: "https://mskelton.dev/yarn-outdated/v2"
|
||||
- checksum: 374f735053958b77583ef7b70e56a59540d1d9c681f04b8ab7a600f4325301d2e1fb67c51d34ba75bbbc4e71f8c003fbc52b1f22d7daa30dca655f1a1bf205a1
|
||||
path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs
|
||||
spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.15.0/bundles/@yarnpkg/plugin-licenses.js"
|
||||
spec: 'https://mskelton.dev/yarn-outdated/v2'
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.9.2.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.6.0.cjs
|
||||
# Uncomment the following lines if you want to use Verdaccio local npm registry. Read more at packages/README.md
|
||||
#npmScopes:
|
||||
# grafana:
|
||||
# npmRegistryServer: http://localhost:4873
|
||||
#
|
||||
#unsafeHttpWhitelist:
|
||||
# - 'localhost'
|
||||
|
||||
875
CHANGELOG.md
875
CHANGELOG.md
@@ -1,220 +1,66 @@
|
||||
<!-- 12.1.2 START -->
|
||||
<!-- 12.0.6 START -->
|
||||
|
||||
# 12.1.2 (2025-09-23)
|
||||
# 12.0.6 (2025-10-21)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** Update alerting module [#109999](https://github.com/grafana/grafana/pull/109999), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Go:** Update to 1.25.2 + golangci-lint v2.5.0 + golang.org/x/net v0.45.0 [#112161](https://github.com/grafana/grafana/pull/112161), [@macabu](https://github.com/macabu)
|
||||
- **Go:** Update to 1.25.3 [#112364](https://github.com/grafana/grafana/pull/112364), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Auth:** Fix render user OAuth passthrough [#112096](https://github.com/grafana/grafana/pull/112096), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **FlameGraph:** Ensure total is only counted once for recursive function calls [#111604](https://github.com/grafana/grafana/pull/111604), [@simonswine](https://github.com/simonswine)
|
||||
- **LDAP Authentication:** Fix URL to propagate username context as parameter [#111847](https://github.com/grafana/grafana/pull/111847), [@bradleypettit](https://github.com/bradleypettit)
|
||||
- **Plugins:** Dependencies do not inherit parent URL for preinstall [#111766](https://github.com/grafana/grafana/pull/111766), [@wbrowne](https://github.com/wbrowne)
|
||||
|
||||
<!-- 12.0.6 END -->
|
||||
<!-- 12.0.5 START -->
|
||||
|
||||
# 12.0.5 (2025-09-23)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** Update alerting module [#110000](https://github.com/grafana/grafana/pull/110000), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
|
||||
- **Auditing:** Document new options for recording datasource query request/response body [#109981](https://github.com/grafana/grafana/pull/109981), [@macabu](https://github.com/macabu)
|
||||
- **Chore:** Don't show a "Not found" for public-dashboard fetches if the service is disabled via config [#110144](https://github.com/grafana/grafana/pull/110144), [@mmandrus](https://github.com/mmandrus)
|
||||
- **CloudWatch:** Use default region when query region is unset [#111079](https://github.com/grafana/grafana/pull/111079), [@iwysiu](https://github.com/iwysiu)
|
||||
- **Auditing:** Document new options for recording datasource query request/response body [#109980](https://github.com/grafana/grafana/pull/109980), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix bug where rules with identical mute/active intervals produced conflicting routes [#110973](https://github.com/grafana/grafana/pull/110973), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Fix copying of recording rule fields [#110312](https://github.com/grafana/grafana/pull/110312), [@moustafab](https://github.com/moustafab)
|
||||
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111097](https://github.com/grafana/grafana/pull/111097), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Alerting:** Fix copying of recording rule fields [#110346](https://github.com/grafana/grafana/pull/110346), [@moustafab](https://github.com/moustafab)
|
||||
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111156](https://github.com/grafana/grafana/pull/111156), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
|
||||
### Plugin development fixes & changes
|
||||
|
||||
- **Fix:** Prevent Rollup from treeshaking NPM packages [#108570](https://github.com/grafana/grafana/pull/108570), [@jackw](https://github.com/jackw)
|
||||
- **Fix:** Prevent Rollup from treeshaking NPM packages [#110523](https://github.com/grafana/grafana/pull/110523), [@jackw](https://github.com/jackw)
|
||||
|
||||
<!-- 12.1.2 END -->
|
||||
<!-- 12.1.1 START -->
|
||||
<!-- 12.0.5 END -->
|
||||
<!-- 12.0.4 START -->
|
||||
|
||||
# 12.1.1 (2025-08-13)
|
||||
# 12.0.4 (2025-08-13)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** Add rule group name validation to the Prometheus conversion API [#108767](https://github.com/grafana/grafana/pull/108767), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **CloudWatch:** Update grafana/aws-sdk-go with STS endpo… [#109357](https://github.com/grafana/grafana/pull/109357), [@iwysiu](https://github.com/iwysiu)
|
||||
- **Go:** Update to 1.24.6 [#109318](https://github.com/grafana/grafana/pull/109318), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Update to 1.24.6 [#109317](https://github.com/grafana/grafana/pull/109317), [@Proximyst](https://github.com/Proximyst)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix active time intervals when time interval is renamed [#108547](https://github.com/grafana/grafana/pull/108547), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Fix subpath handling in the alerting package [#109505](https://github.com/grafana/grafana/pull/109505), [@konrad147](https://github.com/konrad147)
|
||||
- **Config:** Fix date_formats options being moved to a different section [#109366](https://github.com/grafana/grafana/pull/109366), [@joshhunt](https://github.com/joshhunt)
|
||||
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
|
||||
|
||||
<!-- 12.1.1 END -->
|
||||
<!-- 12.1.0 START -->
|
||||
<!-- 12.0.4 END -->
|
||||
<!-- 12.0.3 START -->
|
||||
|
||||
# 12.1.0 (2025-07-23)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Access:** Disable role none option if advanced access control is not enabled [#107378](https://github.com/grafana/grafana/pull/107378), [@Jguer](https://github.com/Jguer)
|
||||
- **Alerting:** Add OAuth2 Support for Webhook Receiver [#106302](https://github.com/grafana/grafana/pull/106302), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Alerting:** Add ability to import rules to GMA from Prometheus YAML [#105807](https://github.com/grafana/grafana/pull/105807), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add details to the payload when tracking import to GMA [#106404](https://github.com/grafana/grafana/pull/106404), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add export folder action to the new list view [#106256](https://github.com/grafana/grafana/pull/106256), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add filters for health and contact point in Prometheus Rules api [#106580](https://github.com/grafana/grafana/pull/106580), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Add loading spinner for loading groups state [#106289](https://github.com/grafana/grafana/pull/106289), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add need more info for import ui datasource field [#106364](https://github.com/grafana/grafana/pull/106364), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add provenance to Prometheus API [#106596](https://github.com/grafana/grafana/pull/106596), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Add provenance to remote-ruler extension response (Enterprise)
|
||||
- **Alerting:** Add simplified routing metadata to the details tab [#106403](https://github.com/grafana/grafana/pull/106403), [@gillesdemey](https://github.com/gillesdemey)
|
||||
- **Alerting:** Add state history backend to write ALERTS metric [#104361](https://github.com/grafana/grafana/pull/104361), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Add support for Redis Sentinel for Alerting HA [#106322](https://github.com/grafana/grafana/pull/106322), [@vstpme](https://github.com/vstpme)
|
||||
- **Alerting:** Allow disabling recording rules write for a data source in the UI [#106664](https://github.com/grafana/grafana/pull/106664), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Correctly persist FiredAt in SyncRuleStatePersister [#106658](https://github.com/grafana/grafana/pull/106658), [@fayzal-g](https://github.com/fayzal-g)
|
||||
- **Alerting:** Ensure errors cleared when Alerting after error [#105246](https://github.com/grafana/grafana/pull/105246), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Evaluate all imported from Prometheus rules sequentially [#106295](https://github.com/grafana/grafana/pull/106295), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Extensible Settings module [#107831](https://github.com/grafana/grafana/pull/107831), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Filter out rules managed by integrations and add an info alert [#106602](https://github.com/grafana/grafana/pull/106602), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Filter out synthetic datasource-managed rules when importing to GMA [#106358](https://github.com/grafana/grafana/pull/106358), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** List V2 - Add labels popup [#107193](https://github.com/grafana/grafana/pull/107193), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** List V2 - Grouped view filters [#106400](https://github.com/grafana/grafana/pull/106400), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** List V2 - Use backend filters for GMA rules [#106897](https://github.com/grafana/grafana/pull/106897), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Make paginated rules endpoint strongly consistent (Enterprise)
|
||||
- **Alerting:** Optimize out unnecessary permission check for rule groups (Enterprise)
|
||||
- **Alerting:** Optimize prometheus api permission checks [#106299](https://github.com/grafana/grafana/pull/106299), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Optimize prometheus api permission checks (Enterprise)
|
||||
- **Alerting:** Persist alert instance FiredAt field [#105927](https://github.com/grafana/grafana/pull/105927), [@fayzal-g](https://github.com/fayzal-g)
|
||||
- **Alerting:** Remove ruler from alert list view2 [#106778](https://github.com/grafana/grafana/pull/106778), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Resend alerts for states that are missing in the eval results [#105965](https://github.com/grafana/grafana/pull/105965), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Send notifications immediately on Error|NoData -> Normal transitions [#106421](https://github.com/grafana/grafana/pull/106421), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Support PDC in Grafana-managed recording rules [#106677](https://github.com/grafana/grafana/pull/106677), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Use default_datasource_uid as the default target for recording rules in UI [#106415](https://github.com/grafana/grafana/pull/106415), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Annotations:** Use dashboard uids instead of dashboard ids [#106676](https://github.com/grafana/grafana/pull/106676), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **App Platform Provisioning:** Add experimental nanogit mode for Git Sync [#106763](https://github.com/grafana/grafana/pull/106763), [@MissingRoberto](https://github.com/MissingRoberto)
|
||||
- **Auth:** Add Azure/Entra workload identity support [#104807](https://github.com/grafana/grafana/pull/104807), [@mehighlow](https://github.com/mehighlow)
|
||||
- **Auth:** Enable improved session handling by default for OAuth and SAML [#107442](https://github.com/grafana/grafana/pull/107442), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Auth:** Enable ssoSettingsLDAP by default [#106310](https://github.com/grafana/grafana/pull/106310), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Auth:** Remove api key endpoints [#106019](https://github.com/grafana/grafana/pull/106019), [@dmihai](https://github.com/dmihai)
|
||||
- **Auth:** Remove code for authenticating API keys [#105998](https://github.com/grafana/grafana/pull/105998), [@dmihai](https://github.com/dmihai)
|
||||
- **Azure:** Support scope selection in Resource Graph queries [#105835](https://github.com/grafana/grafana/pull/105835), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Betterer:** Only allow singleton Storage use [#105310](https://github.com/grafana/grafana/pull/105310), [@tskarhed](https://github.com/tskarhed)
|
||||
- **Caching:** Remove memcached reconnect_interval setting (Enterprise)
|
||||
- **Chore:** Update k8s.io to v0.33.1 [#105307](https://github.com/grafana/grafana/pull/105307), [@ryantxu](https://github.com/ryantxu)
|
||||
- **Cloud Monitoring:** Add support for service account impersonation [#107022](https://github.com/grafana/grafana/pull/107022), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **CloudMigrations:** Add Mute Timings as dependency for Notification Policies [#106751](https://github.com/grafana/grafana/pull/106751), [@macabu](https://github.com/macabu)
|
||||
- **CloudWatch:** Backport aws-sdk-go-v2 update from external plugin [#107136](https://github.com/grafana/grafana/pull/107136), [@njvrzm](https://github.com/njvrzm)
|
||||
- **CloudWatch:** Improve instance attribute variable query editor [#105206](https://github.com/grafana/grafana/pull/105206), [@iwysiu](https://github.com/iwysiu)
|
||||
- **Cloudwatch:** Add missing AWS regions [#106304](https://github.com/grafana/grafana/pull/106304), [@chriscerie](https://github.com/chriscerie)
|
||||
- **Dashboard Provisioning:** Reduce db load [#106114](https://github.com/grafana/grafana/pull/106114), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Dashboard:** Add Alert icon in library panels [#107723](https://github.com/grafana/grafana/pull/107723), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Add server-configurable quick ranges for the time picker [#102254](https://github.com/grafana/grafana/pull/102254), [@chodges15](https://github.com/chodges15)
|
||||
- **Dashboard:** Formatting Currency - add new custom 'financial' currency format without abbreviations [#106604](https://github.com/grafana/grafana/pull/106604), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Library Panels - Add ability to search by folder name [#106997](https://github.com/grafana/grafana/pull/106997), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Schema V2 - Auto-transform V2 dashboards in V1Resource export mode [#105997](https://github.com/grafana/grafana/pull/105997), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Datasources:** Migrate to new sigv4 middleware (Enterprise)
|
||||
- **Datasources:** Update grafana-aws-sdk for new sigv4 middleware and aws-sdk-go v1 removal [#107522](https://github.com/grafana/grafana/pull/107522), [@njvrzm](https://github.com/njvrzm)
|
||||
- **DatePicker:** Add cursor not-allowed style and hover background color [#106451](https://github.com/grafana/grafana/pull/106451), [@ywzheng1](https://github.com/ywzheng1)
|
||||
- **Dependencies:** Bump Go to v1.24.4 [#106533](https://github.com/grafana/grafana/pull/106533), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/go-viper/mapstructure/v2 from 2.2.1 to 2.3.0 [#107379](https://github.com/grafana/grafana/pull/107379), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga to v1.8.13 to address CVE-2025-48371 [#106064](https://github.com/grafana/grafana/pull/106064), [@macabu](https://github.com/macabu)
|
||||
- **ElasticSearch:** Remove frontend response parsing [#104148](https://github.com/grafana/grafana/pull/104148), [@nojaf](https://github.com/nojaf)
|
||||
- **Geomap:** Add HiDPI support to CARTO basemap (#81195) [#106211](https://github.com/grafana/grafana/pull/106211), [@cledwynl](https://github.com/cledwynl)
|
||||
- **Git Sync UI:** Delete Provisioned Dashboard Flow [#106593](https://github.com/grafana/grafana/pull/106593), [@ywzheng1](https://github.com/ywzheng1)
|
||||
- **Grafana/data:** Extract fuzzy search core [#107110](https://github.com/grafana/grafana/pull/107110), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **I18n:** Update eslint rule to catch some untranslated object properties [#105072](https://github.com/grafana/grafana/pull/105072), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **InfluxDB:** Add an optional time range filter for tag queries in the query panel autocompleteInflux tag filter [#107195](https://github.com/grafana/grafana/pull/107195), [@NikolayTsvetkov](https://github.com/NikolayTsvetkov)
|
||||
- **LBAC for data sources:** Adds team filtering for lbac rules (Enterprise)
|
||||
- **Library Panels:** Mark library panel RBAC as GA & enable by default [#106833](https://github.com/grafana/grafana/pull/106833), [@kaydelaney](https://github.com/kaydelaney)
|
||||
- **Library Panels:** Modify connection api endpoint to be compatible with unified storage [#107088](https://github.com/grafana/grafana/pull/107088), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Library elements:** Remove ability to set as "library variable" [#106594](https://github.com/grafana/grafana/pull/106594), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Library panels:** Remove `libraryPanelRBAC` feature flag, and enable rbac by default [#107222](https://github.com/grafana/grafana/pull/107222), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Loki:** Remove experimental lokiQuerySplittingConfig [#107298](https://github.com/grafana/grafana/pull/107298), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Loki:** Remove experimental predefined operations [#107289](https://github.com/grafana/grafana/pull/107289), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **OAuth:** Add access token as third source for user info extraction [#107636](https://github.com/grafana/grafana/pull/107636), [@Jguer](https://github.com/Jguer)
|
||||
- **Plugin Extensions:** Expose PluginMeta generic in usePluginContext [#107577](https://github.com/grafana/grafana/pull/107577), [@MattIPv4](https://github.com/MattIPv4)
|
||||
- **Postgres:** Switch the datasource plugin from lib/pq to pgx [#103961](https://github.com/grafana/grafana/pull/103961), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **Preferences:** Use dashboard uid for the home dashboard [#106666](https://github.com/grafana/grafana/pull/106666), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Profiles:** Stop passing response headers for Grafana-Pyroscope and parca datasources [#106577](https://github.com/grafana/grafana/pull/106577), [@simonswine](https://github.com/simonswine)
|
||||
- **Prometheus:** Deprecation message for Azure auth [#106490](https://github.com/grafana/grafana/pull/106490), [@bossinc](https://github.com/bossinc)
|
||||
- **Prometheus:** Facilitate tree shaking with exports and bundler mode [#105575](https://github.com/grafana/grafana/pull/105575), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **Prometheus:** Migrate remaining selectors to data-testid [#106564](https://github.com/grafana/grafana/pull/106564), [@idastambuk](https://github.com/idastambuk)
|
||||
- **ProvisionedFolder:** Delete folder drawer [#107089](https://github.com/grafana/grafana/pull/107089), [@ywzheng1](https://github.com/ywzheng1)
|
||||
- **Provisioning:** Add pure git repository type [#106815](https://github.com/grafana/grafana/pull/106815), [@MissingRoberto](https://github.com/MissingRoberto)
|
||||
- **Querying:** Pass dashboard and panel title as headers [#107032](https://github.com/grafana/grafana/pull/107032), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Remote Alertmanager:** Send SMTP config [#106337](https://github.com/grafana/grafana/pull/106337), [@santihernandezc](https://github.com/santihernandezc)
|
||||
- **Restore dashboards:** Add filters and search [#106994](https://github.com/grafana/grafana/pull/106994), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **SCIM:** Ignore unsupported fields in user PATCH requests (Enterprise)
|
||||
- **SCIM:** Implement operation for adding an externalId value to a team (Enterprise)
|
||||
- **SCIM:** Implement the add members operation in group PATCH requests (Enterprise)
|
||||
- **SCIM:** Implement the remove members operation in group PATCH requests (Enterprise)
|
||||
- **SCIM:** Update externalId field in group PATCH request (Enterprise)
|
||||
- **SQL Expressions:** Always convert on type first [#106083](https://github.com/grafana/grafana/pull/106083), [@kylebrandt](https://github.com/kylebrandt)
|
||||
- **Select:** Set min width for the current selected item when width=auto [#106131](https://github.com/grafana/grafana/pull/106131), [@tskarhed](https://github.com/tskarhed)
|
||||
- **StateTimeline:** Display false and empty string values [#107059](https://github.com/grafana/grafana/pull/107059), [@jesdavpet](https://github.com/jesdavpet)
|
||||
- **StateTimeline:** Support `NaN` and `null` value mappings [#105638](https://github.com/grafana/grafana/pull/105638), [@fastfrwrd](https://github.com/fastfrwrd)
|
||||
- **Storage:** Take `migration_locking` setting into account [#105938](https://github.com/grafana/grafana/pull/105938), [@JohnnyQQQQ](https://github.com/JohnnyQQQQ)
|
||||
- **TableNG:** Refactor to better take advantage of react-data-grid [#103755](https://github.com/grafana/grafana/pull/103755), [@leeoniya](https://github.com/leeoniya)
|
||||
- **Tables:** Pills for Table Cells [#107485](https://github.com/grafana/grafana/pull/107485), [@timlevett](https://github.com/timlevett)
|
||||
- **Teams:** Add support for updating externalId field [#106406](https://github.com/grafana/grafana/pull/106406), [@dmihai](https://github.com/dmihai)
|
||||
- **Tempo:** Enable native histograms for Tempo service graph [#105989](https://github.com/grafana/grafana/pull/105989), [@bohandley](https://github.com/bohandley)
|
||||
- **TimeRangePicker:** Highlight range on hover [#106616](https://github.com/grafana/grafana/pull/106616), [@joshhunt](https://github.com/joshhunt)
|
||||
- **TraceView:** Resource attributes links extension point [#104680](https://github.com/grafana/grafana/pull/104680), [@edvard-falkskar](https://github.com/edvard-falkskar)
|
||||
- **Transformations:** Add "Auto" mode to Organize Fields [#103055](https://github.com/grafana/grafana/pull/103055), [@gelicia](https://github.com/gelicia)
|
||||
- **Transformations:** GA the Regression transformation [#106074](https://github.com/grafana/grafana/pull/106074), [@gelicia](https://github.com/gelicia)
|
||||
- **Unified storage:** Respect GF_DATABASE_URL override [#105331](https://github.com/grafana/grafana/pull/105331), [@pstibrany](https://github.com/pstibrany)
|
||||
- **VQB:** Add selected columns to GROUP BY dropdown (#106349) [#106391](https://github.com/grafana/grafana/pull/106391), [@Shubham19032004](https://github.com/Shubham19032004)
|
||||
- **VQB:** Allow custom table names in TableSelector [#106420](https://github.com/grafana/grafana/pull/106420), [@Victorthedev](https://github.com/Victorthedev)
|
||||
- **XYChart:** Add support for x=time [#106459](https://github.com/grafana/grafana/pull/106459), [@leeoniya](https://github.com/leeoniya)
|
||||
# 12.0.3 (2025-07-23)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix $value type when single data source is queried [#106080](https://github.com/grafana/grafana/pull/106080), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Fix ImportToGMARules flaky test [#106495](https://github.com/grafana/grafana/pull/106495), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Fix RefIds not being shown when creating or editing Grafana-managed recording rule [#106840](https://github.com/grafana/grafana/pull/106840), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Fix contact points tab visibility when user can only create [#106735](https://github.com/grafana/grafana/pull/106735), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Alerting:** Fix eval time unit in list view [#106488](https://github.com/grafana/grafana/pull/106488), [@ebuildy](https://github.com/ebuildy)
|
||||
- **Alerting:** Fix group interval override when adding new rules [#107324](https://github.com/grafana/grafana/pull/107324), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Fix group-level labels and query_offset in the import API [#106379](https://github.com/grafana/grafana/pull/106379), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Fix notification policy conflicts originating from provenance mismatch [#107343](https://github.com/grafana/grafana/pull/107343), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Fix resolved notifications for same-label Error to Normal transitions [#106210](https://github.com/grafana/grafana/pull/106210), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Hide labels section if we only have private labels [#105996](https://github.com/grafana/grafana/pull/105996), [@gillesdemey](https://github.com/gillesdemey)
|
||||
- **Annotations:** Remove prometheus from legacy runner [#106737](https://github.com/grafana/grafana/pull/106737), [@scottlepp](https://github.com/scottlepp)
|
||||
- **Azure:** Fix Application Insights metadata requests [#105614](https://github.com/grafana/grafana/pull/105614), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix duplicated trace links [#105698](https://github.com/grafana/grafana/pull/105698), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix legend formatting [#106504](https://github.com/grafana/grafana/pull/106504), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix resource name determination in template variable queries [#105705](https://github.com/grafana/grafana/pull/105705), [@aangelisc](https://github.com/aangelisc)
|
||||
- **BarChart/StateTimeline:** Use noValue setting for error message when data is empty [#107147](https://github.com/grafana/grafana/pull/107147), [@fastfrwrd](https://github.com/fastfrwrd)
|
||||
- **CloudWatch:** Fix http client handling + assume role bug [#107893](https://github.com/grafana/grafana/pull/107893), [@njvrzm](https://github.com/njvrzm)
|
||||
- **CloudWatch:** Fix proxy transport issue [#107807](https://github.com/grafana/grafana/pull/107807), [@njvrzm](https://github.com/njvrzm)
|
||||
- **Dashboard:** FF `dashboardNewLayouts` Fix library panels non-editable when multiple added [#107052](https://github.com/grafana/grafana/pull/107052), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Fix cache validation to prevent stale cache [#105918](https://github.com/grafana/grafana/pull/105918), [@yashschandra](https://github.com/yashschandra)
|
||||
- **Dashboard:** Fixes issue with dashboard links that include all variables [#106356](https://github.com/grafana/grafana/pull/106356), [@torkelo](https://github.com/torkelo)
|
||||
- **Dashboards:** Fix history list for dashboard uids that end in `-` [#107073](https://github.com/grafana/grafana/pull/107073), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Drilldown:** Fix js crash when using http [#105646](https://github.com/grafana/grafana/pull/105646), [@chu121su12](https://github.com/chu121su12)
|
||||
- **Fix:** Increase login_attempt.ip_address column length for IPv6 support [#107035](https://github.com/grafana/grafana/pull/107035), [@Jguer](https://github.com/Jguer)
|
||||
- **FlameGraph:** Fix bug for function names that conflict with JavaScript object prototype properties [#106338](https://github.com/grafana/grafana/pull/106338), [@simonswine](https://github.com/simonswine)
|
||||
- **Folders:** Correctly resolve nested folder breadcrumbs [#106344](https://github.com/grafana/grafana/pull/106344), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **GrafanaUI:** Fix Combobox ignoring loading prop [#105584](https://github.com/grafana/grafana/pull/105584), [@ValeraS](https://github.com/ValeraS)
|
||||
- **Graphite:** Fix annotation queries [#106553](https://github.com/grafana/grafana/pull/106553), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix date mutation [#107414](https://github.com/grafana/grafana/pull/107414), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix nested variable interpolation for repeated rows [#106976](https://github.com/grafana/grafana/pull/106976), [@aangelisc](https://github.com/aangelisc)
|
||||
- **K8s:** Dashboards /apis: Fix library element connections [#106734](https://github.com/grafana/grafana/pull/106734), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Loki:** Fix health check message [#107170](https://github.com/grafana/grafana/pull/107170), [@wooffie](https://github.com/wooffie)
|
||||
- **Loki:** Fix issue where step parameter using a template variable was marked as invalid [#106541](https://github.com/grafana/grafana/pull/106541), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Loki:** Fix label browser not sorted after selection of a label [#107394](https://github.com/grafana/grafana/pull/107394), [@paulojmdias](https://github.com/paulojmdias)
|
||||
- **Org:** Fix org deletion [#106193](https://github.com/grafana/grafana/pull/106193), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Plugins:** Fix and encode invalid gRPC header values [#107339](https://github.com/grafana/grafana/pull/107339), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **PostgreSQL:** Fix error on panel when toggling sqlDatasourceDatabaseSelection feature [#106965](https://github.com/grafana/grafana/pull/106965), [@HasithDeAlwis](https://github.com/HasithDeAlwis)
|
||||
- **Profiles:** Fix for passing the response headers [#106293](https://github.com/grafana/grafana/pull/106293), [@simonswine](https://github.com/simonswine)
|
||||
- **Reporting:** Stop sending reports with Never schedule on creation (Enterprise)
|
||||
- **SCIM:** Fix PUT request for deactivating a user (Enterprise)
|
||||
- **SCIM:** Fix the removal of all members in group PUT requests (Enterprise)
|
||||
- **SCIM:** Fix user patch operation (Enterprise)
|
||||
- **Security:** Add fix for CVE-2025-3580 [#105976](https://github.com/grafana/grafana/pull/105976), [@baldm0mma](https://github.com/baldm0mma)
|
||||
- **Security:** Fixes for CVE-2025-6197 and CVE-2025-6023 [#108333](https://github.com/grafana/grafana/pull/108333), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Settings:** Fix reencryption and rollback of encrypted values in setting table (Enterprise)
|
||||
- **Tempo:** Fix showing dangling edges in NodeGraph [#107245](https://github.com/grafana/grafana/pull/107245), [@ifrost](https://github.com/ifrost)
|
||||
- **ToolTip:** Fix flexbox bug with tooltip when `maxWidth` is set manually [#107145](https://github.com/grafana/grafana/pull/107145), [@jdmarshall](https://github.com/jdmarshall)
|
||||
- **URLParams:** Stringify true values as key=true always (fixes issues with variables with true value) [#106440](https://github.com/grafana/grafana/pull/106440), [@torkelo](https://github.com/torkelo)
|
||||
- **Security:** Fixes for CVE-2025-6197 and CVE-2025-6023 [#108280](https://github.com/grafana/grafana/pull/108280), [@volcanonoodle](https://github.com/volcanonoodle)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- **Alerting:** Enable recording rules by default [#105603](https://github.com/grafana/grafana/pull/105603), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
|
||||
### Plugin development fixes & changes
|
||||
|
||||
- **Carousel:** Always center image [#106468](https://github.com/grafana/grafana/pull/106468), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Drawer:** Include divider and close button when passing a custom title element [#106896](https://github.com/grafana/grafana/pull/106896), [@ashharrison90](https://github.com/ashharrison90)
|
||||
|
||||
<!-- 12.1.0 END -->
|
||||
<!-- 12.0.3 END -->
|
||||
<!-- 12.0.2 START -->
|
||||
|
||||
# 12.0.2 (2025-06-17)
|
||||
@@ -234,119 +80,6 @@
|
||||
- **Security:** Fixes CVE-2025-3415
|
||||
|
||||
<!-- 12.0.2 END -->
|
||||
<!-- 11.6.3 START -->
|
||||
|
||||
# 11.6.3 (2025-06-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixes CVE-2025-3415
|
||||
|
||||
<!-- 11.6.3 END -->
|
||||
<!-- 11.5.6 START -->
|
||||
|
||||
# 11.5.6 (2025-06-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixes CVE-2025-3415
|
||||
|
||||
<!-- 11.5.6 END -->
|
||||
<!-- 11.4.6 START -->
|
||||
|
||||
# 11.4.6 (2025-06-17)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Dependencies:** Bump Go to v1.24.4 [#106569](https://github.com/grafana/grafana/pull/106569), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga to v1.8.13 to address CVE-2025-48371 [#106119](https://github.com/grafana/grafana/pull/106119), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixes CVE-2025-3415
|
||||
|
||||
<!-- 11.4.6 END -->
|
||||
<!-- 11.3.8 START -->
|
||||
|
||||
# 11.3.8 (2025-06-17)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Dependencies:** Bump Go to v1.24.4 [#106571](https://github.com/grafana/grafana/pull/106571), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga to v1.8.13 to address CVE-2025-48371 [#106120](https://github.com/grafana/grafana/pull/106120), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixes CVE-2025-3415
|
||||
|
||||
<!-- 11.3.8 END -->
|
||||
<!-- 12.0.1+security-01 START -->
|
||||
|
||||
# 12.0.1+security-01 (2025-06-13)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 12.0.1+security-01 END -->
|
||||
<!-- 11.6.2+security-01 START -->
|
||||
|
||||
# 11.6.2+security-01 (2025-06-13)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 11.6.2+security-01 END -->
|
||||
<!-- 11.5.5+security-01 START -->
|
||||
|
||||
# 11.5.5+security-01 (2025-06-13)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 11.5.5+security-01 END -->
|
||||
<!-- 11.4.5+security-01 START -->
|
||||
|
||||
# 11.4.5+security-01 (2025-06-12)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 11.4.5+security-01 END -->
|
||||
<!-- 11.3.7+security-01 START -->
|
||||
|
||||
# 11.3.7+security-01 (2025-06-12)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 11.3.7+security-01 END -->
|
||||
<!-- 11.2.10+security-01 START -->
|
||||
|
||||
# 11.2.10+security-01 (2025-06-12)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 11.2.10+security-01 END -->
|
||||
<!-- 10.4.19+security-01 START -->
|
||||
|
||||
# 10.4.19+security-01 (2025-06-12)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-3415
|
||||
|
||||
<!-- 10.4.19+security-01 END -->
|
||||
<!-- 12.0.1 START -->
|
||||
|
||||
# 12.0.1 (2025-05-22)
|
||||
@@ -371,538 +104,10 @@
|
||||
- **Prometheus:** Fix semver import path [#104945](https://github.com/grafana/grafana/pull/104945), [@jackw](https://github.com/jackw)
|
||||
- **Themes:** Prevent duplicated API call in drawer [#105611](https://github.com/grafana/grafana/pull/105611), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **XYChart:** Coerce threshold steps to numbers [#104492](https://github.com/grafana/grafana/pull/104492), [@leeoniya](https://github.com/leeoniya)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 12.0.1 END -->
|
||||
<!-- 11.6.2 START -->
|
||||
|
||||
# 11.6.2 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105103](https://github.com/grafana/grafana/pull/105103), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.4-git to v2.5.0 [#105443](https://github.com/grafana/grafana/pull/105443), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.6 to v1.8.12 [#105369](https://github.com/grafana/grafana/pull/105369), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105251](https://github.com/grafana/grafana/pull/105251), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Dashboard:** Fixes issue with row repeats and first row [#104467](https://github.com/grafana/grafana/pull/104467), [@torkelo](https://github.com/torkelo)
|
||||
- **Graphite:** Ensure template variables are interpolated correctly [#105388](https://github.com/grafana/grafana/pull/105388), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix Graphite series interpolation [#104568](https://github.com/grafana/grafana/pull/104568), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Prometheus:** Fix semver import path [#104943](https://github.com/grafana/grafana/pull/104943), [@jackw](https://github.com/jackw)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.6.2 END -->
|
||||
<!-- 11.5.5 START -->
|
||||
|
||||
# 11.5.5 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105109](https://github.com/grafana/grafana/pull/105109), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.3 to v2.5.0 [#105441](https://github.com/grafana/grafana/pull/105441), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105373](https://github.com/grafana/grafana/pull/105373), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105252](https://github.com/grafana/grafana/pull/105252), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.5.5 END -->
|
||||
<!-- 11.4.5 START -->
|
||||
|
||||
# 11.4.5 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105110](https://github.com/grafana/grafana/pull/105110), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.2 to v2.5.0 [#105445](https://github.com/grafana/grafana/pull/105445), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105375](https://github.com/grafana/grafana/pull/105375), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105253](https://github.com/grafana/grafana/pull/105253), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.4.5 END -->
|
||||
<!-- 11.3.7 START -->
|
||||
|
||||
# 11.3.7 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105112](https://github.com/grafana/grafana/pull/105112), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.2 to v2.5.0 [#105447](https://github.com/grafana/grafana/pull/105447), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105376](https://github.com/grafana/grafana/pull/105376), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105254](https://github.com/grafana/grafana/pull/105254), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.3.7 END -->
|
||||
<!-- 11.2.10 START -->
|
||||
|
||||
# 11.2.10 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105113](https://github.com/grafana/grafana/pull/105113), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105378](https://github.com/grafana/grafana/pull/105378), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105255](https://github.com/grafana/grafana/pull/105255), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.2.10 END -->
|
||||
<!-- 10.4.19 START -->
|
||||
|
||||
# 10.4.19 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105115](https://github.com/grafana/grafana/pull/105115), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump golang.org/x/net from v0.37.0 to v0.40.0 [#105449](https://github.com/grafana/grafana/pull/105449), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 10.4.19 END -->
|
||||
<!-- 12.0.0+security-01 START -->
|
||||
|
||||
# 12.0.0+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 12.0.0+security-01 END -->
|
||||
<!-- 11.6.1+security-01 START -->
|
||||
|
||||
# 11.6.1+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.6.1+security-01 END -->
|
||||
<!-- 11.5.4+security-01 START -->
|
||||
|
||||
# 11.5.4+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.5.4+security-01 END -->
|
||||
<!-- 11.4.4+security-01 START -->
|
||||
|
||||
# 11.4.4+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.4.4+security-01 END -->
|
||||
<!-- 11.3.6+security-01 START -->
|
||||
|
||||
# 11.3.6+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.3.6+security-01 END -->
|
||||
<!-- 11.2.9+security-01 START -->
|
||||
|
||||
# 11.2.9+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.2.9+security-01 END -->
|
||||
<!-- 10.4.18+security-01 START -->
|
||||
|
||||
# 10.4.18+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 10.4.18+security-01 END -->
|
||||
<!-- 12.0.0 START -->
|
||||
|
||||
# 12.0.0 (2025-05-05)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** API to convert submitted Prometheus rules to GMA [#102231](https://github.com/grafana/grafana/pull/102231), [@fayzal-g](https://github.com/fayzal-g)
|
||||
- **Alerting:** Add HMAC signature config to the webhook integration [#100960](https://github.com/grafana/grafana/pull/100960), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Add MissingSeriesEvalsToResolve to the APIs [#102150](https://github.com/grafana/grafana/pull/102150), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Add UI migration feature toggle [#102217](https://github.com/grafana/grafana/pull/102217), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **Alerting:** Add backend support for keep_firing_for [#100750](https://github.com/grafana/grafana/pull/100750), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Add details and edit pages for groups [#100884](https://github.com/grafana/grafana/pull/100884), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Add keep_firing_for and Recovering state [#103248](https://github.com/grafana/grafana/pull/103248), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add migration to clean up rule versions table [#102484](https://github.com/grafana/grafana/pull/102484), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Add missing_series_evals_to_resolve option to alert rule form [#102808](https://github.com/grafana/grafana/pull/102808), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **Alerting:** Delete permanently deleted alert rules. [#102960](https://github.com/grafana/grafana/pull/102960), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Detect target folder rules and show warning [#103673](https://github.com/grafana/grafana/pull/103673), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Migration UI [#102010](https://github.com/grafana/grafana/pull/102010), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Recover deleted alert rules [#101869](https://github.com/grafana/grafana/pull/101869), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Remove constraints for uniqueness of rule title [#102067](https://github.com/grafana/grafana/pull/102067), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Remove feature flag `alertingNoDataErrorExecution` [#102156](https://github.com/grafana/grafana/pull/102156), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Sequential evaluation of rules in group [#98829](https://github.com/grafana/grafana/pull/98829), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Skip rules that are managed by plugins when importing datasource-managed rules [#103573](https://github.com/grafana/grafana/pull/103573), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Stop allowing manual editing/restore of internal AM config via settings [#103884](https://github.com/grafana/grafana/pull/103884), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **Alerting:** Template preview enhancements [#103817](https://github.com/grafana/grafana/pull/103817), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Alerting:** Update alerting module to 58ba6c617ff05eb1d6f65c59d369a6a16923dff6 [#102812](https://github.com/grafana/grafana/pull/102812), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Alerting:** Use 'Grafana IRM' wording in alerting contact point [#102014](https://github.com/grafana/grafana/pull/102014), [@brojd](https://github.com/brojd)
|
||||
- **Alerting:** Webhook Improvements - Templateable Payloads [#103818](https://github.com/grafana/grafana/pull/103818), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **AppChrome:** Move kiosk button into profile menu [#103600](https://github.com/grafana/grafana/pull/103600), [@torkelo](https://github.com/torkelo)
|
||||
- **AppPlatform:** Introduce experimental Github integration for dashboard configuration management [#96329](https://github.com/grafana/grafana/pull/96329), [@MissingRoberto](https://github.com/MissingRoberto)
|
||||
- **Authorization:** Add group to role DisplayName to make filtered list more clear [#102950](https://github.com/grafana/grafana/pull/102950), [@forsethc](https://github.com/forsethc)
|
||||
- **Azure Monitor:** Add logs query builder [#99055](https://github.com/grafana/grafana/pull/99055), [@alyssabull](https://github.com/alyssabull)
|
||||
- **Azure:** Mark Azure Prometheus exemplars as GA and enable by default [#100595](https://github.com/grafana/grafana/pull/100595), [@aangelisc](https://github.com/aangelisc)
|
||||
- **AzureMonitor:** Improve selection of Basic Logs tables in the query builder [#103820](https://github.com/grafana/grafana/pull/103820), [@aangelisc](https://github.com/aangelisc)
|
||||
- **BrowseDashboards:** Switch to list view if sort is set [#102196](https://github.com/grafana/grafana/pull/102196), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **Checkbox:** Add z-index to description [#103847](https://github.com/grafana/grafana/pull/103847), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **Chore:** Promoting feature toggle pluginsSriChecks GA [#102212](https://github.com/grafana/grafana/pull/102212), [@tolzhabayev](https://github.com/tolzhabayev)
|
||||
- **CloudMigrations:** Add sorting and error filtering to Snapshot Results backend [#102753](https://github.com/grafana/grafana/pull/102753), [@mmandrus](https://github.com/mmandrus)
|
||||
- **CloudMigrations:** Change onPremToCloudMigrations feature toggle to GA [#103212](https://github.com/grafana/grafana/pull/103212), [@dana-axinte](https://github.com/dana-axinte)
|
||||
- **CloudMigrations:** Enable high-level resource type selection [#103011](https://github.com/grafana/grafana/pull/103011), [@macabu](https://github.com/macabu)
|
||||
- **CloudMigrations:** Implement table sorting in the UI [#103061](https://github.com/grafana/grafana/pull/103061), [@mmandrus](https://github.com/mmandrus)
|
||||
- **CloudWatch:** Migrate to aws-sdk-go-v2 [#103106](https://github.com/grafana/grafana/pull/103106), [@njvrzm](https://github.com/njvrzm)
|
||||
- **Cloudwatch:** Do not parse log query grouping field to float [#102244](https://github.com/grafana/grafana/pull/102244), [@iwysiu](https://github.com/iwysiu)
|
||||
- **Cloudwatch:** Migrate to aws-sdk-go-v2 [#99643](https://github.com/grafana/grafana/pull/99643), [@njvrzm](https://github.com/njvrzm)
|
||||
- **Cloudwatch:** Revert aws sdk go v2 [#103644](https://github.com/grafana/grafana/pull/103644), [@iwysiu](https://github.com/iwysiu)
|
||||
- **Config:** Removes setting `viewers_can_edit` [#102275](https://github.com/grafana/grafana/pull/102275), [@eleijonmarck](https://github.com/eleijonmarck)
|
||||
- **Dashboard Restore:** Remove experimental functionality under feature flag `dashboardRestore` for now - this will be reworked [#103204](https://github.com/grafana/grafana/pull/103204), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Dashboards:** Add Dashboard Schema validation (1) [#103662](https://github.com/grafana/grafana/pull/103662), [@marcoabreu](https://github.com/marcoabreu)
|
||||
- **Dashboards:** Add a config setting that limits the number of series that will be displayed in a panel. Users can opt in to render all series. [#103405](https://github.com/grafana/grafana/pull/103405), [@oscarkilhed](https://github.com/oscarkilhed)
|
||||
- **Dashboards:** Prevent saving to a non-existent folder [#103503](https://github.com/grafana/grafana/pull/103503), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Dashboards:** Prevent version restore to same data [#102665](https://github.com/grafana/grafana/pull/102665), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Dependencies:** Bump github.com/redis/go-redis/v9 from 9.7.0 to 9.7.3 [#102555](https://github.com/grafana/grafana/pull/102555), [@dependabot[bot]](https://github.com/dependabot[bot])
|
||||
- **Docs:** Standard Datetime units limited to millisecond precision [#103610](https://github.com/grafana/grafana/pull/103610), [@axelavargas](https://github.com/axelavargas)
|
||||
- **ElasticSearch:** Improve index pattern error messaging and docs [#103899](https://github.com/grafana/grafana/pull/103899), [@idastambuk](https://github.com/idastambuk)
|
||||
- **ElasticSearch:** Make script field input a text area [#103708](https://github.com/grafana/grafana/pull/103708), [@idastambuk](https://github.com/idastambuk)
|
||||
- **Extensions:** Expose new observable APIs for accessing components and links [#103063](https://github.com/grafana/grafana/pull/103063), [@leventebalogh](https://github.com/leventebalogh)
|
||||
- **Feat:** Make expressions work with plugins that set `alerting:false` but `backend:true` in their `plugin.json` files [#102232](https://github.com/grafana/grafana/pull/102232), [@tolzhabayev](https://github.com/tolzhabayev)
|
||||
- **FlameGraphPanel:** Add units to standard options (#89815) [#102720](https://github.com/grafana/grafana/pull/102720), [@snyderdan](https://github.com/snyderdan)
|
||||
- **Frontend:** Remove Angular [#99760](https://github.com/grafana/grafana/pull/99760), [@jackw](https://github.com/jackw)
|
||||
- **Go:** Bump to 1.24.2 [#103521](https://github.com/grafana/grafana/pull/103521), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
- **I18n:** Add 13 new languages for translations [#102971](https://github.com/grafana/grafana/pull/102971), [@joshhunt](https://github.com/joshhunt)
|
||||
- **Influx:** Support PDC for Influx SQL [#103032](https://github.com/grafana/grafana/pull/103032), [@aangelisc](https://github.com/aangelisc)
|
||||
- **JWT:** Add org role mapping support to the JWT provider [#101584](https://github.com/grafana/grafana/pull/101584), [@QuentinBisson](https://github.com/QuentinBisson)
|
||||
- **K8s:** Dashboards: Add fine grained access control checks to /apis [#104418](https://github.com/grafana/grafana/pull/104418), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **K8s:** Enable kubernetesClientDashboardsFolders by default [#103843](https://github.com/grafana/grafana/pull/103843), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **LBAC for data sources:** PublicPreview and self serve enablement [#102276](https://github.com/grafana/grafana/pull/102276), [@eleijonmarck](https://github.com/eleijonmarck)
|
||||
- **Live:** Remove queryOverLive and live-service-web-worker experimental feature flags [#103518](https://github.com/grafana/grafana/pull/103518), [@ryantxu](https://github.com/ryantxu)
|
||||
- **Logs Panel:** Add ISO8601 date to log download files [#102932](https://github.com/grafana/grafana/pull/102932), [@gtk-grafana](https://github.com/gtk-grafana)
|
||||
- **Logs Table:** Add new Controls component to Explore [#103467](https://github.com/grafana/grafana/pull/103467), [@matyax](https://github.com/matyax)
|
||||
- **Logs:** Add new Controls component to Explore [#103401](https://github.com/grafana/grafana/pull/103401), [@matyax](https://github.com/matyax)
|
||||
- **Logs:** Always keep displayed fields with changed queries [#102493](https://github.com/grafana/grafana/pull/102493), [@svennergr](https://github.com/svennergr)
|
||||
- **Logs:** Clean up Explore meta information [#103801](https://github.com/grafana/grafana/pull/103801), [@matyax](https://github.com/matyax)
|
||||
- **Logs:** Prevent automatic scrolling on refresh after changing scroll position [#102463](https://github.com/grafana/grafana/pull/102463), [@matyax](https://github.com/matyax)
|
||||
- **MetricsDrilldown:** Advance `exploreMetricsUseExternalAppPlugin` feature toggle stage [#102137](https://github.com/grafana/grafana/pull/102137), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **MetricsDrilldown:** Advance `exploreMetricsUseExternalAppPlugin` to GA [#103653](https://github.com/grafana/grafana/pull/103653), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **MetricsDrilldown:** Mark `exploreMetricsUseExternalAppPlugin` as not frontend-only [#102942](https://github.com/grafana/grafana/pull/102942), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **MetricsDrilldown:** Remove legacy Metrics Drilldown code paths [#103845](https://github.com/grafana/grafana/pull/103845), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **MetricsDrilldown:** Restore link to Metrics Drilldown from Explore [#104075](https://github.com/grafana/grafana/pull/104075), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **NodeGraph:** Add node graph algorithm layout option [#102760](https://github.com/grafana/grafana/pull/102760), [@joey-grafana](https://github.com/joey-grafana)
|
||||
- **Plugins:** Remove plugin dependency version (Enterprise)
|
||||
- **Plugins:** Remove sort by options from plugins catalog [#102862](https://github.com/grafana/grafana/pull/102862), [@oshirohugo](https://github.com/oshirohugo)
|
||||
- **Plugins:** Remove support for secrets manager plugins [#101467](https://github.com/grafana/grafana/pull/101467), [@wbrowne](https://github.com/wbrowne)
|
||||
- **Plugins:** Remove support for secrets manager plugins (Enterprise)
|
||||
- **Plugins:** Remove userStorageAPI feature toggle [#102915](https://github.com/grafana/grafana/pull/102915), [@oshirohugo](https://github.com/oshirohugo)
|
||||
- **Prometheus:** Add back @lezer/highlight to dev dependency [#102632](https://github.com/grafana/grafana/pull/102632), [@idastambuk](https://github.com/idastambuk)
|
||||
- **Prometheus:** Add support for cloud partners Prometheus data sources [#103482](https://github.com/grafana/grafana/pull/103482), [@kevinwcyu](https://github.com/kevinwcyu)
|
||||
- **Prometheus:** Enable Combobox metric select by default [#101045](https://github.com/grafana/grafana/pull/101045), [@joshhunt](https://github.com/joshhunt)
|
||||
- **Prometheus:** Enable prometheusRunQueriesInParallel feature toggle by default [#102127](https://github.com/grafana/grafana/pull/102127), [@itsmylife](https://github.com/itsmylife)
|
||||
- **RecordedQueries:** Deprecate recorded queries UI messaging (Enterprise)
|
||||
- **Security:** Update JWT library (CVE-2025-30204) [#102715](https://github.com/grafana/grafana/pull/102715), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Tempo:** Add support for ad-hoc filters [#102448](https://github.com/grafana/grafana/pull/102448), [@ifrost](https://github.com/ifrost)
|
||||
- **Tempo:** Remove aggregate by [#98474](https://github.com/grafana/grafana/pull/98474), [@joey-grafana](https://github.com/joey-grafana)
|
||||
- **TraceView:** Add scope attributes to span details [#103173](https://github.com/grafana/grafana/pull/103173), [@joey-grafana](https://github.com/joey-grafana)
|
||||
- **TraceView:** Render all links in span details [#101881](https://github.com/grafana/grafana/pull/101881), [@ifrost](https://github.com/ifrost)
|
||||
- **Traces:** Preinstall Traces Drilldown app with Grafana [#102986](https://github.com/grafana/grafana/pull/102986), [@ifrost](https://github.com/ifrost)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix Simple condition threshold inputs with negative values. [#102976](https://github.com/grafana/grafana/pull/102976), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Fix display of `Normal (Updated)` in alert history [#102476](https://github.com/grafana/grafana/pull/102476), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **Alerting:** Fix rule instances table [#102290](https://github.com/grafana/grafana/pull/102290), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Make nested folders work in Alert List Panel [#103550](https://github.com/grafana/grafana/pull/103550), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **Alerting:** Remove rule type switch for modified export mode [#102287](https://github.com/grafana/grafana/pull/102287), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Simplified alert rule toggle bug fixes [#102119](https://github.com/grafana/grafana/pull/102119), [@gillesdemey](https://github.com/gillesdemey)
|
||||
- **Alertmanager:** Add Role-Based Access Control via reqAction Field [#101543](https://github.com/grafana/grafana/pull/101543), [@olegpixel](https://github.com/olegpixel)
|
||||
- **App Platform:** Pin bleve to fix CVE-2022-31022 [#102513](https://github.com/grafana/grafana/pull/102513), [@Proximyst](https://github.com/Proximyst)
|
||||
- **AppChrome/MegaMenu:** Fixes issue with default state being initialised to undocked [#103507](https://github.com/grafana/grafana/pull/103507), [@torkelo](https://github.com/torkelo)
|
||||
- **AppTitle:** Fix overflowing text [#103583](https://github.com/grafana/grafana/pull/103583), [@tskarhed](https://github.com/tskarhed)
|
||||
- **Azure:** Ensure basic logs queries are limited to a single resource [#103588](https://github.com/grafana/grafana/pull/103588), [@aangelisc](https://github.com/aangelisc)
|
||||
- **CloudWatch:** Import new grafana-aws-sdk with PDC fix [#103249](https://github.com/grafana/grafana/pull/103249), [@njvrzm](https://github.com/njvrzm)
|
||||
- **ColorPicker:** Fixed height when switching tabs [#103304](https://github.com/grafana/grafana/pull/103304), [@DanMPA](https://github.com/DanMPA)
|
||||
- **Dashboard:** Fix Core Panel Migrations - table panel [#102146](https://github.com/grafana/grafana/pull/102146), [@axelavargas](https://github.com/axelavargas)
|
||||
- **DashboardScenePage:** Correct slug in self referencing data links [#100048](https://github.com/grafana/grafana/pull/100048), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
|
||||
- **Dashboards:** Fix duplicate provisioning when errors occur on title-only based provisioning [#102249](https://github.com/grafana/grafana/pull/102249), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Dashboards:** Fix panel link to Grafana Metrics Drilldown [#103759](https://github.com/grafana/grafana/pull/103759), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **Fix:** Change secure_json_data column data type to medium text only MYSQL [#102557](https://github.com/grafana/grafana/pull/102557), [@s4kh](https://github.com/s4kh)
|
||||
- **GrafanaUI:** Prevent ToolbarButton from submitting form [#102228](https://github.com/grafana/grafana/pull/102228), [@kozhuhds](https://github.com/kozhuhds)
|
||||
- **GrafanaUI:** Remove blurred background from overlay backdrops to improve performance [#103563](https://github.com/grafana/grafana/pull/103563), [@joshhunt](https://github.com/joshhunt)
|
||||
- **LDAP test:** Fix page crash [#102587](https://github.com/grafana/grafana/pull/102587), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Navigation:** Fix bookmarks when Grafana is running under subpath [#102679](https://github.com/grafana/grafana/pull/102679), [@matejkubinec](https://github.com/matejkubinec)
|
||||
- **PanelEdit:** Fixes suggestions not applying options or field config [#102675](https://github.com/grafana/grafana/pull/102675), [@torkelo](https://github.com/torkelo)
|
||||
- **PluginProxy:** Fix nil pointer in OAuth forwarding [#103626](https://github.com/grafana/grafana/pull/103626), [@moustafab](https://github.com/moustafab)
|
||||
- **Plugins:** Fix better UX for disabled Angular plugins [#101333](https://github.com/grafana/grafana/pull/101333), [@hugohaggmark](https://github.com/hugohaggmark)
|
||||
- **Plugins:** Fix support for adhoc filters with raw queries in InfluxDB [#101966](https://github.com/grafana/grafana/pull/101966), [@beejeebus](https://github.com/beejeebus)
|
||||
- **Renderer:** Fix regression on callback URL in plugin mode [#103787](https://github.com/grafana/grafana/pull/103787), [@AgnesToulet](https://github.com/AgnesToulet)
|
||||
- **SQL:** Fix builder crashes when any in selected [#102871](https://github.com/grafana/grafana/pull/102871), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **SSE:** Fix goroutine leak in math operation expression parsing [#102380](https://github.com/grafana/grafana/pull/102380), [@kylebrandt](https://github.com/kylebrandt)
|
||||
- **Tempo:** Add fixes for broken exemplars [#103298](https://github.com/grafana/grafana/pull/103298), [@joey-grafana](https://github.com/joey-grafana)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- **Alerting:** Make $value return the query value in case when a single datasource is used [#102301](https://github.com/grafana/grafana/pull/102301), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Relax permissions for access a rule [#103664](https://github.com/grafana/grafana/pull/103664), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Remove feature toggles relating to Loki Alert State History [#103540](https://github.com/grafana/grafana/pull/103540), [@rwwiv](https://github.com/rwwiv)
|
||||
- **Alerting:** Remove the POST endpoint for the internal Grafana Alertmanager config [#103819](https://github.com/grafana/grafana/pull/103819), [@rwwiv](https://github.com/rwwiv)
|
||||
- **Anonymous:** Enforce org role Viewer setting [#102070](https://github.com/grafana/grafana/pull/102070), [@eleijonmarck](https://github.com/eleijonmarck)
|
||||
- **Chore:** Enable Grafana version check when installing plugins [#103176](https://github.com/grafana/grafana/pull/103176), [@andresmgot](https://github.com/andresmgot)
|
||||
- **Chore:** Enabling failWrongDSUID by default in Grafana 12 [#102192](https://github.com/grafana/grafana/pull/102192), [@tolzhabayev](https://github.com/tolzhabayev)
|
||||
- **Config:** Removes setting `viewers_can_edit` [#101767](https://github.com/grafana/grafana/pull/101767), [@eleijonmarck](https://github.com/eleijonmarck)
|
||||
- **Frontend:** Remove Angular (Enterprise)
|
||||
- **Plugin Extensions:** Clean up the deprecated APIs [#102102](https://github.com/grafana/grafana/pull/102102), [@leventebalogh](https://github.com/leventebalogh)
|
||||
- **Plugins:** Remove plugin dependency version [#103728](https://github.com/grafana/grafana/pull/103728), [@wbrowne](https://github.com/wbrowne)
|
||||
- **Tempo:** Remove traceQLStreaming feature toggle [#103619](https://github.com/grafana/grafana/pull/103619), [@adrapereira](https://github.com/adrapereira)
|
||||
|
||||
### Plugin development fixes & changes
|
||||
|
||||
- **Combobox:** add grouping functionality [#100603](https://github.com/grafana/grafana/pull/100603), [@eledobleefe](https://github.com/eledobleefe)
|
||||
- **Grafana UI:** Add `columnGap` + `rowGap` to `Stack`/`Grid` [#102883](https://github.com/grafana/grafana/pull/102883), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Grafana UI:** Clearly separate multiple warnings by using HTML tags [#97979](https://github.com/grafana/grafana/pull/97979), [@zenador](https://github.com/zenador)
|
||||
|
||||
<!-- 12.0.0 END -->
|
||||
<!-- 11.6.1 START -->
|
||||
|
||||
# 11.6.1 (2025-04-23)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Update JWT library (CVE-2025-30204) [#102727](https://github.com/grafana/grafana/pull/102727), [@grambbledook](https://github.com/grambbledook)
|
||||
- **DashboardScenePage:** Correct slug in self referencing data links [#103854](https://github.com/grafana/grafana/pull/103854), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
|
||||
- **Dependencies:** Bump github.com/redis/go-redis/v9 to 9.7.3 to address CVE-2025-29923 [#102863](https://github.com/grafana/grafana/pull/102863), [@macabu](https://github.com/macabu)
|
||||
- **Go:** Bump to 1.24.2 [#103523](https://github.com/grafana/grafana/pull/103523), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
- **GrafanaUI:** Use safePolygon close handler for interactive tooltips instead of a delay [#102869](https://github.com/grafana/grafana/pull/102869), [@mthorning](https://github.com/mthorning)
|
||||
- **Prometheus:** Add support for cloud partners Prometheus data sources [#103941](https://github.com/grafana/grafana/pull/103941), [@kevinwcyu](https://github.com/kevinwcyu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alertmanager:** Add Role-Based Access Control via reqAction Field [#103479](https://github.com/grafana/grafana/pull/103479), [@olegpixel](https://github.com/olegpixel)
|
||||
- **GrafanaUI:** Remove blurred background from overlay backdrops to improve performance [#103647](https://github.com/grafana/grafana/pull/103647), [@joshhunt](https://github.com/joshhunt)
|
||||
- **InfluxDB:** Fix nested variable interpolation [#104096](https://github.com/grafana/grafana/pull/104096), [@aangelisc](https://github.com/aangelisc)
|
||||
- **LDAP test:** Fix page crash [#102684](https://github.com/grafana/grafana/pull/102684), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Org redirection:** Fix linking between orgs [#102870](https://github.com/grafana/grafana/pull/102870), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
- **Security:** Fix CVE-2025-3260
|
||||
|
||||
<!-- 11.6.1 END -->
|
||||
<!-- 11.5.4 START -->
|
||||
|
||||
# 11.5.4 (2025-04-23)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Azure Monitor:** Filter namespaces by resource group [#103654](https://github.com/grafana/grafana/pull/103654), [@alyssabull](https://github.com/alyssabull)
|
||||
- **Azure:** Add support for custom namespace and custom metrics variable queries [#103650](https://github.com/grafana/grafana/pull/103650), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Resource picker improvements [#103638](https://github.com/grafana/grafana/pull/103638), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Support more complex variable interpolation [#103651](https://github.com/grafana/grafana/pull/103651), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Variable editor and resource picker improvements [#103657](https://github.com/grafana/grafana/pull/103657), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Chore:** Update CVE-affected dependencies [#102709](https://github.com/grafana/grafana/pull/102709), [@grambbledook](https://github.com/grambbledook)
|
||||
- **DashboardScenePage:** Correct slug in self referencing data links [#103853](https://github.com/grafana/grafana/pull/103853), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
|
||||
- **Dependencies:** Bump github.com/redis/go-redis/v9 to 9.6.3 to address CVE-2025-29923 [#102865](https://github.com/grafana/grafana/pull/102865), [@macabu](https://github.com/macabu)
|
||||
- **Go:** Bump to 1.24.2 [#103525](https://github.com/grafana/grafana/pull/103525), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
- **Prometheus:** Add support for cloud partners Prometheus data sources [#103942](https://github.com/grafana/grafana/pull/103942), [@kevinwcyu](https://github.com/kevinwcyu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **InfluxDB:** Fix nested variable interpolation [#104095](https://github.com/grafana/grafana/pull/104095), [@aangelisc](https://github.com/aangelisc)
|
||||
- **LDAP test:** Fix page crash [#102683](https://github.com/grafana/grafana/pull/102683), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.5.4 END -->
|
||||
<!-- 11.4.4 START -->
|
||||
|
||||
# 11.4.4 (2025-04-23)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.4.4 END -->
|
||||
<!-- 11.3.6 START -->
|
||||
|
||||
# 11.3.6 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Update libs with CVE in dependencies [#102710](https://github.com/grafana/grafana/pull/102710), [@grambbledook](https://github.com/grambbledook)
|
||||
- **Go:** Bump to 1.24.2 [#103528](https://github.com/grafana/grafana/pull/103528), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Auth:** Fix SAML user IsExternallySynced not being set correctly [#103101](https://github.com/grafana/grafana/pull/103101), [@volcanonoodle](https://github.com/volcanonoodle)
|
||||
- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#102983](https://github.com/grafana/grafana/pull/102983), [@kalleep](https://github.com/kalleep)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.3.6 END -->
|
||||
<!-- 11.2.9 START -->
|
||||
|
||||
# 11.2.9 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Update libs with CVE in dependencies [#102712](https://github.com/grafana/grafana/pull/102712), [@grambbledook](https://github.com/grambbledook)
|
||||
- **Go:** Bump to 1.24.2 [#103529](https://github.com/grafana/grafana/pull/103529), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Auth:** Fix SAML user IsExternallySynced not being set correctly [#103102](https://github.com/grafana/grafana/pull/103102), [@volcanonoodle](https://github.com/volcanonoodle)
|
||||
- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#102982](https://github.com/grafana/grafana/pull/102982), [@kalleep](https://github.com/kalleep)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.2.9 END -->
|
||||
<!-- 10.4.18 START -->
|
||||
|
||||
# 10.4.18 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump golang-jwt/jwt/v4 and golang-jwt/jwt/v5 to address security issues [#102762](https://github.com/grafana/grafana/pull/102762), [@macabu](https://github.com/macabu)
|
||||
- **Go:** Bump to 1.24.2 [#103531](https://github.com/grafana/grafana/pull/103531), [@Proximyst](https://github.com/Proximyst)
|
||||
- **Go:** Bump to 1.24.2 (Enterprise)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Auth:** Fix SAML user IsExternallySynced not being set correctly (#98487) [#103177](https://github.com/grafana/grafana/pull/103177), [@volcanonoodle](https://github.com/volcanonoodle)
|
||||
- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#102981](https://github.com/grafana/grafana/pull/102981), [@kalleep](https://github.com/kalleep)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
|
||||
<!-- 10.4.18 END -->
|
||||
<!-- 11.6.0+security-01 START -->
|
||||
|
||||
# 11.6.0+security-01 (2025-04-22)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
- **Security:** Fix CVE-2025-3260
|
||||
|
||||
<!-- 11.6.0+security-01 END -->
|
||||
<!-- 11.5.3+security-01 START -->
|
||||
|
||||
# 11.5.3+security-01 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go to 1.23.7 [#101581](https://github.com/grafana/grafana/pull/101581), [@macabu](https://github.com/macabu)
|
||||
- **Chore:** Bump Go to 1.23.7 (Enterprise)
|
||||
- **Chore:** Update CVE-affected dependencies [#102709](https://github.com/grafana/grafana/pull/102709), [@grambbledook](https://github.com/grambbledook)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix token-based Slack image upload to work with channel names [#101078](https://github.com/grafana/grafana/pull/101078), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Auth:** Fix AzureAD config UI's ClientAuthentication dropdown [#100869](https://github.com/grafana/grafana/pull/100869), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Dashboard:** Fix the unintentional time range and variables updates on saving [#101671](https://github.com/grafana/grafana/pull/101671), [@harisrozajac](https://github.com/harisrozajac)
|
||||
- **Dashboards:** Fix missing `v/e/i` keybindings to return back to dashboard [#102365](https://github.com/grafana/grafana/pull/102365), [@mdvictor](https://github.com/mdvictor)
|
||||
- **InfluxDB:** Improve handling of template variables contained in regular expressions (InfluxQL) [#100977](https://github.com/grafana/grafana/pull/100977), [@aangelisc](https://github.com/aangelisc)
|
||||
- **LDAP test:** Fix page crash [#102683](https://github.com/grafana/grafana/pull/102683), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Org redirection:** Fix linking between orgs [#102089](https://github.com/grafana/grafana/pull/102089), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.5.3+security-01 END -->
|
||||
<!-- 11.4.3+security-01 START -->
|
||||
|
||||
# 11.4.3+security-01 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go to 1.23.7 [#101582](https://github.com/grafana/grafana/pull/101582), [@macabu](https://github.com/macabu)
|
||||
- **Chore:** Bump Go to 1.23.7 (Enterprise)
|
||||
- **Chore:** Update CVE-affected golang-gwt dependencies [#102704](https://github.com/grafana/grafana/pull/102704), [@grambbledook](https://github.com/grambbledook)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix token-based Slack image upload to work with channel names [#101072](https://github.com/grafana/grafana/pull/101072), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **InfluxDB:** Improve handling of template variables contained in regular expressions (InfluxQL) [#100987](https://github.com/grafana/grafana/pull/100987), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Service Accounts:** Do not show error pop-ups for Service Account and Renderer UI flows [#101790](https://github.com/grafana/grafana/pull/101790), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.4.3+security-01 END -->
|
||||
<!-- 11.3.5+security-01 START -->
|
||||
|
||||
# 11.3.5+security-01 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go to 1.23.7 [#101583](https://github.com/grafana/grafana/pull/101583), [@macabu](https://github.com/macabu)
|
||||
- **Chore:** Bump Go to 1.23.7 (Enterprise)
|
||||
- **Chore:** Update libs with CVE in dependencies [#102710](https://github.com/grafana/grafana/pull/102710), [@grambbledook](https://github.com/grambbledook)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix token-based Slack image upload to work with channel names [#101488](https://github.com/grafana/grafana/pull/101488), [@moustafab](https://github.com/moustafab)
|
||||
- **Service Accounts:** Do not show error pop-ups for Service Account and Renderer UI flows [#101791](https://github.com/grafana/grafana/pull/101791), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.3.5+security-01 END -->
|
||||
<!-- 11.2.8+security-01 START -->
|
||||
|
||||
# 11.2.8+security-01 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.23.7 [#101294](https://github.com/grafana/grafana/pull/101294), [@macabu](https://github.com/macabu)
|
||||
- **Chore:** Bump Go version to 1.23.7 (Enterprise)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Update slack image upload to use new API [#101487](https://github.com/grafana/grafana/pull/101487), [@moustafab](https://github.com/moustafab)
|
||||
- **CloudMigrations:** Fix OrderBy clause in GetSnapshotList sql handler [#102351](https://github.com/grafana/grafana/pull/102351), [@mmandrus](https://github.com/mmandrus)
|
||||
- **Service Accounts:** Do not show error pop-ups for Service Account and Renderer UI flows [#101795](https://github.com/grafana/grafana/pull/101795), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
- **Security:** Fix CVE-2025-2703
|
||||
|
||||
<!-- 11.2.8+security-01 END -->
|
||||
<!-- 10.4.17+security-01 START -->
|
||||
|
||||
# 10.4.17+security-01 (2025-04-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.23.7 [#101565](https://github.com/grafana/grafana/pull/101565), [@macabu](https://github.com/macabu)
|
||||
- **Chore:** Bump Go version to 1.23.7 (Enterprise)
|
||||
- **Chore:** Bump golang-jwt/jwt/v4 and golang-jwt/jwt/v5 to address security issues [#102762](https://github.com/grafana/grafana/pull/102762), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Update slack image upload to use new API [#101483](https://github.com/grafana/grafana/pull/101483), [@moustafab](https://github.com/moustafab)
|
||||
- **Service Accounts:** Do not show error pop-ups for Service Account and Renderer UI flows [#101804](https://github.com/grafana/grafana/pull/101804), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **Security:** Fix CVE-2025-3454
|
||||
|
||||
<!-- 10.4.17+security-01 END -->
|
||||
<!-- 11.6.0 START -->
|
||||
|
||||
# 11.6.0 (2025-03-25)
|
||||
|
||||
24
COMMIT_EDITMSG
Normal file
24
COMMIT_EDITMSG
Normal file
@@ -0,0 +1,24 @@
|
||||
TableNG: Fix interpolation for actions (#104577)
|
||||
|
||||
(cherry picked from commit 6c0250dde285affbdc56edcd069473f33d48b3be)
|
||||
|
||||
# Conflicts:
|
||||
# packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellNG.tsx
|
||||
#
|
||||
# It looks like you may be committing a cherry-pick.
|
||||
# If this is not correct, please run
|
||||
# git update-ref -d CHERRY_PICK_HEAD
|
||||
# and try again.
|
||||
|
||||
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
#
|
||||
# Author: Adela Almasan <88068998+adela-almasan@users.noreply.github.com>
|
||||
# Date: Wed Apr 30 09:42:15 2025 -0500
|
||||
#
|
||||
# On branch backport-104577-to-release-12.0.1
|
||||
# Your branch is up to date with 'origin/backport-104577-to-release-12.0.1'.
|
||||
#
|
||||
# You are currently cherry-picking commit 6c0250dde28.
|
||||
#
|
||||
@@ -16,7 +16,7 @@ ARG JS_SRC=js-builder
|
||||
# By using FROM instructions we can delegate dependency updates to dependabot
|
||||
FROM alpine:3.21.3 AS alpine-base
|
||||
FROM ubuntu:22.04 AS ubuntu-base
|
||||
FROM golang:1.24.6-alpine AS go-builder-base
|
||||
FROM golang:1.25.3-alpine AS go-builder-base
|
||||
FROM --platform=${JS_PLATFORM} node:22-alpine AS js-builder-base
|
||||
|
||||
# Javascript build stage
|
||||
@@ -71,6 +71,7 @@ COPY .citools .citools
|
||||
|
||||
# Include vendored dependencies
|
||||
COPY pkg/util/xorm pkg/util/xorm
|
||||
COPY pkg/apis/folder pkg/apis/folder
|
||||
COPY pkg/apis/secret pkg/apis/secret
|
||||
COPY pkg/apiserver pkg/apiserver
|
||||
COPY pkg/apimachinery pkg/apimachinery
|
||||
@@ -78,7 +79,6 @@ COPY pkg/build pkg/build
|
||||
COPY pkg/build/wire pkg/build/wire
|
||||
COPY pkg/promlib pkg/promlib
|
||||
COPY pkg/storage/unified/resource pkg/storage/unified/resource
|
||||
COPY pkg/storage/unified/resourcepb pkg/storage/unified/resourcepb
|
||||
COPY pkg/storage/unified/apistore pkg/storage/unified/apistore
|
||||
COPY pkg/semconv pkg/semconv
|
||||
COPY pkg/aggregator pkg/aggregator
|
||||
@@ -87,7 +87,6 @@ COPY apps/investigations apps/investigations
|
||||
COPY apps/advisor apps/advisor
|
||||
COPY apps/dashboard apps/dashboard
|
||||
COPY apps/folder apps/folder
|
||||
COPY apps/iam apps/iam
|
||||
COPY apps apps
|
||||
COPY kindsv2 kindsv2
|
||||
COPY apps/alerting/notifications apps/alerting/notifications
|
||||
|
||||
10
LICENSING.md
10
LICENSING.md
@@ -30,13 +30,3 @@ The following directories and their subdirectories are licensed under their orig
|
||||
```
|
||||
public/vendor/
|
||||
```
|
||||
|
||||
## MIT license
|
||||
|
||||
The following files are licensed under MIT License:
|
||||
|
||||
```
|
||||
.github/workflows/actionlint-format.txt
|
||||
-> Vendored: https://github.com/rhysd/actionlint/blob/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/format/sarif_template.txt
|
||||
-> The workflow that uses it is AGPL-3.0-only.
|
||||
```
|
||||
|
||||
27
Makefile
27
Makefile
@@ -9,7 +9,7 @@ include .bingo/Variables.mk
|
||||
include .citools/Variables.mk
|
||||
|
||||
GO = go
|
||||
GO_VERSION = 1.24.6
|
||||
GO_VERSION = 1.25.3
|
||||
GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh)
|
||||
GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh)
|
||||
SH_FILES ?= $(shell find ./scripts -name *.sh)
|
||||
@@ -90,7 +90,6 @@ swagger-enterprise-gen: ## Generate API Swagger specification
|
||||
-x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \
|
||||
-x "github.com/prometheus/alertmanager" \
|
||||
-i pkg/api/swagger_tags.json \
|
||||
-t enterprise \
|
||||
--exclude-tag=alpha \
|
||||
--include-tag=enterprise
|
||||
endif
|
||||
@@ -141,10 +140,6 @@ endif
|
||||
i18n-extract: i18n-extract-enterprise
|
||||
@echo "Extracting i18n strings for OSS"
|
||||
yarn run i18next --config public/locales/i18next-parser.config.cjs
|
||||
@echo "Extracting i18n strings for packages"
|
||||
yarn run packages:i18n-extract
|
||||
@echo "Extracting i18n strings for plugins"
|
||||
yarn run plugin:i18n-extract
|
||||
|
||||
##@ Building
|
||||
.PHONY: gen-cue
|
||||
@@ -169,7 +164,7 @@ gen-cuev2: ## Do all CUE code generation
|
||||
# For now, we want to use an explicit list of apps to generate code for.
|
||||
#
|
||||
# APPS_DIRS=$(shell find ./apps -mindepth 1 -maxdepth 1 -type d | sort)
|
||||
APPS_DIRS := ./apps/dashboard ./apps/folder ./apps/alerting/notifications
|
||||
APPS_DIRS := ./apps/dashboard ./apps/folder
|
||||
|
||||
.PHONY: gen-apps
|
||||
gen-apps: ## Generate code for Grafana App SDK apps
|
||||
@@ -200,7 +195,7 @@ gen-enterprise-go: ## Generate Wire graph (Enterprise)
|
||||
$(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags "enterprise" -gen_tags "(enterprise || pro)" -output_file_prefix="enterprise_" ./pkg/server
|
||||
endif
|
||||
gen-go: gen-enterprise-go ## Generate Wire graph
|
||||
@echo "generatng Wire graph"
|
||||
@echo "generating Wire graph"
|
||||
$(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags "oss" -gen_tags "(!enterprise && !pro)" ./pkg/server
|
||||
|
||||
.PHONY: fix-cue
|
||||
@@ -275,10 +270,6 @@ run-go: ## Build and run web server immediately.
|
||||
run-frontend: deps-js ## Fetch js dependencies and watch frontend for rebuild
|
||||
yarn start
|
||||
|
||||
.PHONY: run-air
|
||||
run-air: ## [Experimental] Build and run backend, and watch for changes. See .air.toml for configuration. Check https://github.com/air-verse/air for installation instructions.
|
||||
air -c .air.toml
|
||||
|
||||
##@ Testing
|
||||
|
||||
.PHONY: test-go
|
||||
@@ -468,11 +459,11 @@ devenv-mysql:
|
||||
.PHONY: protobuf
|
||||
protobuf: ## Compile protobuf definitions
|
||||
bash scripts/protobuf-check.sh
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.5
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.4.0
|
||||
buf generate pkg/plugins/backendplugin/pluginextensionv2 --template pkg/plugins/backendplugin/pluginextensionv2/buf.gen.yaml
|
||||
buf generate pkg/apis/secret/v0alpha1/decrypt --template pkg/apis/secret/v0alpha1/decrypt/buf.gen.yaml
|
||||
buf generate pkg/storage/unified/proto --template pkg/storage/unified/proto/buf.gen.yaml
|
||||
buf generate pkg/storage/unified/resource --template pkg/storage/unified/resource/buf.gen.yaml
|
||||
buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml
|
||||
buf generate pkg/services/ngalert/store/proto/v1 --template pkg/services/ngalert/store/proto/v1/buf.gen.yaml
|
||||
|
||||
@@ -495,7 +486,7 @@ gen-ts:
|
||||
.PHONY: drone
|
||||
drone: $(DRONE)
|
||||
bash scripts/drone/env-var-check.sh
|
||||
$(DRONE) starlark --format
|
||||
$(DRONE) starlark --format --max-execution-steps 100000
|
||||
$(DRONE) lint .drone.yml --trusted
|
||||
$(DRONE) --server https://drone.grafana.net sign --save grafana/grafana
|
||||
|
||||
@@ -529,9 +520,3 @@ check-tparse:
|
||||
.PHONY: help
|
||||
help: ## Display this help.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
# check licenses of used dependencies (can be run using build image using
|
||||
# container/check-licenses target)
|
||||
check-licenses:
|
||||
license_finder --decisions-file .github/license_finder.yaml
|
||||
|
||||
|
||||
10
SECURITY.md
10
SECURITY.md
@@ -1,16 +1,10 @@
|
||||
# Reporting security issues
|
||||
|
||||
If you think you have found a security vulnerability, we have two routes for reporting security issues.
|
||||
|
||||
Important: Whichever route you choose, we ask you to not disclose the vulnerability before it has been fixed and announced, unless you received a response from the Grafana Labs security team that you can do so.
|
||||
|
||||
[Full guidance on reporting a security issue can be found here](https://grafana.com/legal/report-a-security-issue/).
|
||||
|
||||
This product is in scope for our Bug Bounty Program. To submit a vulnerability report, please visit [Grafana Labs Bug Bounty page](https://app.intigriti.com/programs/grafanalabs/grafanaossbbp/detail) and follow the instructions provided. Our security team will review your submission and get back to you as soon as possible.
|
||||
This product is in scope for our Bug Bounty Program. To submit a vulnerability report, please visit [Grafana Labs Bug Bounty Policy page](https://github.com/grafana/bugbounty) and follow the instructions provided. Our security team will review your submission and get back to you as soon as possible.
|
||||
|
||||
---
|
||||
|
||||
For products and services outside the scope of our bug bounty program, or if you do not wish to receive a bounty, you can report issues directly to us via email at security@grafana.com. This address can be used for all of Grafana Labs’ open source and commercial products (including but not limited to Grafana, Grafana Cloud, Grafana Enterprise, and grafana.com).
|
||||
For any other security issues, please send an email to security@grafana.com
|
||||
|
||||
Please encrypt your message to us; please use our PGP key. The key fingerprint is:
|
||||
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
# Grafana Advisor - Implementing New Checks
|
||||
|
||||
This guide explains how to implement new checks in the Grafana Advisor system. The Advisor system allows you to create custom checks that can validate various aspects of your Grafana instance.
|
||||
|
||||
## Check Structure
|
||||
|
||||
A check in Grafana Advisor consists of two main components:
|
||||
|
||||
1. A main check struct that implements the [`checks.Check`](https://github.com/grafana/grafana/blob/269226cb50b970ad9f692f1fdd220e9822e90db8/apps/advisor/pkg/app/checks/ifaces.go#L11-L25) interface
|
||||
2. One or more step structs that implement the [`checks.Step`](https://github.com/grafana/grafana/blob/269226cb50b970ad9f692f1fdd220e9822e90db8/apps/advisor/pkg/app/checks/ifaces.go#L28-L39) interface
|
||||
|
||||
## Implementing a New Check
|
||||
|
||||
### 1. Create the Check Package
|
||||
|
||||
Create a new package in `pkg/app/checks/` for your check. For example, if you're creating a check for validating configuration fields, you might create `pkg/app/checks/configchecks/`. Add a `check.go` file to the package, there we will implement the check interface. Let's start by implementing the `Check` interface but without any steps yet:
|
||||
|
||||
```go
|
||||
package configchecks
|
||||
|
||||
var _ checks.Check = (*check)(nil)
|
||||
|
||||
type check struct{}
|
||||
|
||||
func New() checks.Check {
|
||||
return &check{}
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "config setting"
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
return []checks.Step{}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 2. Define Dependencies and Register the Check
|
||||
|
||||
In order to be able to implement a check, it will likely need some dependencies in the form of `wire` services. This is the internal dependency injection system used in Grafana and it allows you to access the services you need.
|
||||
|
||||
For our example, we will need access to the grafana settings, which are exposed by wire as `*setting.Cfg` (in this case is a pointer to a struct, not an interface but the idea is the same). So let's add it to our check as a parameter for our `New` function:
|
||||
|
||||
```go
|
||||
type check struct {
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func New(cfg *setting.Cfg) checks.Check {
|
||||
return &check{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, to register our check in the `checkregistry` package, we need to add it to the `ProvideService` function. First, we need to verify that the services we need are available in the `ProvideService` function, and if not, add them. Then, we need to add our check to the `Checks` slice.
|
||||
|
||||
```go
|
||||
func ProvideService(..., cfg *setting.Cfg,
|
||||
) *Service {
|
||||
return &Service{
|
||||
...
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Checks() []checks.Check {
|
||||
return []checks.Check{
|
||||
...
|
||||
configchecks.New(s.cfg),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Complete the Check Implementation
|
||||
|
||||
Now that we have our check registered, we can implement the rest of the check logic.
|
||||
|
||||
#### 3.1. Implement the `Items` and `Item` methods
|
||||
|
||||
The `Items` method is used to return a list of items that the check will be run on (e.g. all data sources, all plugins, etc). The `Item` method is used to return a single item by its ID.
|
||||
|
||||
These functions can return `any` type, we will convert them to the expected type in the step `Run` method.
|
||||
|
||||
In our case, we will implement the `Items` method to return a list of config sections that we want to check. The `Item` method will return a single config section by its name.
|
||||
|
||||
```go
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
return []any{"security.secret_key"}, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
return id, nil
|
||||
}
|
||||
```
|
||||
|
||||
Check other checks for examples of how to implement these methods in more interesting ways.
|
||||
|
||||
#### 3.2. Implement the `Init` method
|
||||
|
||||
The `Init` method is used to initialize the check. It is called when the check is first created. It should be used to gather any information that is needed to run the check and for the steps to have some shared context.
|
||||
|
||||
In our case, we don't need to do anything special so we can just return `nil`.
|
||||
|
||||
```go
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
One more interesting example is the `plugincheck`, where we gather all the plugin information from GCOM and store it in the check struct.
|
||||
|
||||
### 4. Implement Steps
|
||||
|
||||
Like the `Check` interface, each `Step` needs to return some information (metadata) about the step, which will be used to populate the UI, and the logic to `Run` the step.
|
||||
|
||||
In our example, we will implement a step that will check if the `security.secret_key` is set correctly. In case it's not correct, we recommend the user to follow the documentation.
|
||||
|
||||
Check [`security_config_step.go`](./pkg/app/checks/configchecks/security_config_step.go) for the full implementation.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Error Handling**: In general, avoid returning errors for known issues, these will mark the check report as failed and the UI will render an error page. Only unexpected errors should be returned.
|
||||
|
||||
2. **Type Safety**: Use type assertions to ensure you're working with the correct type of item.
|
||||
|
||||
3. **Severity Levels**: Use appropriate severity levels:
|
||||
|
||||
- `CheckReportFailureSeverityHigh`: For critical issues that need immediate attention
|
||||
- `CheckReportFailureSeverityLow`: For non-critical issues that can be addressed later
|
||||
|
||||
4. **Resolution Links**: Provide helpful links in the `CheckErrorLink` slice to help users resolve issues.
|
||||
|
||||
5. **Logging**: Use the provided logger to log important information and errors.
|
||||
|
||||
## Testing
|
||||
|
||||
Create tests for your check and its steps to ensure they work as expected. Test both successful and failure scenarios.
|
||||
@@ -1,12 +1,12 @@
|
||||
module github.com/grafana/grafana/apps/advisor
|
||||
|
||||
go 1.24.6
|
||||
go 1.25.3
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.39.0
|
||||
k8s.io/apimachinery v0.33.1
|
||||
github.com/grafana/grafana-app-sdk v0.31.0
|
||||
k8s.io/apimachinery v0.32.1
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -27,13 +27,12 @@ require (
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -50,40 +49,40 @@ require (
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.33.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.1 // indirect
|
||||
k8s.io/client-go v0.33.1 // indirect
|
||||
k8s.io/api v0.32.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.32.1 // indirect
|
||||
k8s.io/client-go v0.32.1 // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -18,7 +18,6 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
|
||||
github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
|
||||
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
|
||||
github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -36,8 +35,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@@ -46,7 +43,6 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@@ -58,21 +54,14 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb h1:oTl2j6/4miQUYmXANp2pBuYCWA5f8NVYFfCWpczpFso=
|
||||
github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
|
||||
github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw=
|
||||
github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk=
|
||||
github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs=
|
||||
github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI=
|
||||
github.com/grafana/grafana-app-sdk v0.39.0/go.mod h1:xRyBQOttgWTc3tGe9pI0upnpEPVhzALf7Mh/61O4zyY=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.35.0/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
@@ -124,14 +113,12 @@ 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/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
||||
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg=
|
||||
@@ -159,39 +146,31 @@ go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||
@@ -202,7 +181,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -213,24 +192,22 @@ golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -239,11 +216,13 @@ golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
@@ -251,7 +230,7 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -266,29 +245,21 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
@@ -302,35 +273,24 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
|
||||
k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
|
||||
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw=
|
||||
k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
|
||||
k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
|
||||
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
|
||||
k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
|
||||
k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA=
|
||||
k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
|
||||
k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
|
||||
k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
|
||||
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -36,12 +36,8 @@ check: {
|
||||
stepID: string
|
||||
// Human readable identifier of the item that failed
|
||||
item: string
|
||||
// ID of the item that failed
|
||||
itemID: string
|
||||
// Links to actions that can be taken to resolve the failure
|
||||
links: [...#ErrorLink]
|
||||
// More information about the failure, not meant to be displayed to the user. Used for LLM suggestions.
|
||||
moreInfo?: string
|
||||
}
|
||||
#Report: {
|
||||
// Number of elements analyzed
|
||||
|
||||
@@ -23,12 +23,8 @@ type CheckReportFailure struct {
|
||||
StepID string `json:"stepID"`
|
||||
// Human readable identifier of the item that failed
|
||||
Item string `json:"item"`
|
||||
// ID of the item that failed
|
||||
ItemID string `json:"itemID"`
|
||||
// Links to actions that can be taken to resolve the failure
|
||||
Links []CheckErrorLink `json:"links"`
|
||||
// More information about the failure, not meant to be displayed to the user. Used for LLM suggestions.
|
||||
MoreInfo *string `json:"moreInfo,omitempty"`
|
||||
}
|
||||
|
||||
// NewCheckReportFailure creates a new CheckReportFailure object.
|
||||
|
||||
@@ -183,14 +183,6 @@ func schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref common.ReferenceCal
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"itemID": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "ID of the item that failed",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"links": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Links to actions that can be taken to resolve the failure",
|
||||
@@ -205,15 +197,8 @@ func schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref common.ReferenceCal
|
||||
},
|
||||
},
|
||||
},
|
||||
"moreInfo": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "More information about the failure, not meant to be displayed to the user. Used for LLM suggestions.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"severity", "stepID", "item", "itemID", "links"},
|
||||
Required: []string{"severity", "stepID", "item", "links"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"failures":{"description":"List of failures","items":{"properties":{"item":{"description":"Human readable identifier of the item that failed","type":"string"},"itemID":{"description":"ID of the item that failed","type":"string"},"links":{"description":"Links to actions that can be taken to resolve the failure","items":{"properties":{"message":{"description":"Human readable error message","type":"string"},"url":{"description":"URL to a page with more information about the error","type":"string"}},"required":["url","message"],"type":"object"},"type":"array"},"moreInfo":{"description":"More information about the failure, not meant to be displayed to the user. Used for LLM suggestions.","type":"string"},"severity":{"description":"Severity of the failure","enum":["high","low"],"type":"string"},"stepID":{"description":"Step ID that the failure is associated with","type":"string"}},"required":["severity","stepID","item","itemID","links"],"type":"object"},"type":"array"}},"required":["count","failures"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"failures":{"description":"List of failures","items":{"properties":{"item":{"description":"Human readable identifier of the item that failed","type":"string"},"links":{"description":"Links to actions that can be taken to resolve the failure","items":{"properties":{"message":{"description":"Human readable error message","type":"string"},"url":{"description":"URL to a page with more information about the error","type":"string"}},"required":["url","message"],"type":"object"},"type":"array"},"severity":{"description":"Severity of the failure","enum":["high","low"],"type":"string"},"stepID":{"description":"Step ID that the failure is associated with","type":"string"}},"required":["severity","stepID","item","links"],"type":"object"},"type":"array"}},"required":["count","failures"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
versionSchemaCheckv0alpha1 app.VersionSchema
|
||||
_ = json.Unmarshal(rawSchemaCheckv0alpha1, &versionSchemaCheckv0alpha1)
|
||||
rawSchemaCheckTypev0alpha1 = []byte(`{"spec":{"properties":{"name":{"type":"string"},"steps":{"items":{"properties":{"description":{"type":"string"},"resolution":{"type":"string"},"stepID":{"type":"string"},"title":{"type":"string"}},"required":["title","description","stepID","resolution"],"type":"object"},"type":"array"}},"required":["name","steps"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/k8s"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
@@ -15,7 +14,9 @@ import (
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checkscheduler"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checktyperegisterer"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func New(cfg app.Config) (app.App, error) {
|
||||
@@ -25,7 +26,7 @@ func New(cfg app.Config) (app.App, error) {
|
||||
return nil, fmt.Errorf("invalid config type")
|
||||
}
|
||||
checkRegistry := specificConfig.CheckRegistry
|
||||
log := logging.DefaultLogger.With("app", "advisor.app")
|
||||
log := log.New("advisor.app")
|
||||
|
||||
// Prepare storage client
|
||||
clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{})
|
||||
@@ -33,10 +34,6 @@ func New(cfg app.Config) (app.App, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typesClient, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize checks
|
||||
checkMap := map[string]checks.Check{}
|
||||
@@ -49,7 +46,7 @@ func New(cfg app.Config) (app.App, error) {
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
log.WithContext(ctx).Error("Informer processing error", "error", err)
|
||||
klog.ErrorS(err, "Informer processing error")
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
@@ -64,33 +61,16 @@ func New(cfg app.Config) (app.App, error) {
|
||||
}
|
||||
if req.Action == resource.AdmissionActionCreate {
|
||||
go func() {
|
||||
logger := log.WithContext(ctx).With("check", check.ID())
|
||||
logger.Debug("Processing check", "namespace", req.Object.GetNamespace())
|
||||
log.Debug("Processing check", "namespace", req.Object.GetNamespace())
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
logger.Error("Error getting requester", "error", err)
|
||||
log.Error("Error getting requester", "error", err)
|
||||
return
|
||||
}
|
||||
ctx = identity.WithServiceIdentityContext(context.WithoutCancel(ctx), requester.GetOrgID())
|
||||
err = processCheck(ctx, logger, client, typesClient, req.Object, check)
|
||||
ctx = identity.WithRequester(context.Background(), requester)
|
||||
err = processCheck(ctx, client, req.Object, check)
|
||||
if err != nil {
|
||||
logger.Error("Error processing check", "error", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if req.Action == resource.AdmissionActionUpdate {
|
||||
go func() {
|
||||
logger := log.WithContext(ctx).With("check", check.ID())
|
||||
logger.Debug("Updating check", "namespace", req.Object.GetNamespace(), "name", req.Object.GetName())
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
logger.Error("Error getting requester", "error", err)
|
||||
return
|
||||
}
|
||||
ctx = identity.WithServiceIdentityContext(context.WithoutCancel(ctx), requester.GetOrgID())
|
||||
err = processCheckRetry(ctx, logger, client, typesClient, req.Object, check)
|
||||
if err != nil {
|
||||
logger.Error("Error processing check retry", "error", err)
|
||||
log.Error("Error processing check", "error", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -116,14 +96,14 @@ func New(cfg app.Config) (app.App, error) {
|
||||
}
|
||||
|
||||
// Save check types as resources
|
||||
ctr, err := checktyperegisterer.New(cfg, log)
|
||||
ctr, err := checktyperegisterer.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.AddRunnable(ctr)
|
||||
|
||||
// Start scheduler
|
||||
csch, err := checkscheduler.New(cfg, log)
|
||||
csch, err := checkscheduler.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,21 +2,16 @@ package checkregistry
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/authchecks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/configchecks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/datasourcecheck"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/instancechecks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/plugincheck"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type CheckService interface {
|
||||
@@ -29,22 +24,15 @@ type Service struct {
|
||||
pluginContextProvider *plugincontext.Provider
|
||||
pluginClient plugins.Client
|
||||
pluginRepo repo.Service
|
||||
pluginErrorResolver plugins.ErrorResolver
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginPreinstall pluginchecker.Preinstall
|
||||
pluginPreinstall plugininstaller.Preinstall
|
||||
managedPlugins managedplugins.Manager
|
||||
provisionedPlugins provisionedplugins.Manager
|
||||
ssoSettingsSvc ssosettings.Service
|
||||
GrafanaVersion string
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore pluginstore.Store,
|
||||
pluginContextProvider *plugincontext.Provider, pluginClient plugins.Client,
|
||||
updateChecker pluginchecker.PluginUpdateChecker,
|
||||
pluginRepo repo.Service, pluginPreinstall pluginchecker.Preinstall, managedPlugins managedplugins.Manager,
|
||||
provisionedPlugins provisionedplugins.Manager, ssoSettingsSvc ssosettings.Service, cfg *setting.Cfg,
|
||||
pluginErrorResolver plugins.ErrorResolver,
|
||||
pluginRepo repo.Service, pluginPreinstall plugininstaller.Preinstall, managedPlugins managedplugins.Manager,
|
||||
provisionedPlugins provisionedplugins.Manager,
|
||||
) *Service {
|
||||
return &Service{
|
||||
datasourceSvc: datasourceSvc,
|
||||
@@ -52,14 +40,9 @@ func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore plu
|
||||
pluginContextProvider: pluginContextProvider,
|
||||
pluginClient: pluginClient,
|
||||
pluginRepo: pluginRepo,
|
||||
pluginErrorResolver: pluginErrorResolver,
|
||||
updateChecker: updateChecker,
|
||||
pluginPreinstall: pluginPreinstall,
|
||||
managedPlugins: managedPlugins,
|
||||
provisionedPlugins: provisionedPlugins,
|
||||
ssoSettingsSvc: ssoSettingsSvc,
|
||||
GrafanaVersion: cfg.BuildVersion,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,18 +54,14 @@ func (s *Service) Checks() []checks.Check {
|
||||
s.pluginContextProvider,
|
||||
s.pluginClient,
|
||||
s.pluginRepo,
|
||||
s.GrafanaVersion,
|
||||
),
|
||||
plugincheck.New(
|
||||
s.pluginStore,
|
||||
s.pluginRepo,
|
||||
s.updateChecker,
|
||||
s.pluginErrorResolver,
|
||||
s.GrafanaVersion,
|
||||
s.pluginPreinstall,
|
||||
s.managedPlugins,
|
||||
s.provisionedPlugins,
|
||||
),
|
||||
authchecks.New(s.ssoSettingsSvc),
|
||||
configchecks.New(s.cfg),
|
||||
instancechecks.New(s.cfg),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package authchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||
)
|
||||
|
||||
const (
|
||||
CheckID = "ssosetting"
|
||||
)
|
||||
|
||||
var _ checks.Check = (*check)(nil)
|
||||
|
||||
type check struct {
|
||||
ssoSettingsService ssosettings.Service
|
||||
}
|
||||
|
||||
func New(ssoSettingsService ssosettings.Service) checks.Check {
|
||||
return &check{
|
||||
ssoSettingsService: ssoSettingsService,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return CheckID
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "SSO setting"
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
return []checks.Step{
|
||||
&listFormatValidation{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
ssoSettings, err := c.ssoSettingsService.ListWithRedactedSecrets(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list SSO settings: %w", err)
|
||||
}
|
||||
res := make([]any, len(ssoSettings))
|
||||
for i, ds := range ssoSettings {
|
||||
res[i] = ds
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
ssoSetting, err := c.ssoSettingsService.GetForProviderWithRedactedSecrets(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, ssosettings.ErrNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ssoSetting, nil
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package authchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingstests" // Correct import path for the mock
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheck_ID(t *testing.T) {
|
||||
mockService := ssosettingstests.NewMockService(t)
|
||||
c := New(mockService)
|
||||
require.Equal(t, CheckID, c.ID())
|
||||
}
|
||||
|
||||
func TestCheck_Items(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
mockService := ssosettingstests.NewMockService(t)
|
||||
expectedSettings := []*models.SSOSettings{
|
||||
{Provider: "google", Settings: map[string]any{"client_id": "id1"}},
|
||||
{Provider: "github", Settings: map[string]any{"client_id": "id2"}},
|
||||
}
|
||||
mockService.On("ListWithRedactedSecrets", ctx).Return(expectedSettings, nil)
|
||||
|
||||
c := New(mockService)
|
||||
items, err := c.Items(ctx)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Len(t, items, len(expectedSettings))
|
||||
|
||||
actualSettings := make([]*models.SSOSettings, len(items))
|
||||
for i, item := range items {
|
||||
setting, ok := item.(*models.SSOSettings)
|
||||
require.True(t, ok, "Item should be of type *models.SSOSettings")
|
||||
actualSettings[i] = setting
|
||||
}
|
||||
require.Equal(t, expectedSettings, actualSettings)
|
||||
})
|
||||
|
||||
t.Run("Error from service", func(t *testing.T) {
|
||||
mockService := ssosettingstests.NewMockService(t)
|
||||
expectedErr := errors.New("database error")
|
||||
mockService.On("ListWithRedactedSecrets", ctx).Return(nil, expectedErr)
|
||||
|
||||
c := New(mockService)
|
||||
items, err := c.Items(ctx)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, items)
|
||||
require.ErrorContains(t, err, "failed to list SSO settings")
|
||||
require.ErrorIs(t, err, expectedErr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheck_Item(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
providerID := "google"
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
mockService := ssosettingstests.NewMockService(t)
|
||||
expectedSetting := &models.SSOSettings{
|
||||
Provider: providerID,
|
||||
Settings: map[string]any{"client_id": "id1"},
|
||||
}
|
||||
mockService.On("GetForProviderWithRedactedSecrets", ctx, providerID).Return(expectedSetting, nil)
|
||||
|
||||
c := New(mockService)
|
||||
item, err := c.Item(ctx, providerID)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, item)
|
||||
actualSetting, ok := item.(*models.SSOSettings)
|
||||
require.True(t, ok, "Item should be of type *models.SSOSettings")
|
||||
require.Equal(t, expectedSetting, actualSetting)
|
||||
})
|
||||
|
||||
t.Run("Error from service", func(t *testing.T) {
|
||||
mockService := ssosettingstests.NewMockService(t)
|
||||
expectedErr := errors.New("not found")
|
||||
mockService.On("GetForProviderWithRedactedSecrets", ctx, providerID).Return(nil, expectedErr)
|
||||
|
||||
c := New(mockService)
|
||||
item, err := c.Item(ctx, providerID)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, item)
|
||||
require.ErrorIs(t, err, expectedErr)
|
||||
})
|
||||
|
||||
t.Run("should return nil when sso settings are not found", func(t *testing.T) {
|
||||
mockService := ssosettingstests.NewMockService(t)
|
||||
mockService.On("GetForProviderWithRedactedSecrets", ctx, providerID).Return(nil, ssosettings.ErrNotFound)
|
||||
|
||||
c := New(mockService)
|
||||
item, err := c.Item(ctx, providerID)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, item)
|
||||
})
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package authchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const ListFormatValidationStepID = "sso-list-format-validation"
|
||||
|
||||
// listSettingKeys defines the SSO setting keys that expect a list format (space-separated, comma-separated or JSON array).
|
||||
var listSettingKeys = []string{
|
||||
"allowed_domains",
|
||||
"allowed_groups",
|
||||
"allowed_organizations",
|
||||
"role_values_none",
|
||||
"role_values_grafana_admin",
|
||||
"role_values_admin",
|
||||
"role_values_editor",
|
||||
"role_values_viewer",
|
||||
}
|
||||
|
||||
var _ checks.Step = (*listFormatValidation)(nil)
|
||||
|
||||
// listFormatValidation checks if the specified list parameters in SSO settings are in a valid format.
|
||||
type listFormatValidation struct{}
|
||||
|
||||
func (s *listFormatValidation) ID() string {
|
||||
return ListFormatValidationStepID
|
||||
}
|
||||
|
||||
func (s *listFormatValidation) Title() string {
|
||||
return "SSO List Setting Format Validation"
|
||||
}
|
||||
|
||||
func (s *listFormatValidation) Description() string {
|
||||
return "Checks if list configs in SSO settings are in a valid list format (space-separated, comma-separated or JSON array)."
|
||||
}
|
||||
|
||||
func (s *listFormatValidation) Resolution() string {
|
||||
return "Configure the relevant SSO setting using a valid format, like space-separated (\"opt1 opt2\"), comma-separated values (\"opt1, opt2\") or JSON array format ([\"opt1\", \"opt2\"])."
|
||||
}
|
||||
|
||||
func (s *listFormatValidation) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, objToCheck any) ([]advisor.CheckReportFailure, error) {
|
||||
setting, ok := objToCheck.(*models.SSOSettings)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", objToCheck)
|
||||
}
|
||||
|
||||
reportIssues := make([]advisor.CheckReportFailure, 0, 3)
|
||||
|
||||
for _, settingKey := range listSettingKeys {
|
||||
currentSettingValue, exists := setting.Settings[settingKey]
|
||||
if !exists || currentSettingValue == nil {
|
||||
// If the setting is not present or nil, its format is considered valid (or non-applicable).
|
||||
continue
|
||||
}
|
||||
|
||||
currentSettingStr, ok := currentSettingValue.(string)
|
||||
if !ok {
|
||||
reportIssues = append(reportIssues, checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
fmt.Sprintf("%s - Invalid type for '%s': expected string, got %T", login.GetAuthProviderLabel(setting.Provider), settingKey, currentSettingValue),
|
||||
setting.Provider,
|
||||
s.generateLinks(setting.Provider),
|
||||
))
|
||||
}
|
||||
|
||||
if currentSettingStr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := util.SplitStringWithError(currentSettingStr)
|
||||
if err != nil {
|
||||
reportIssues = append(reportIssues, checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
fmt.Sprintf("%s - Invalid format for '%s': %s", login.GetAuthProviderLabel(setting.Provider), settingKey, currentSettingStr),
|
||||
setting.Provider,
|
||||
s.generateLinks(setting.Provider),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return reportIssues, nil
|
||||
}
|
||||
|
||||
func (s *listFormatValidation) generateLinks(provider string) []advisor.CheckErrorLink {
|
||||
return []advisor.CheckErrorLink{
|
||||
{
|
||||
Url: fmt.Sprintf("https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/%s", strings.ReplaceAll(provider, "_", "-")),
|
||||
Message: "Check the documentation",
|
||||
},
|
||||
{
|
||||
Url: fmt.Sprintf("/admin/authentication/%s", provider),
|
||||
Message: "Configure provider",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
package authchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestListFormatValidation_Methods(t *testing.T) {
|
||||
validator := &listFormatValidation{}
|
||||
|
||||
require.Equal(t, ListFormatValidationStepID, validator.ID())
|
||||
require.Equal(t, "SSO List Setting Format Validation", validator.Title())
|
||||
require.Equal(t, "Checks if list configs in SSO settings are in a valid list format (space-separated, comma-separated or JSON array).", validator.Description())
|
||||
require.Equal(t, "Configure the relevant SSO setting using a valid format, like space-separated (\"opt1 opt2\"), comma-separated values (\"opt1, opt2\") or JSON array format ([\"opt1\", \"opt2\"]).", validator.Resolution())
|
||||
}
|
||||
|
||||
func TestListFormatValidation_Run(t *testing.T) {
|
||||
validator := &listFormatValidation{}
|
||||
ctx := context.Background()
|
||||
spec := &advisor.CheckSpec{}
|
||||
provider := "generic_oauth"
|
||||
providerLabel := login.GetAuthProviderLabel(provider)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
objToCheck any
|
||||
expectedError string
|
||||
expectedFailures []advisor.CheckReportFailure
|
||||
}{
|
||||
{
|
||||
name: "invalid object type",
|
||||
objToCheck: struct{}{},
|
||||
expectedError: "invalid item type struct {}",
|
||||
},
|
||||
{
|
||||
name: "no relevant settings exist",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{"other_setting": "value"},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is nil",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": nil,
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is empty string",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": "",
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is empty JSON array",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": "[]",
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is valid (comma-separated)",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": "group1, group2",
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is valid (JSON array)",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_domains": `["domain1.com", "domain2.com"]`,
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is valid (space-separated)",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": "group1 group2",
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "one setting exists and is not a string",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": 123,
|
||||
},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid type for '%s': expected string, got %T", providerLabel, "allowed_groups", 123),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
)},
|
||||
},
|
||||
{
|
||||
name: "one setting exists and has invalid format (bad JSON)",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_groups": `["group1", "group2"`,
|
||||
},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid format for '%s': %s", providerLabel, "allowed_groups", `["group1", "group2"`),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
)},
|
||||
},
|
||||
{
|
||||
name: "multiple settings exist, first one is invalid (type)",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_domains": 123,
|
||||
"allowed_groups": "group1, group2",
|
||||
},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid type for '%s': expected string, got %T", providerLabel, "allowed_domains", 123),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
)},
|
||||
},
|
||||
{
|
||||
name: "multiple settings exist, second one is invalid (format)",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_domains": "domain1.com",
|
||||
"allowed_groups": `["group1",`,
|
||||
},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid format for '%s': %s", providerLabel, "allowed_groups", `["group1",`),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
)},
|
||||
},
|
||||
{
|
||||
name: "all settings exist and are valid",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_domains": "d1.com, d2.com",
|
||||
"allowed_groups": `["g1", "g2"]`,
|
||||
"allowed_organizations": "org1",
|
||||
"role_values_none": "None",
|
||||
"role_values_grafana_admin": "GrafanaAdmin",
|
||||
"role_values_admin": "Admin",
|
||||
"role_values_editor": "Editor",
|
||||
"role_values_viewer": "Viewer",
|
||||
},
|
||||
},
|
||||
expectedFailures: nil,
|
||||
},
|
||||
{
|
||||
name: "returns multiple errors",
|
||||
objToCheck: &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Settings: map[string]any{
|
||||
"allowed_domains": `[\"group1\", \"group2\"]`,
|
||||
"allowed_groups": `["group1", "group2"`,
|
||||
"role_values_editor": `["group1-`,
|
||||
},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{
|
||||
checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid format for '%s': %v", providerLabel, "allowed_domains", `[\"group1\", \"group2\"]`),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
),
|
||||
checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid format for '%s': %s", providerLabel, "allowed_groups", `["group1", "group2"`),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
),
|
||||
checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
ListFormatValidationStepID,
|
||||
fmt.Sprintf("%s - Invalid format for '%s': %v", providerLabel, "role_values_editor", `["group1-`),
|
||||
provider,
|
||||
generateExpectedLinks(provider),
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
failures, err := validator.Run(ctx, logging.DefaultLogger, spec, tt.objToCheck)
|
||||
|
||||
if tt.expectedError != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tt.expectedError)
|
||||
require.Nil(t, failures)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
if len(tt.expectedFailures) > 0 {
|
||||
require.NotNil(t, failures)
|
||||
require.Len(t, failures, len(tt.expectedFailures))
|
||||
require.ElementsMatch(t, tt.expectedFailures, failures)
|
||||
} else {
|
||||
require.Empty(t, failures, "Expected no failure reports, but got some: %+v", failures)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateExpectedLinks(provider string) []advisor.CheckErrorLink {
|
||||
return []advisor.CheckErrorLink{
|
||||
{
|
||||
Url: fmt.Sprintf("https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/%s", strings.ReplaceAll(provider, "_", "-")),
|
||||
Message: "Check the documentation",
|
||||
},
|
||||
{
|
||||
Url: fmt.Sprintf("/admin/authentication/%s", provider),
|
||||
Message: "Configure provider",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package configchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var _ checks.Check = (*check)(nil)
|
||||
|
||||
type check struct {
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func New(cfg *setting.Cfg) checks.Check {
|
||||
return &check{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "config setting"
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
return []any{"security.secret_key"}, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
return []checks.Step{
|
||||
&securityConfigStep{
|
||||
securitySection: c.cfg.SectionWithEnvOverrides("security"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package configchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
// nolint:gosec // Defined in defaults.ini originally
|
||||
defaultSecretKey = "SW2YcwTIb9zpOOhoPsMm"
|
||||
)
|
||||
|
||||
type securityConfigStep struct {
|
||||
securitySection *setting.DynamicSection
|
||||
}
|
||||
|
||||
func (s *securityConfigStep) Title() string {
|
||||
return "Security config check"
|
||||
}
|
||||
|
||||
func (s *securityConfigStep) Description() string {
|
||||
return "Check if the Grafana security config is set correctly."
|
||||
}
|
||||
|
||||
func (s *securityConfigStep) Resolution() string {
|
||||
return "Follow the documentation for each element."
|
||||
}
|
||||
|
||||
func (s *securityConfigStep) ID() string {
|
||||
return "security_config"
|
||||
}
|
||||
|
||||
func (s *securityConfigStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
itemPath, ok := it.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
items := strings.Split(itemPath, ".")
|
||||
if len(items) != 2 {
|
||||
// Not interested in this item
|
||||
return nil, nil
|
||||
}
|
||||
section, key := items[0], items[1]
|
||||
if section != "security" {
|
||||
// Only interested in security section
|
||||
return nil, nil
|
||||
}
|
||||
if key == "secret_key" {
|
||||
secretKey := s.securitySection.Key("secret_key").Value()
|
||||
if secretKey == defaultSecretKey {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
"secret_key",
|
||||
itemPath,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Avoid default value",
|
||||
Url: "https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-database-encryption/",
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package configchecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSecurityConfigStepSuccess(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
step := &securityConfigStep{
|
||||
securitySection: cfg.SectionWithEnvOverrides("security"),
|
||||
}
|
||||
|
||||
errs, err := step.Run(context.Background(), logging.DefaultLogger, nil, "security.secret_key")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, errs, 0)
|
||||
}
|
||||
|
||||
func TestSecurityConfigStepFailure(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SectionWithEnvOverrides("security").Key("secret_key").SetValue(defaultSecretKey)
|
||||
step := &securityConfigStep{
|
||||
securitySection: cfg.SectionWithEnvOverrides("security"),
|
||||
}
|
||||
|
||||
errs, err := step.Run(context.Background(), logging.DefaultLogger, nil, "security.secret_key")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, errs, 1)
|
||||
}
|
||||
@@ -3,14 +3,18 @@ package datasourcecheck
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,7 +30,7 @@ type check struct {
|
||||
PluginContextProvider pluginContextProvider
|
||||
PluginClient plugins.Client
|
||||
PluginRepo repo.Service
|
||||
GrafanaVersion string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -35,7 +39,6 @@ func New(
|
||||
pluginContextProvider pluginContextProvider,
|
||||
pluginClient plugins.Client,
|
||||
pluginRepo repo.Service,
|
||||
grafanaVersion string,
|
||||
) checks.Check {
|
||||
return &check{
|
||||
DatasourceSvc: datasourceSvc,
|
||||
@@ -43,7 +46,7 @@ func New(
|
||||
PluginContextProvider: pluginContextProvider,
|
||||
PluginClient: pluginClient,
|
||||
PluginRepo: pluginRepo,
|
||||
GrafanaVersion: grafanaVersion,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,52 +62,191 @@ func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds, err := c.DatasourceSvc.GetDataSource(ctx, &datasources.GetDataSourceQuery{
|
||||
UID: id,
|
||||
OrgID: requester.GetOrgID(),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
// The data source does not exist, skip the check
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return CheckID
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "data source"
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
return []checks.Step{
|
||||
&uidValidationStep{},
|
||||
&healthCheckStep{
|
||||
PluginContextProvider: c.PluginContextProvider,
|
||||
PluginClient: c.PluginClient,
|
||||
log: c.log,
|
||||
},
|
||||
&missingPluginStep{
|
||||
PluginStore: c.PluginStore,
|
||||
PluginRepo: c.PluginRepo,
|
||||
GrafanaVersion: c.GrafanaVersion,
|
||||
PluginStore: c.PluginStore,
|
||||
PluginRepo: c.PluginRepo,
|
||||
log: c.log,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type uidValidationStep struct{}
|
||||
|
||||
func (s *uidValidationStep) ID() string {
|
||||
return UIDValidationStepID
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Title() string {
|
||||
return "UID validation"
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Description() string {
|
||||
return "Checks if the UID of a data source is valid."
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Resolution() string {
|
||||
return "Check the <a href='https://grafana.com/docs/grafana/latest/upgrade-guide/upgrade-v11.2/#grafana-data-source-uid-format-enforcement'" +
|
||||
"target=_blank>documentation</a> for more information or delete the data source and create a new one."
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
|
||||
ds, ok := i.(*datasources.DataSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
// Data source UID validation
|
||||
err := util.ValidateUID(ds.UID)
|
||||
if err != nil {
|
||||
return checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
fmt.Sprintf("%s (%s)", ds.Name, ds.UID),
|
||||
[]advisor.CheckErrorLink{},
|
||||
), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type healthCheckStep struct {
|
||||
PluginContextProvider pluginContextProvider
|
||||
PluginClient plugins.Client
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Title() string {
|
||||
return "Health check"
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Description() string {
|
||||
return "Checks if a data sources is healthy."
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Resolution() string {
|
||||
return "Go to the data source configuration page and address the issues reported."
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) ID() string {
|
||||
return HealthCheckStepID
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
|
||||
ds, ok := i.(*datasources.DataSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
|
||||
// Health check execution
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pCtx, err := s.PluginContextProvider.GetWithDataSource(ctx, ds.Type, requester, ds)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugins.ErrPluginNotRegistered) {
|
||||
// The plugin is not installed, handle this in the missing plugin step
|
||||
return nil, nil
|
||||
}
|
||||
// Unable to check health check
|
||||
s.log.Error("Failed to get plugin context", "datasource_uid", ds.UID, "error", err)
|
||||
return nil, nil
|
||||
}
|
||||
req := &backend.CheckHealthRequest{
|
||||
PluginContext: pCtx,
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
resp, err := s.PluginClient.CheckHealth(ctx, req)
|
||||
if err != nil || resp.Status != backend.HealthStatusOk {
|
||||
if err != nil {
|
||||
s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "error", err)
|
||||
if errors.Is(err, plugins.ErrMethodNotImplemented) || errors.Is(err, plugins.ErrPluginUnavailable) {
|
||||
// The plugin does not support backend health checks
|
||||
return nil, nil
|
||||
}
|
||||
} else {
|
||||
s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "status", resp.Status, "message", resp.Message)
|
||||
}
|
||||
return checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
ds.Name,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Fix me",
|
||||
Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID),
|
||||
},
|
||||
},
|
||||
), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type missingPluginStep struct {
|
||||
PluginStore pluginstore.Store
|
||||
PluginRepo repo.Service
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Title() string {
|
||||
return "Missing plugin check"
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Description() string {
|
||||
return "Checks if the plugin associated with the data source is installed."
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Resolution() string {
|
||||
return "Delete the datasource or install the plugin."
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) ID() string {
|
||||
return MissingPluginStepID
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
|
||||
ds, ok := i.(*datasources.DataSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
|
||||
_, exists := s.PluginStore.Plugin(ctx, ds.Type)
|
||||
if !exists {
|
||||
links := []advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Delete data source",
|
||||
Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID),
|
||||
},
|
||||
}
|
||||
_, err := s.PluginRepo.PluginInfo(ctx, ds.Type)
|
||||
if err == nil {
|
||||
// Plugin is available in the repo
|
||||
links = append(links, advisor.CheckErrorLink{
|
||||
Message: "Install plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", ds.Type),
|
||||
})
|
||||
}
|
||||
// The plugin is not installed
|
||||
return checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
ds.Name,
|
||||
links,
|
||||
), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type pluginContextProvider interface {
|
||||
GetWithDataSource(ctx context.Context, pluginID string, user identity.Requester, ds *datasources.DataSource) (backend.PluginContext, error)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package datasourcecheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
@@ -25,18 +26,14 @@ func runChecks(check *check) ([]advisor.CheckReportFailure, error) {
|
||||
}
|
||||
|
||||
failures := []advisor.CheckReportFailure{}
|
||||
err = check.Init(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, step := range check.Steps() {
|
||||
for _, item := range items {
|
||||
stepFailures, err := step.Run(ctx, logging.DefaultLogger, &advisor.CheckSpec{}, item)
|
||||
stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(stepFailures) > 0 {
|
||||
failures = append(failures, stepFailures...)
|
||||
if stepFailures != nil {
|
||||
failures = append(failures, *stepFailures)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,10 +51,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{
|
||||
{ID: 1, Slug: "prometheus", Status: "active"},
|
||||
{ID: 2, Slug: "mysql", Status: "active"},
|
||||
}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: true}
|
||||
mockPluginStore := &MockPluginStore{exists: true}
|
||||
|
||||
check := &check{
|
||||
@@ -66,6 +60,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
@@ -81,9 +76,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{
|
||||
{ID: 1, Slug: "prometheus", Status: "active"},
|
||||
}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: true}
|
||||
mockPluginStore := &MockPluginStore{exists: true}
|
||||
|
||||
check := &check{
|
||||
@@ -92,6 +85,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
@@ -107,10 +101,8 @@ func TestCheck_Run(t *testing.T) {
|
||||
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: "test message"}}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{
|
||||
{ID: 1, Slug: "prometheus", Status: "active"},
|
||||
}}
|
||||
mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusError}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: true}
|
||||
mockPluginStore := &MockPluginStore{exists: true}
|
||||
|
||||
check := &check{
|
||||
@@ -119,13 +111,13 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, failures, 1)
|
||||
assert.Equal(t, "health-check", failures[0].StepID)
|
||||
assert.Contains(t, *failures[0].MoreInfo, "test message")
|
||||
})
|
||||
|
||||
t.Run("should skip health check when plugin does not support backend health checks", func(t *testing.T) {
|
||||
@@ -135,9 +127,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{err: plugins.ErrMethodNotImplemented}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{
|
||||
{ID: 1, Slug: "prometheus", Status: "active"},
|
||||
}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: true}
|
||||
mockPluginStore := &MockPluginStore{exists: true}
|
||||
|
||||
check := &check{
|
||||
@@ -146,6 +136,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
@@ -160,9 +151,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{err: plugins.ErrPluginNotRegistered}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{
|
||||
{ID: 1, Slug: "prometheus", Status: "active"},
|
||||
}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: true}
|
||||
mockPluginStore := &MockPluginStore{exists: true}
|
||||
|
||||
check := &check{
|
||||
@@ -171,6 +160,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
@@ -186,9 +176,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{
|
||||
{ID: 1, Slug: "prometheus", Status: "active"},
|
||||
}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: true}
|
||||
mockPluginStore := &MockPluginStore{exists: false}
|
||||
|
||||
check := &check{
|
||||
@@ -197,6 +185,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
@@ -213,7 +202,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
|
||||
mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
|
||||
mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}}
|
||||
mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{}}
|
||||
mockPluginRepo := &MockPluginRepo{exists: false}
|
||||
mockPluginStore := &MockPluginStore{exists: false}
|
||||
|
||||
check := &check{
|
||||
@@ -222,6 +211,7 @@ func TestCheck_Run(t *testing.T) {
|
||||
PluginClient: mockPluginClient,
|
||||
PluginRepo: mockPluginRepo,
|
||||
PluginStore: mockPluginStore,
|
||||
log: log.New("advisor.datasourcecheck"),
|
||||
}
|
||||
|
||||
failures, err := runChecks(check)
|
||||
@@ -232,19 +222,6 @@ func TestCheck_Run(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheck_Item(t *testing.T) {
|
||||
t.Run("should return nil when datasource is not found", func(t *testing.T) {
|
||||
mockDatasourceSvc := &MockDatasourceSvc{dss: []*datasources.DataSource{}}
|
||||
check := &check{
|
||||
DatasourceSvc: mockDatasourceSvc,
|
||||
}
|
||||
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{})
|
||||
item, err := check.Item(ctx, "invalid-uid")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, item)
|
||||
})
|
||||
}
|
||||
|
||||
type MockDatasourceSvc struct {
|
||||
datasources.DataSourceService
|
||||
|
||||
@@ -255,13 +232,6 @@ func (m *MockDatasourceSvc) GetAllDataSources(context.Context, *datasources.GetA
|
||||
return m.dss, nil
|
||||
}
|
||||
|
||||
func (m *MockDatasourceSvc) GetDataSource(context.Context, *datasources.GetDataSourceQuery) (*datasources.DataSource, error) {
|
||||
if len(m.dss) == 0 {
|
||||
return nil, datasources.ErrDataSourceNotFound
|
||||
}
|
||||
return m.dss[0], nil
|
||||
}
|
||||
|
||||
type MockPluginContextProvider struct {
|
||||
pCtx backend.PluginContext
|
||||
}
|
||||
@@ -294,9 +264,12 @@ func (m *MockPluginStore) Plugin(context.Context, string) (pluginstore.Plugin, b
|
||||
type MockPluginRepo struct {
|
||||
repo.Service
|
||||
|
||||
plugins []repo.PluginInfo
|
||||
exists bool
|
||||
}
|
||||
|
||||
func (m *MockPluginRepo) GetPluginsInfo(context.Context, repo.GetPluginsInfoOptions, repo.CompatOpts) ([]repo.PluginInfo, error) {
|
||||
return m.plugins, nil
|
||||
func (m *MockPluginRepo) PluginInfo(context.Context, string) (*repo.PluginInfo, error) {
|
||||
if !m.exists {
|
||||
return nil, errors.New("plugin not found")
|
||||
}
|
||||
return &repo.PluginInfo{}, nil
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package datasourcecheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
)
|
||||
|
||||
type healthCheckStep struct {
|
||||
PluginContextProvider pluginContextProvider
|
||||
PluginClient plugins.Client
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Title() string {
|
||||
return "Health check"
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Description() string {
|
||||
return "Checks if a data source is healthy."
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Resolution() string {
|
||||
return "Go to the data source configuration page and address the issues reported."
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) ID() string {
|
||||
return HealthCheckStepID
|
||||
}
|
||||
|
||||
func (s *healthCheckStep) Run(ctx context.Context, log logging.Logger, obj *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) {
|
||||
ds, ok := i.(*datasources.DataSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
|
||||
// Health check execution
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pCtx, err := s.PluginContextProvider.GetWithDataSource(ctx, ds.Type, requester, ds)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugins.ErrPluginNotRegistered) {
|
||||
// The plugin is not installed, handle this in the missing plugin step
|
||||
return nil, nil
|
||||
}
|
||||
// Unable to check health check
|
||||
log.Error("Failed to get plugin context", "datasource_uid", ds.UID, "error", err)
|
||||
return nil, nil
|
||||
}
|
||||
req := &backend.CheckHealthRequest{
|
||||
PluginContext: pCtx,
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
resp, err := s.PluginClient.CheckHealth(ctx, req)
|
||||
if err != nil || (resp != nil && resp.Status != backend.HealthStatusOk) {
|
||||
if err != nil {
|
||||
log.Debug("Failed to check health", "datasource_uid", ds.UID, "error", err)
|
||||
if errors.Is(err, plugins.ErrMethodNotImplemented) || errors.Is(err, plugins.ErrPluginUnavailable) {
|
||||
// The plugin does not support backend health checks
|
||||
return nil, nil
|
||||
}
|
||||
} else {
|
||||
log.Debug("Failed to check health", "datasource_uid", ds.UID, "status", resp.Status, "message", resp.Message)
|
||||
}
|
||||
moreInfo := ""
|
||||
if resp != nil {
|
||||
moreInfo = fmt.Sprintf("Status: %s\nMessage: %s\nJSONDetails: %s", resp.Status, resp.Message, resp.JSONDetails)
|
||||
}
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailureWithMoreInfo(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
ds.Name,
|
||||
ds.UID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Fix me",
|
||||
Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID),
|
||||
},
|
||||
},
|
||||
moreInfo,
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package datasourcecheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sysruntime "runtime"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
)
|
||||
|
||||
type missingPluginStep struct {
|
||||
PluginStore pluginstore.Store
|
||||
PluginRepo repo.Service
|
||||
GrafanaVersion string
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Title() string {
|
||||
return "Missing plugin check"
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Description() string {
|
||||
return "Checks if the plugin associated with the data source is installed and available."
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Resolution() string {
|
||||
return "Delete the datasource or install the plugin."
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) ID() string {
|
||||
return MissingPluginStepID
|
||||
}
|
||||
|
||||
func (s *missingPluginStep) Run(ctx context.Context, log logging.Logger, obj *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) {
|
||||
ds, ok := i.(*datasources.DataSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
|
||||
_, exists := s.PluginStore.Plugin(ctx, ds.Type)
|
||||
if !exists {
|
||||
links := []advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Delete data source",
|
||||
Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID),
|
||||
},
|
||||
}
|
||||
plugins, err := s.PluginRepo.GetPluginsInfo(ctx, repo.GetPluginsInfoOptions{
|
||||
IncludeDeprecated: true,
|
||||
Plugins: []string{ds.Type},
|
||||
}, repo.NewCompatOpts(s.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(plugins) > 0 {
|
||||
// Plugin is available in the repo
|
||||
links = append(links, advisor.CheckErrorLink{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", ds.Type),
|
||||
})
|
||||
}
|
||||
// The plugin is not installed
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailureWithMoreInfo(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
ds.Name,
|
||||
ds.UID,
|
||||
links,
|
||||
fmt.Sprintf("Plugin: %s", ds.Type),
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package datasourcecheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type uidValidationStep struct{}
|
||||
|
||||
func (s *uidValidationStep) ID() string {
|
||||
return UIDValidationStepID
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Title() string {
|
||||
return "UID validation"
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Description() string {
|
||||
return "Checks if the UID of a data source is valid."
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Resolution() string {
|
||||
return "Check the <a href='https://grafana.com/docs/grafana/latest/upgrade-guide/upgrade-v11.2/#grafana-data-source-uid-format-enforcement'" +
|
||||
"target=_blank>documentation</a> for more information or delete the data source and create a new one."
|
||||
}
|
||||
|
||||
func (s *uidValidationStep) Run(ctx context.Context, log logging.Logger, obj *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) {
|
||||
ds, ok := i.(*datasources.DataSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
// Data source UID validation
|
||||
err := util.ValidateUID(ds.UID)
|
||||
if err != nil {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
fmt.Sprintf("%s (%s)", ds.Name, ds.UID),
|
||||
ds.UID,
|
||||
[]advisor.CheckErrorLink{},
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package checks
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
)
|
||||
|
||||
@@ -11,17 +10,10 @@ import (
|
||||
type Check interface {
|
||||
// ID returns the unique identifier of the check
|
||||
ID() string
|
||||
// Name returns the human-readable name of the check
|
||||
Name() string
|
||||
// Item returns the item that will be checked
|
||||
Item(ctx context.Context, id string) (any, error)
|
||||
// Items returns the list of items that will be checked
|
||||
Items(ctx context.Context) ([]any, error)
|
||||
// Steps returns the list of steps that will be executed
|
||||
Steps() []Step
|
||||
// Init initializes the check. It's called before running the steps and should be idempotent.
|
||||
// The result should not be cached, it should be initialized from scratch.
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
// Step is a single step in a check, including its metadata
|
||||
@@ -35,5 +27,5 @@ type Step interface {
|
||||
// Explains the action that needs to be taken to resolve the issue
|
||||
Resolution() string
|
||||
// Run executes the step for an item and returns a report
|
||||
Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error)
|
||||
Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error)
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var _ checks.Check = (*check)(nil)
|
||||
|
||||
type check struct {
|
||||
cfg *setting.Cfg
|
||||
isCloudInstance bool
|
||||
}
|
||||
|
||||
func New(cfg *setting.Cfg) checks.Check {
|
||||
return &check{
|
||||
cfg: cfg,
|
||||
isCloudInstance: cfg.StackID != "",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return "instance"
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "instance attribute"
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
if c.isCloudInstance {
|
||||
return []any{
|
||||
pinnedVersion, // pinned version check
|
||||
}, nil
|
||||
}
|
||||
|
||||
return []any{
|
||||
outOfSupportVersion, // out of support version check
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
// If running in cloud, we need to check if the version is pinned
|
||||
if c.isCloudInstance {
|
||||
return []checks.Step{
|
||||
&pinnedVersionStep{
|
||||
BuildBranch: c.cfg.BuildBranch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If running in self-managed, we need to check if the version is out of support
|
||||
return []checks.Step{
|
||||
&outOfSupportVersionStep{
|
||||
GrafanaVersion: c.cfg.BuildVersion,
|
||||
BuildDate: time.Unix(c.cfg.BuildStamp, 0).UTC(),
|
||||
ghClient: github.NewClient(nil).Repositories,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheck_IsCloudInstance_Logic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
stackID string
|
||||
expectedCloud bool
|
||||
expectedStepID string
|
||||
expectedItemID string
|
||||
}{
|
||||
{
|
||||
name: "empty stackID should run out of support check",
|
||||
stackID: "",
|
||||
expectedCloud: false,
|
||||
expectedStepID: outOfSupportVersion,
|
||||
expectedItemID: outOfSupportVersion,
|
||||
},
|
||||
{
|
||||
name: "non-empty stackID should run pinned version check",
|
||||
stackID: "12345",
|
||||
expectedCloud: true,
|
||||
expectedStepID: pinnedVersion,
|
||||
expectedItemID: pinnedVersion,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &setting.Cfg{
|
||||
StackID: tt.stackID,
|
||||
BuildBranch: "v10.0.0",
|
||||
BuildVersion: "10.0.0",
|
||||
BuildStamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
check := New(cfg).(*check)
|
||||
|
||||
// Verify isCloudInstance field is set correctly
|
||||
assert.Equal(t, tt.expectedCloud, check.isCloudInstance)
|
||||
|
||||
// Verify Steps() returns the correct step type
|
||||
steps := check.Steps()
|
||||
require.Len(t, steps, 1)
|
||||
assert.Equal(t, tt.expectedStepID, steps[0].ID())
|
||||
|
||||
// Verify Items() returns the correct item type
|
||||
items, err := check.Items(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, items, 1)
|
||||
assert.Equal(t, tt.expectedItemID, items[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
)
|
||||
|
||||
var _ checks.Step = &outOfSupportVersionStep{}
|
||||
|
||||
const (
|
||||
outOfSupportVersion = "out_of_support_version"
|
||||
)
|
||||
|
||||
type outOfSupportVersionStep struct {
|
||||
GrafanaVersion string
|
||||
BuildDate time.Time
|
||||
ghClient gitHubClient
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Title() string {
|
||||
return "Grafana version check"
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Description() string {
|
||||
return "Check if the current Grafana version is out of support."
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Resolution() string {
|
||||
return "Out of support versions will not receive security updates or bug fixes. " +
|
||||
"Upgrade to a more recent version. " +
|
||||
"<a href='https://grafana.com/docs/grafana/latest/upgrade-guide/when-to-upgrade/#what-to-know-about-version-support' target='_blank'>" +
|
||||
"Learn more about version support</a>."
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) ID() string {
|
||||
return outOfSupportVersion
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
item, ok := it.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
if item != outOfSupportVersion {
|
||||
// Not interested in this item
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If the build date is less than 9 months old, it's supported
|
||||
if s.BuildDate.After(time.Now().AddDate(0, -9, 0)) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If the build date is older than 15 months, it's not supported
|
||||
isOutOfSupport := false
|
||||
if s.BuildDate.Before(time.Now().AddDate(0, -15, 0)) {
|
||||
isOutOfSupport = true
|
||||
} else {
|
||||
// In other cases, we need to check if the version is out of support.
|
||||
// Minor versions are generally supported for 9 months but the last
|
||||
// minor version for a major version is supported for 15 months.
|
||||
// This is the case when we keep doing releases for old minor versions (e.g. 11.x when 12.x is out).
|
||||
// https://grafana.com/docs/grafana/latest/upgrade-guide/when-to-upgrade/#what-to-know-about-version-support
|
||||
|
||||
// Parse the current version using semver
|
||||
version, err := semver.NewVersion(s.GrafanaVersion)
|
||||
if err != nil {
|
||||
// Unable to parse the version so unable to check if it's out of support
|
||||
log.Error("Unable to parse the version", "version", s.GrafanaVersion, "error", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// To verify if the current version is the latest minor version for this major version,
|
||||
// we try to get the version vX.Y+1.0 from GitHub
|
||||
nextMinorVersion := fmt.Sprintf("v%d.%d.0", version.Major(), version.Minor()+1)
|
||||
_, res, err := s.ghClient.GetReleaseByTag(ctx, "grafana", "grafana", nextMinorVersion)
|
||||
if err != nil && res.StatusCode != 404 {
|
||||
// Unable to get the release info so unable to check if it's out of support
|
||||
log.Error("Unable to get the release info", "version", s.GrafanaVersion, "error", err.Error())
|
||||
return nil, nil
|
||||
}
|
||||
latestMinor := false
|
||||
if res.StatusCode == 404 {
|
||||
// No next minor version found, so the current version is the latest minor version
|
||||
latestMinor = true
|
||||
}
|
||||
|
||||
// Calculate support end date
|
||||
supportMonths := 9
|
||||
if latestMinor {
|
||||
supportMonths = 15 // Extended support for last minor version
|
||||
}
|
||||
|
||||
supportEndDate := s.BuildDate.AddDate(0, supportMonths, 0)
|
||||
if time.Now().After(supportEndDate) {
|
||||
isOutOfSupport = true
|
||||
}
|
||||
}
|
||||
|
||||
if isOutOfSupport {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
fmt.Sprintf("Grafana version %s is out of support", s.GrafanaVersion),
|
||||
outOfSupportVersion,
|
||||
[]advisor.CheckErrorLink{},
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// gitHubClient is a mockable interface for the GitHub client
|
||||
type gitHubClient interface {
|
||||
GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*github.RepositoryRelease, *github.Response, error)
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOutOfSupportVersionStep(t *testing.T) {
|
||||
now := time.Now()
|
||||
oldDate := now.AddDate(0, -16, 0) // 16 months ago
|
||||
recentDate := now.AddDate(0, -8, 0) // 8 months ago
|
||||
oldDateButSupportedIfLatestMinor := now.AddDate(0, -10, 0) // 10 months ago
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
step *outOfSupportVersionStep
|
||||
input any
|
||||
wantFailure bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should return error for invalid input type",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: now,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: 123, // invalid type
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "should return nil for non-out-of-support item",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: now,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: "other_item",
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for recent build date",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "11.5.0",
|
||||
BuildDate: recentDate,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return failure for old build date",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDate,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for invalid version format",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "invalid-version",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for GitHub API error",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{
|
||||
errCode: http.StatusTooManyRequests,
|
||||
},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return failure for non-latest minor version",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{
|
||||
release: &github.RepositoryRelease{},
|
||||
},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for latest minor version",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{
|
||||
errCode: http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
failures, err := tt.step.Run(context.Background(), logging.DefaultLogger, &advisor.CheckSpec{}, tt.input)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.wantFailure {
|
||||
require.Len(t, failures, 1)
|
||||
assert.Equal(t, advisor.CheckReportFailureSeverityHigh, failures[0].Severity)
|
||||
assert.Equal(t, outOfSupportVersion, failures[0].ItemID)
|
||||
assert.Equal(t, "Grafana version "+tt.step.GrafanaVersion+" is out of support", failures[0].Item)
|
||||
} else {
|
||||
assert.Empty(t, failures)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockGitHubClient struct {
|
||||
release *github.RepositoryRelease
|
||||
errCode int
|
||||
}
|
||||
|
||||
func (m *mockGitHubClient) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*github.RepositoryRelease, *github.Response, error) {
|
||||
if m.errCode != 0 {
|
||||
return nil, &github.Response{Response: &http.Response{StatusCode: m.errCode}}, assert.AnError
|
||||
}
|
||||
return m.release, &github.Response{Response: &http.Response{StatusCode: http.StatusOK}}, nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
)
|
||||
|
||||
var _ checks.Step = &pinnedVersionStep{}
|
||||
|
||||
const (
|
||||
pinnedVersion = "pinned_version"
|
||||
)
|
||||
|
||||
type pinnedVersionStep struct {
|
||||
BuildBranch string
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Title() string {
|
||||
return "Grafana Cloud version check"
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Description() string {
|
||||
return "Check if the Grafana version is pinned."
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Resolution() string {
|
||||
return "You may miss out on security updates and bug fixes if you use a pinned version. " +
|
||||
"Contact your Grafana administrator and open a " +
|
||||
"<a href='https://grafana.com/profile/org#support' target=_blank>support ticket</a> to help you get unpinned."
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) ID() string {
|
||||
return pinnedVersion
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
item, ok := it.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
if item != pinnedVersion {
|
||||
// Not interested in this item
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if s.BuildBranch == "HEAD" {
|
||||
// Not a pinned version
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
"Grafana version is pinned",
|
||||
"pinned_version",
|
||||
[]advisor.CheckErrorLink{},
|
||||
)}, nil
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPinnedVersionStep(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
step *pinnedVersionStep
|
||||
input any
|
||||
wantFailure bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should return error for invalid input type",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "test-branch",
|
||||
},
|
||||
input: 123, // invalid type
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "should return nil for non-pinned version item",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "test-branch",
|
||||
},
|
||||
input: "other_item",
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for HEAD build branch",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "HEAD",
|
||||
},
|
||||
input: pinnedVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return failure for pinned version",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "v9.5.0",
|
||||
},
|
||||
input: pinnedVersion,
|
||||
wantFailure: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
failures, err := tt.step.Run(context.Background(), logging.DefaultLogger, &advisor.CheckSpec{}, tt.input)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.wantFailure {
|
||||
require.Len(t, failures, 1)
|
||||
assert.Equal(t, advisor.CheckReportFailureSeverityLow, failures[0].Severity)
|
||||
assert.Equal(t, "pinned_version", failures[0].ItemID)
|
||||
assert.Equal(t, "Grafana version is pinned", failures[0].Item)
|
||||
} else {
|
||||
assert.Empty(t, failures)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,135 +2,234 @@ package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sysruntime "runtime"
|
||||
"slices"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
|
||||
)
|
||||
|
||||
const (
|
||||
CheckID = "plugin"
|
||||
CheckID = "plugin"
|
||||
DeprecationStepID = "deprecation"
|
||||
UpdateStepID = "update"
|
||||
)
|
||||
|
||||
func New(
|
||||
pluginStore pluginstore.Store,
|
||||
pluginRepo repo.Service,
|
||||
updateChecker pluginchecker.PluginUpdateChecker,
|
||||
pluginErrorResolver plugins.ErrorResolver,
|
||||
grafanaVersion string,
|
||||
pluginPreinstall plugininstaller.Preinstall,
|
||||
managedPlugins managedplugins.Manager,
|
||||
provisionedPlugins provisionedplugins.Manager,
|
||||
) checks.Check {
|
||||
return &check{
|
||||
PluginStore: pluginStore,
|
||||
PluginRepo: pluginRepo,
|
||||
GrafanaVersion: grafanaVersion,
|
||||
updateChecker: updateChecker,
|
||||
pluginErrorResolver: pluginErrorResolver,
|
||||
PluginStore: pluginStore,
|
||||
PluginRepo: pluginRepo,
|
||||
PluginPreinstall: pluginPreinstall,
|
||||
ManagedPlugins: managedPlugins,
|
||||
ProvisionedPlugins: provisionedPlugins,
|
||||
}
|
||||
}
|
||||
|
||||
type check struct {
|
||||
PluginStore pluginstore.Store
|
||||
PluginRepo repo.Service
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginErrorResolver plugins.ErrorResolver
|
||||
GrafanaVersion string
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
PluginStore pluginstore.Store
|
||||
PluginRepo repo.Service
|
||||
PluginPreinstall plugininstaller.Preinstall
|
||||
ManagedPlugins managedplugins.Manager
|
||||
ProvisionedPlugins provisionedplugins.Manager
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return CheckID
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "plugin"
|
||||
}
|
||||
|
||||
type pluginItem struct {
|
||||
Plugin *pluginstore.Plugin
|
||||
Err *plugins.Error
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
ps := c.PluginStore.Plugins(ctx)
|
||||
resMap := map[string]*pluginItem{}
|
||||
for _, p := range ps {
|
||||
resMap[p.ID] = &pluginItem{
|
||||
Plugin: &p,
|
||||
Err: c.pluginErrorResolver.PluginError(ctx, p.ID),
|
||||
}
|
||||
}
|
||||
|
||||
// Plugins with errors are not added to the plugin store but
|
||||
// we still want to show them in the check results so we add them to the map
|
||||
pluginErrors := c.pluginErrorResolver.PluginErrors(ctx)
|
||||
for _, e := range pluginErrors {
|
||||
if _, exists := resMap[e.PluginID]; exists {
|
||||
resMap[e.PluginID].Err = e
|
||||
} else {
|
||||
resMap[e.PluginID] = &pluginItem{
|
||||
Plugin: nil,
|
||||
Err: e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := make([]any, 0, len(resMap))
|
||||
for _, p := range resMap {
|
||||
res = append(res, p)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
p, exists := c.PluginStore.Plugin(ctx, id)
|
||||
if !exists {
|
||||
return nil, nil
|
||||
}
|
||||
return &pluginItem{
|
||||
Plugin: &p,
|
||||
Err: c.pluginErrorResolver.PluginError(ctx, p.ID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
compatOpts := repo.NewCompatOpts(c.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH)
|
||||
ps := c.PluginStore.Plugins(ctx)
|
||||
pluginIDs := make([]string, len(ps))
|
||||
res := make([]any, len(ps))
|
||||
for i, p := range ps {
|
||||
pluginIDs[i] = p.ID
|
||||
res[i] = p
|
||||
}
|
||||
plugins, err := c.PluginRepo.GetPluginsInfo(ctx, repo.GetPluginsInfoOptions{
|
||||
IncludeDeprecated: true,
|
||||
Plugins: pluginIDs,
|
||||
}, compatOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.pluginIndex = make(map[string]repo.PluginInfo)
|
||||
for _, p := range plugins {
|
||||
c.pluginIndex[p.Slug] = p
|
||||
}
|
||||
return nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
return []checks.Step{
|
||||
&deprecationStep{
|
||||
GrafanaVersion: c.GrafanaVersion,
|
||||
updateChecker: c.updateChecker,
|
||||
pluginIndex: c.pluginIndex,
|
||||
PluginRepo: c.PluginRepo,
|
||||
},
|
||||
&updateStep{
|
||||
GrafanaVersion: c.GrafanaVersion,
|
||||
updateChecker: c.updateChecker,
|
||||
pluginIndex: c.pluginIndex,
|
||||
},
|
||||
&unsignedStep{
|
||||
pluginIndex: c.pluginIndex,
|
||||
PluginRepo: c.PluginRepo,
|
||||
PluginPreinstall: c.PluginPreinstall,
|
||||
ManagedPlugins: c.ManagedPlugins,
|
||||
ProvisionedPlugins: c.ProvisionedPlugins,
|
||||
log: log.New("advisor.check.plugin.update"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type deprecationStep struct {
|
||||
PluginRepo repo.Service
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Title() string {
|
||||
return "Deprecation check"
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Description() string {
|
||||
return "Check if any installed plugins are deprecated."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Resolution() string {
|
||||
return "Check the <a href='https://grafana.com/legal/plugin-deprecation/#a-plugin-i-use-is-deprecated-what-should-i-do'" +
|
||||
"target=_blank>documentation</a> for recommended steps or delete the plugin."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) ID() string {
|
||||
return DeprecationStepID
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Run(ctx context.Context, _ *advisor.CheckSpec, it any) (*advisor.CheckReportFailure, error) {
|
||||
p, ok := it.(pluginstore.Plugin)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
|
||||
// Skip if it's a core plugin
|
||||
if p.IsCorePlugin() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin is deprecated
|
||||
i, err := s.PluginRepo.PluginInfo(ctx, p.ID)
|
||||
if err != nil {
|
||||
// Unable to check deprecation status
|
||||
return nil, nil
|
||||
}
|
||||
if i.Status == "deprecated" {
|
||||
return checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Admin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
},
|
||||
},
|
||||
), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type updateStep struct {
|
||||
PluginRepo repo.Service
|
||||
PluginPreinstall plugininstaller.Preinstall
|
||||
ManagedPlugins managedplugins.Manager
|
||||
ProvisionedPlugins provisionedplugins.Manager
|
||||
provisionedPlugins []string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (s *updateStep) Title() string {
|
||||
return "Update check"
|
||||
}
|
||||
|
||||
func (s *updateStep) Description() string {
|
||||
return "Checks if an installed plugins has a newer version available."
|
||||
}
|
||||
|
||||
func (s *updateStep) Resolution() string {
|
||||
return "Go to the plugin admin page and upgrade to the latest version."
|
||||
}
|
||||
|
||||
func (s *updateStep) ID() string {
|
||||
return UpdateStepID
|
||||
}
|
||||
|
||||
func (s *updateStep) Run(ctx context.Context, _ *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
|
||||
p, ok := i.(pluginstore.Plugin)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
|
||||
// Skip if it's a core plugin
|
||||
if p.IsCorePlugin() {
|
||||
s.log.Debug("Skipping core plugin", "plugin", p.ID)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Skip if it's managed or pinned
|
||||
if s.isManaged(ctx, p.ID) || s.PluginPreinstall.IsPinned(p.ID) {
|
||||
s.log.Debug("Skipping managed or pinned plugin", "plugin", p.ID)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Skip if it's provisioned
|
||||
if s.isProvisioned(ctx, p.ID) {
|
||||
s.log.Debug("Skipping provisioned plugin", "plugin", p.ID)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin has a newer version available
|
||||
compatOpts := repo.NewCompatOpts(services.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH)
|
||||
info, err := s.PluginRepo.GetPluginArchiveInfo(ctx, p.ID, "", compatOpts)
|
||||
if err != nil {
|
||||
// Unable to check updates
|
||||
return nil, nil
|
||||
}
|
||||
if hasUpdate(p, info) {
|
||||
return checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Upgrade",
|
||||
Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID),
|
||||
},
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func hasUpdate(current pluginstore.Plugin, latest *repo.PluginArchiveInfo) bool {
|
||||
// If both versions are semver-valid, compare them
|
||||
v1, err1 := semver.NewVersion(current.Info.Version)
|
||||
v2, err2 := semver.NewVersion(latest.Version)
|
||||
if err1 == nil && err2 == nil {
|
||||
return v1.LessThan(v2)
|
||||
}
|
||||
// In other case, assume that a different latest version will always be newer
|
||||
return current.Info.Version != latest.Version
|
||||
}
|
||||
|
||||
func (s *updateStep) isManaged(ctx context.Context, pluginID string) bool {
|
||||
for _, managedPlugin := range s.ManagedPlugins.ManagedPlugins(ctx) {
|
||||
if managedPlugin == pluginID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *updateStep) isProvisioned(ctx context.Context, pluginID string) bool {
|
||||
if s.provisionedPlugins == nil {
|
||||
var err error
|
||||
s.provisionedPlugins, err = s.ProvisionedPlugins.ProvisionedPlugins(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return slices.Contains(s.provisionedPlugins, pluginID)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -19,11 +18,11 @@ func TestRun(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
plugins []pluginstore.Plugin
|
||||
pluginInfo []repo.PluginInfo
|
||||
pluginInfo map[string]*repo.PluginInfo
|
||||
pluginArchives map[string]*repo.PluginArchiveInfo
|
||||
pluginPreinstalled []string
|
||||
pluginManaged []string
|
||||
pluginProvisioned []string
|
||||
pluginErrors []*plugins.Error
|
||||
expectedFailures []advisor.CheckReportFailure
|
||||
}{
|
||||
{
|
||||
@@ -34,21 +33,23 @@ func TestRun(t *testing.T) {
|
||||
{
|
||||
name: "Deprecated plugin",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin1", Name: "Plugin 1", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
{JSONData: plugins.JSONData{ID: "plugin1", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "deprecated", Slug: "plugin1", Version: "1.0.0"},
|
||||
pluginInfo: map[string]*repo.PluginInfo{
|
||||
"plugin1": {Status: "deprecated"},
|
||||
},
|
||||
pluginArchives: map[string]*repo.PluginArchiveInfo{
|
||||
"plugin1": {Version: "1.0.0"},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: "deprecation",
|
||||
Item: "Plugin 1",
|
||||
ItemID: "plugin1",
|
||||
Item: "plugin1",
|
||||
Links: []advisor.CheckErrorLink{
|
||||
{
|
||||
Url: "/plugins/plugin1",
|
||||
Message: "View plugin",
|
||||
Message: "Admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -57,17 +58,19 @@ func TestRun(t *testing.T) {
|
||||
{
|
||||
name: "Plugin with update",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin2", Name: "Plugin 2", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
{JSONData: plugins.JSONData{ID: "plugin2", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "active", Slug: "plugin2", Version: "1.1.0"},
|
||||
pluginInfo: map[string]*repo.PluginInfo{
|
||||
"plugin2": {Status: "active"},
|
||||
},
|
||||
pluginArchives: map[string]*repo.PluginArchiveInfo{
|
||||
"plugin2": {Version: "1.1.0"},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: "update",
|
||||
Item: "Plugin 2",
|
||||
ItemID: "plugin2",
|
||||
Item: "plugin2",
|
||||
Links: []advisor.CheckErrorLink{
|
||||
{
|
||||
Url: "/plugins/plugin2?page=version-history",
|
||||
@@ -80,20 +83,38 @@ func TestRun(t *testing.T) {
|
||||
{
|
||||
name: "Plugin with update (non semver)",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin2", Name: "Plugin 2", Info: plugins.Info{Version: "alpha"}}},
|
||||
{JSONData: plugins.JSONData{ID: "plugin2", Info: plugins.Info{Version: "alpha"}}},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "active", Slug: "plugin2", Version: "beta"},
|
||||
pluginInfo: map[string]*repo.PluginInfo{
|
||||
"plugin2": {Status: "active"},
|
||||
},
|
||||
pluginArchives: map[string]*repo.PluginArchiveInfo{
|
||||
"plugin2": {Version: "beta"},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: "update",
|
||||
Item: "plugin2",
|
||||
Links: []advisor.CheckErrorLink{
|
||||
{
|
||||
Url: "/plugins/plugin2?page=version-history",
|
||||
Message: "Upgrade",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{}, // Cannot be compared because the version is not semver
|
||||
},
|
||||
{
|
||||
name: "Plugin pinned",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin3", Name: "Plugin 3", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
{JSONData: plugins.JSONData{ID: "plugin3", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "deprecated", Slug: "plugin3"}, // This should be ignored
|
||||
pluginInfo: map[string]*repo.PluginInfo{
|
||||
"plugin3": {Status: "active"},
|
||||
},
|
||||
pluginArchives: map[string]*repo.PluginArchiveInfo{
|
||||
"plugin3": {Version: "1.1.0"},
|
||||
},
|
||||
pluginPreinstalled: []string{"plugin3"},
|
||||
expectedFailures: []advisor.CheckReportFailure{},
|
||||
@@ -101,10 +122,13 @@ func TestRun(t *testing.T) {
|
||||
{
|
||||
name: "Managed plugin",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin4", Name: "Plugin 4", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
{JSONData: plugins.JSONData{ID: "plugin4", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "deprecated", Slug: "plugin4", Version: "1.1.0"}, // This should be ignored
|
||||
pluginInfo: map[string]*repo.PluginInfo{
|
||||
"plugin4": {Status: "active"},
|
||||
},
|
||||
pluginArchives: map[string]*repo.PluginArchiveInfo{
|
||||
"plugin4": {Version: "1.1.0"},
|
||||
},
|
||||
pluginManaged: []string{"plugin4"},
|
||||
expectedFailures: []advisor.CheckReportFailure{},
|
||||
@@ -112,125 +136,50 @@ func TestRun(t *testing.T) {
|
||||
{
|
||||
name: "Provisioned plugin",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin5", Name: "Plugin 5", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
{JSONData: plugins.JSONData{ID: "plugin5", Info: plugins.Info{Version: "1.0.0"}}},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "deprecated", Slug: "plugin5", Version: "1.1.0"}, // This should be ignored
|
||||
pluginInfo: map[string]*repo.PluginInfo{
|
||||
"plugin5": {Status: "active"},
|
||||
},
|
||||
pluginArchives: map[string]*repo.PluginArchiveInfo{
|
||||
"plugin5": {Version: "1.1.0"},
|
||||
},
|
||||
pluginProvisioned: []string{"plugin5"},
|
||||
expectedFailures: []advisor.CheckReportFailure{},
|
||||
},
|
||||
{
|
||||
name: "Invalid signatures",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin6", Name: "Plugin 6", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusInvalid},
|
||||
{JSONData: plugins.JSONData{ID: "plugin7", Name: "Plugin 7", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusModified},
|
||||
{JSONData: plugins.JSONData{ID: "plugin8", Name: "Plugin 8", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusUnsigned},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "active", Slug: "plugin6", Version: "1.0.0"},
|
||||
{Status: "active", Slug: "plugin7", Version: "1.0.0"},
|
||||
{Status: "active", Slug: "plugin8", Version: "1.0.0"},
|
||||
},
|
||||
pluginErrors: []*plugins.Error{
|
||||
{PluginID: "plugin9", ErrorCode: plugins.ErrorCodeSignatureInvalid},
|
||||
{PluginID: "plugin10", ErrorCode: plugins.ErrorCodeSignatureModified},
|
||||
{PluginID: "plugin11", ErrorCode: plugins.ErrorCodeSignatureMissing},
|
||||
{PluginID: "plugin12", ErrorCode: plugins.ErrorCodeFailedBackendStart}, // This should be ignored atm
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "Plugin 6",
|
||||
ItemID: "plugin6",
|
||||
Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin6", Message: "View plugin"}},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "Plugin 7",
|
||||
ItemID: "plugin7",
|
||||
Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin7", Message: "View plugin"}},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "Plugin 8",
|
||||
ItemID: "plugin8",
|
||||
Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin8", Message: "View plugin"}},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "plugin9",
|
||||
ItemID: "plugin9",
|
||||
Links: []advisor.CheckErrorLink{},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "plugin10",
|
||||
ItemID: "plugin10",
|
||||
Links: []advisor.CheckErrorLink{},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "plugin11",
|
||||
ItemID: "plugin11",
|
||||
Links: []advisor.CheckErrorLink{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pluginStore := &mockPluginStore{plugins: tt.plugins}
|
||||
pluginRepo := &mockPluginRepo{
|
||||
pluginInfo: tt.pluginInfo,
|
||||
pluginInfo: tt.pluginInfo,
|
||||
pluginArchiveInfo: tt.pluginArchives,
|
||||
}
|
||||
pluginPreinstall := &mockPluginPreinstall{pinned: tt.pluginPreinstalled}
|
||||
managedPlugins := &mockManagedPlugins{managed: tt.pluginManaged}
|
||||
provisionedPlugins := &mockProvisionedPlugins{provisioned: tt.pluginProvisioned}
|
||||
updateChecker := pluginchecker.ProvideService(managedPlugins, provisionedPlugins, pluginPreinstall)
|
||||
pluginErrorResolver := &mockPluginErrorResolver{pluginErrors: tt.pluginErrors}
|
||||
check := New(pluginStore, pluginRepo, updateChecker, pluginErrorResolver, "12.0.0")
|
||||
check := New(pluginStore, pluginRepo, pluginPreinstall, managedPlugins, provisionedPlugins)
|
||||
|
||||
items, err := check.Items(context.Background())
|
||||
assert.NoError(t, err)
|
||||
failures := []advisor.CheckReportFailure{}
|
||||
err = check.Init(context.Background())
|
||||
assert.NoError(t, err)
|
||||
for _, step := range check.Steps() {
|
||||
for _, item := range items {
|
||||
stepFailures, err := step.Run(context.Background(), logging.DefaultLogger, &advisor.CheckSpec{}, item)
|
||||
stepFailures, err := step.Run(context.Background(), &advisor.CheckSpec{}, item)
|
||||
assert.NoError(t, err)
|
||||
if len(stepFailures) > 0 {
|
||||
failures = append(failures, stepFailures...)
|
||||
if stepFailures != nil {
|
||||
failures = append(failures, *stepFailures)
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(tt.plugins)+len(tt.pluginErrors), len(items))
|
||||
assert.ElementsMatch(t, tt.expectedFailures, failures)
|
||||
assert.Equal(t, len(tt.plugins), len(items))
|
||||
assert.Equal(t, tt.expectedFailures, failures)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheck_Item(t *testing.T) {
|
||||
t.Run("should return nil when plugin is not found", func(t *testing.T) {
|
||||
pluginStore := &mockPluginStore{plugins: []pluginstore.Plugin{}}
|
||||
check := &check{
|
||||
PluginStore: pluginStore,
|
||||
}
|
||||
item, err := check.Item(context.Background(), "invalid-uid")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, item)
|
||||
})
|
||||
}
|
||||
|
||||
type mockPluginStore struct {
|
||||
pluginstore.Store
|
||||
plugins []pluginstore.Plugin
|
||||
@@ -240,24 +189,22 @@ func (m *mockPluginStore) Plugins(ctx context.Context, t ...plugins.Type) []plug
|
||||
return m.plugins
|
||||
}
|
||||
|
||||
func (m *mockPluginStore) Plugin(ctx context.Context, id string) (pluginstore.Plugin, bool) {
|
||||
if len(m.plugins) == 0 {
|
||||
return pluginstore.Plugin{}, false
|
||||
}
|
||||
return m.plugins[0], true
|
||||
}
|
||||
|
||||
type mockPluginRepo struct {
|
||||
repo.Service
|
||||
pluginInfo []repo.PluginInfo
|
||||
pluginInfo map[string]*repo.PluginInfo
|
||||
pluginArchiveInfo map[string]*repo.PluginArchiveInfo
|
||||
}
|
||||
|
||||
func (m *mockPluginRepo) GetPluginsInfo(ctx context.Context, options repo.GetPluginsInfoOptions, compatOpts repo.CompatOpts) ([]repo.PluginInfo, error) {
|
||||
return m.pluginInfo, nil
|
||||
func (m *mockPluginRepo) PluginInfo(ctx context.Context, id string) (*repo.PluginInfo, error) {
|
||||
return m.pluginInfo[id], nil
|
||||
}
|
||||
|
||||
func (m *mockPluginRepo) GetPluginArchiveInfo(ctx context.Context, id, version string, opts repo.CompatOpts) (*repo.PluginArchiveInfo, error) {
|
||||
return m.pluginArchiveInfo[id], nil
|
||||
}
|
||||
|
||||
type mockPluginPreinstall struct {
|
||||
pluginchecker.Preinstall
|
||||
plugininstaller.Preinstall
|
||||
pinned []string
|
||||
}
|
||||
|
||||
@@ -287,21 +234,3 @@ type mockProvisionedPlugins struct {
|
||||
func (m *mockProvisionedPlugins) ProvisionedPlugins(ctx context.Context) ([]string, error) {
|
||||
return m.provisioned, nil
|
||||
}
|
||||
|
||||
type mockPluginErrorResolver struct {
|
||||
plugins.ErrorResolver
|
||||
pluginErrors []*plugins.Error
|
||||
}
|
||||
|
||||
func (m *mockPluginErrorResolver) PluginErrors(ctx context.Context) []*plugins.Error {
|
||||
return m.pluginErrors
|
||||
}
|
||||
|
||||
func (m *mockPluginErrorResolver) PluginError(ctx context.Context, id string) *plugins.Error {
|
||||
for _, err := range m.pluginErrors {
|
||||
if err.PluginID == id {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
)
|
||||
|
||||
const (
|
||||
DeprecationStepID = "deprecation"
|
||||
)
|
||||
|
||||
type deprecationStep struct {
|
||||
GrafanaVersion string
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Title() string {
|
||||
return "Deprecation check"
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Description() string {
|
||||
return "Check if any installed plugins are deprecated."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Resolution() string {
|
||||
return "Check the <a href='https://grafana.com/legal/plugin-deprecation/#a-plugin-i-use-is-deprecated-what-should-i-do'" +
|
||||
"target=_blank>documentation</a> for recommended steps or delete the plugin."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) ID() string {
|
||||
return DeprecationStepID
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
pi, ok := it.(*pluginItem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
p := pi.Plugin
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !s.updateChecker.IsUpdatable(ctx, *p) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin is deprecated
|
||||
i, ok := s.pluginIndex[p.ID]
|
||||
if !ok {
|
||||
// Unable to check deprecation status
|
||||
return nil, nil
|
||||
}
|
||||
if i.Status == "deprecated" {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
)
|
||||
|
||||
const (
|
||||
UnsignedStepID = "unsigned"
|
||||
)
|
||||
|
||||
type unsignedStep struct {
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Title() string {
|
||||
return "Plugin signature check"
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Description() string {
|
||||
return "Checks has a missing or invalid signature."
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Resolution() string {
|
||||
return "For security, we recommend only installing plugins from the catalog. " +
|
||||
"Review the plugin's status and verify your allowlist if appropriate."
|
||||
}
|
||||
|
||||
func (s *unsignedStep) ID() string {
|
||||
return UnsignedStepID
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
pi, ok := it.(*pluginItem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
|
||||
p := pi.Plugin
|
||||
invalidSignatureTypes := []plugins.SignatureStatus{
|
||||
plugins.SignatureStatusUnsigned,
|
||||
plugins.SignatureStatusModified,
|
||||
plugins.SignatureStatusInvalid,
|
||||
}
|
||||
if p != nil && slices.Contains(invalidSignatureTypes, p.Signature) {
|
||||
// This will only happen in dev mode or if the plugin is in the unsigned allow list
|
||||
links := []advisor.CheckErrorLink{}
|
||||
if _, ok := s.pluginIndex[p.ID]; ok {
|
||||
links = append(links, advisor.CheckErrorLink{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
})
|
||||
}
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
links,
|
||||
)}, nil
|
||||
}
|
||||
|
||||
pluginErr := pi.Err
|
||||
invalidErrorCodeTypes := []plugins.ErrorCode{
|
||||
plugins.ErrorCodeSignatureMissing,
|
||||
plugins.ErrorCodeSignatureInvalid,
|
||||
plugins.ErrorCodeSignatureModified,
|
||||
}
|
||||
if pluginErr != nil && slices.Contains(invalidErrorCodeTypes, pluginErr.ErrorCode) {
|
||||
links := []advisor.CheckErrorLink{}
|
||||
if _, ok := s.pluginIndex[pluginErr.PluginID]; ok {
|
||||
links = append(links, advisor.CheckErrorLink{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", pluginErr.PluginID),
|
||||
})
|
||||
}
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
pluginErr.PluginID,
|
||||
pluginErr.PluginID,
|
||||
links,
|
||||
)}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
)
|
||||
|
||||
const (
|
||||
UpdateStepID = "update"
|
||||
)
|
||||
|
||||
type updateStep struct {
|
||||
GrafanaVersion string
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *updateStep) Title() string {
|
||||
return "Update check"
|
||||
}
|
||||
|
||||
func (s *updateStep) Description() string {
|
||||
return "Checks if an installed plugins has a newer version available."
|
||||
}
|
||||
|
||||
func (s *updateStep) Resolution() string {
|
||||
return "Go to the plugin admin page and upgrade to the latest version."
|
||||
}
|
||||
|
||||
func (s *updateStep) ID() string {
|
||||
return UpdateStepID
|
||||
}
|
||||
|
||||
func (s *updateStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
pi, ok := it.(*pluginItem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
p := pi.Plugin
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !s.updateChecker.IsUpdatable(ctx, *p) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin has a newer version available
|
||||
info, ok := s.pluginIndex[p.ID]
|
||||
if !ok {
|
||||
// Unable to check updates
|
||||
return nil, nil
|
||||
}
|
||||
if s.updateChecker.CanUpdate(p.ID, p.Info.Version, info.Version, false) {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Upgrade",
|
||||
Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID),
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package checks
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
@@ -15,10 +14,6 @@ import (
|
||||
const (
|
||||
TypeLabel = "advisor.grafana.app/type"
|
||||
StatusAnnotation = "advisor.grafana.app/status"
|
||||
RetryAnnotation = "advisor.grafana.app/retry"
|
||||
IgnoreStepsAnnotation = "advisor.grafana.app/ignore-steps"
|
||||
IgnoreStepsAnnotationList = "advisor.grafana.app/ignore-steps-list"
|
||||
NameAnnotation = "advisor.grafana.app/checktype-name"
|
||||
StatusAnnotationError = "error"
|
||||
StatusAnnotationProcessed = "processed"
|
||||
)
|
||||
@@ -27,36 +22,16 @@ func NewCheckReportFailure(
|
||||
severity advisor.CheckReportFailureSeverity,
|
||||
stepID string,
|
||||
item string,
|
||||
itemID string,
|
||||
links []advisor.CheckErrorLink,
|
||||
) advisor.CheckReportFailure {
|
||||
return advisor.CheckReportFailure{
|
||||
) *advisor.CheckReportFailure {
|
||||
return &advisor.CheckReportFailure{
|
||||
Severity: severity,
|
||||
StepID: stepID,
|
||||
Item: item,
|
||||
ItemID: itemID,
|
||||
Links: links,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCheckReportFailureWithMoreInfo(
|
||||
severity advisor.CheckReportFailureSeverity,
|
||||
stepID string,
|
||||
item string,
|
||||
itemID string,
|
||||
links []advisor.CheckErrorLink,
|
||||
moreInfo string,
|
||||
) advisor.CheckReportFailure {
|
||||
return advisor.CheckReportFailure{
|
||||
Severity: severity,
|
||||
StepID: stepID,
|
||||
Item: item,
|
||||
ItemID: itemID,
|
||||
Links: links,
|
||||
MoreInfo: &moreInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func GetNamespace(stackID string) (string, error) {
|
||||
if stackID == "" {
|
||||
return metav1.NamespaceDefault, nil
|
||||
@@ -72,32 +47,12 @@ func GetStatusAnnotation(obj resource.Object) string {
|
||||
return obj.GetAnnotations()[StatusAnnotation]
|
||||
}
|
||||
|
||||
func GetRetryAnnotation(obj resource.Object) string {
|
||||
return obj.GetAnnotations()[RetryAnnotation]
|
||||
}
|
||||
|
||||
func AddAnnotations(ctx context.Context, obj resource.Object, annotations map[string]string) map[string]string {
|
||||
existingAnnotations := obj.GetAnnotations()
|
||||
if existingAnnotations == nil {
|
||||
existingAnnotations = map[string]string{}
|
||||
}
|
||||
maps.Copy(existingAnnotations, annotations)
|
||||
return existingAnnotations
|
||||
}
|
||||
|
||||
func DeleteAnnotations(ctx context.Context, obj resource.Object, annotations []string) map[string]string {
|
||||
existingAnnotations := obj.GetAnnotations()
|
||||
if existingAnnotations == nil {
|
||||
existingAnnotations = map[string]string{}
|
||||
}
|
||||
for _, annotation := range annotations {
|
||||
delete(existingAnnotations, annotation)
|
||||
}
|
||||
return existingAnnotations
|
||||
}
|
||||
|
||||
func SetStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error {
|
||||
annotations := AddAnnotations(ctx, obj, map[string]string{StatusAnnotation: status})
|
||||
annotations := obj.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
annotations[StatusAnnotation] = status
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
|
||||
@@ -3,45 +3,39 @@ package checkscheduler
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/k8s"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const defaultEvaluationInterval = 7 * 24 * time.Hour // 7 days
|
||||
const defaultEvaluationInterval = 24 * time.Hour
|
||||
const defaultMaxHistory = 10
|
||||
|
||||
var (
|
||||
waitInterval = 5 * time.Second
|
||||
waitMaxRetries = 3
|
||||
)
|
||||
|
||||
// Runner is a "runnable" app used to be able to expose and API endpoint
|
||||
// with the existing checks types. This does not need to be a CRUD resource, but it is
|
||||
// the only way existing at the moment to expose the check types.
|
||||
type Runner struct {
|
||||
checkRegistry checkregistry.CheckService
|
||||
client resource.Client
|
||||
typesClient resource.Client
|
||||
evaluationInterval time.Duration
|
||||
maxHistory int
|
||||
namespace string
|
||||
log logging.Logger
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewRunner creates a new Runner.
|
||||
func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
|
||||
func New(cfg app.Config) (app.Runnable, error) {
|
||||
// Read config
|
||||
specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig)
|
||||
if !ok {
|
||||
@@ -67,59 +61,54 @@ func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typesClient, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Runner{
|
||||
checkRegistry: checkRegistry,
|
||||
client: client,
|
||||
typesClient: typesClient,
|
||||
evaluationInterval: evalInterval,
|
||||
maxHistory: maxHistory,
|
||||
namespace: namespace,
|
||||
log: log.With("runner", "advisor.checkscheduler"),
|
||||
log: log.New("advisor.checkscheduler"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Runner) Run(ctx context.Context) error {
|
||||
logger := r.log.WithContext(ctx)
|
||||
// We still need the context to eventually be cancelled to exit this function
|
||||
// but we don't want the requests to fail because of it
|
||||
ctxWithoutCancel := context.WithoutCancel(ctx)
|
||||
lastCreated, err := r.checkLastCreated(ctxWithoutCancel, logger)
|
||||
lastCreated, err := r.checkLastCreated(ctx)
|
||||
if err != nil {
|
||||
logger.Error("Error getting last check creation time", "error", err)
|
||||
r.log.Error("Error getting last check creation time", "error", err)
|
||||
// Wait for interval to create the next scheduled check
|
||||
lastCreated = time.Now()
|
||||
} else {
|
||||
// do an initial creation if necessary
|
||||
if lastCreated.IsZero() {
|
||||
err = r.createChecks(ctxWithoutCancel, logger)
|
||||
err = r.createChecks(ctx)
|
||||
if err != nil {
|
||||
logger.Error("Error creating new check reports", "error", err)
|
||||
klog.Error("Error creating new check reports", "error", err)
|
||||
} else {
|
||||
lastCreated = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextSendInterval := getNextSendInterval(lastCreated, r.evaluationInterval)
|
||||
nextSendInterval := time.Until(lastCreated.Add(r.evaluationInterval))
|
||||
if nextSendInterval < time.Minute {
|
||||
nextSendInterval = 1 * time.Minute
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(nextSendInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err = r.createChecks(ctxWithoutCancel, logger)
|
||||
err = r.createChecks(ctx)
|
||||
if err != nil {
|
||||
logger.Error("Error creating new check reports", "error", err)
|
||||
klog.Error("Error creating new check reports", "error", err)
|
||||
}
|
||||
|
||||
err = r.cleanupChecks(ctxWithoutCancel, logger)
|
||||
err = r.cleanupChecks(ctx)
|
||||
if err != nil {
|
||||
logger.Error("Error cleaning up old check reports", "error", err)
|
||||
klog.Error("Error cleaning up old check reports", "error", err)
|
||||
}
|
||||
|
||||
if nextSendInterval != r.evaluationInterval {
|
||||
@@ -127,6 +116,7 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
}
|
||||
ticker.Reset(nextSendInterval)
|
||||
case <-ctx.Done():
|
||||
r.markUnprocessedChecksAsErrored(ctx)
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
@@ -135,8 +125,7 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
// checkLastCreated returns the creation time of the last check created
|
||||
// regardless of its ID. This assumes that the checks are created in batches
|
||||
// so a batch will have a similar creation time.
|
||||
// In case it finds an unprocessed check from a previous run, it will set it to error.
|
||||
func (r *Runner) checkLastCreated(ctx context.Context, log logging.Logger) (time.Time, error) {
|
||||
func (r *Runner) checkLastCreated(ctx context.Context) (time.Time, error) {
|
||||
list, err := r.client.List(ctx, r.namespace, resource.ListOptions{})
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
@@ -147,53 +136,19 @@ func (r *Runner) checkLastCreated(ctx context.Context, log logging.Logger) (time
|
||||
if itemCreated.After(lastCreated) {
|
||||
lastCreated = itemCreated
|
||||
}
|
||||
|
||||
// If the check is unprocessed, set it to error
|
||||
if checks.GetStatusAnnotation(item) == "" {
|
||||
log.Info("Check is unprocessed, marking as error", "check", item.GetStaticMetadata().Identifier())
|
||||
err := checks.SetStatusAnnotation(ctx, r.client, item, checks.StatusAnnotationError)
|
||||
if err != nil {
|
||||
log.Error("Error setting check status to error", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastCreated, nil
|
||||
}
|
||||
|
||||
// createChecks creates a new check for each check type in the registry.
|
||||
func (r *Runner) createChecks(ctx context.Context, logger logging.Logger) error {
|
||||
// List existing CheckType objects
|
||||
list, err := r.typesClient.List(ctx, r.namespace, resource.ListOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing check types: %w", err)
|
||||
}
|
||||
// This may be run before the check types are registered, so we need to wait for them to be registered.
|
||||
allChecksRegistered := len(list.GetItems()) == len(r.checkRegistry.Checks())
|
||||
retryCount := 0
|
||||
for !allChecksRegistered && retryCount < waitMaxRetries {
|
||||
logger.Info("Waiting for all check types to be registered", "retryCount", retryCount, "waitInterval", waitInterval)
|
||||
time.Sleep(waitInterval)
|
||||
list, err = r.typesClient.List(ctx, r.namespace, resource.ListOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing check types: %w", err)
|
||||
}
|
||||
allChecksRegistered = len(list.GetItems()) == len(r.checkRegistry.Checks())
|
||||
retryCount++
|
||||
}
|
||||
|
||||
// Create checks for each CheckType
|
||||
for _, item := range list.GetItems() {
|
||||
checkType, ok := item.(*advisorv0alpha1.CheckType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
func (r *Runner) createChecks(ctx context.Context) error {
|
||||
for _, check := range r.checkRegistry.Checks() {
|
||||
obj := &advisorv0alpha1.Check{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "check-",
|
||||
Namespace: r.namespace,
|
||||
Labels: map[string]string{
|
||||
checks.TypeLabel: checkType.Spec.Name,
|
||||
checks.TypeLabel: check.ID(),
|
||||
},
|
||||
},
|
||||
Spec: advisorv0alpha1.CheckSpec{},
|
||||
@@ -208,7 +163,7 @@ func (r *Runner) createChecks(ctx context.Context, logger logging.Logger) error
|
||||
}
|
||||
|
||||
// cleanupChecks deletes the olders checks if the number of checks exceeds the limit.
|
||||
func (r *Runner) cleanupChecks(ctx context.Context, logger logging.Logger) error {
|
||||
func (r *Runner) cleanupChecks(ctx context.Context) error {
|
||||
list, err := r.client.List(ctx, r.namespace, resource.ListOptions{Limit: -1})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -220,7 +175,7 @@ func (r *Runner) cleanupChecks(ctx context.Context, logger logging.Logger) error
|
||||
labels := check.GetLabels()
|
||||
checkType, ok := labels[checks.TypeLabel]
|
||||
if !ok {
|
||||
logger.Error("Check type not found in labels", "check", check)
|
||||
klog.Error("Check type not found in labels", "check", check)
|
||||
continue
|
||||
}
|
||||
checksByType[checkType] = append(checksByType[checkType], check)
|
||||
@@ -262,17 +217,6 @@ func getEvaluationInterval(pluginConfig map[string]string) (time.Duration, error
|
||||
return evaluationInterval, nil
|
||||
}
|
||||
|
||||
func getNextSendInterval(lastCreated time.Time, evaluationInterval time.Duration) time.Duration {
|
||||
nextSendInterval := time.Until(lastCreated.Add(evaluationInterval))
|
||||
// Add random variation of one hour
|
||||
randomVariation := time.Duration(rand.Int63n(time.Hour.Nanoseconds()))
|
||||
nextSendInterval += randomVariation
|
||||
if nextSendInterval < time.Minute {
|
||||
nextSendInterval = 1 * time.Minute
|
||||
}
|
||||
return nextSendInterval
|
||||
}
|
||||
|
||||
func getMaxHistory(pluginConfig map[string]string) (int, error) {
|
||||
maxHistory := defaultMaxHistory
|
||||
configMaxHistory, ok := pluginConfig["max_history"]
|
||||
@@ -285,3 +229,21 @@ func getMaxHistory(pluginConfig map[string]string) (int, error) {
|
||||
}
|
||||
return maxHistory, nil
|
||||
}
|
||||
|
||||
func (r *Runner) markUnprocessedChecksAsErrored(ctx context.Context) {
|
||||
list, err := r.client.List(ctx, r.namespace, resource.ListOptions{})
|
||||
if err != nil {
|
||||
r.log.Error("Error getting checks", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, check := range list.GetItems() {
|
||||
if checks.GetStatusAnnotation(check) == "" {
|
||||
r.log.Error("Check is unprocessed", "check", check.GetStaticMetadata().Identifier())
|
||||
err := checks.SetStatusAnnotation(ctx, r.client, check, checks.StatusAnnotationError)
|
||||
if err != nil {
|
||||
r.log.Error("Error setting check status to error", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,17 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestRunner_Run(t *testing.T) {
|
||||
t.Run("does not crash when error on list", func(t *testing.T) {
|
||||
mockCheckService := &MockCheckService{}
|
||||
mockClient := &MockClient{
|
||||
listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
|
||||
return nil, errors.New("list error")
|
||||
@@ -27,16 +28,10 @@ func TestRunner_Run(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
mockTypesClient := &MockClient{
|
||||
listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
|
||||
return &advisorv0alpha1.CheckTypeList{Items: []advisorv0alpha1.CheckType{}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
runner := &Runner{
|
||||
checkRegistry: mockCheckService,
|
||||
client: mockClient,
|
||||
typesClient: mockTypesClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
evaluationInterval: 1 * time.Hour,
|
||||
}
|
||||
|
||||
@@ -56,110 +51,59 @@ func TestRunner_checkLastCreated_ErrorOnList(t *testing.T) {
|
||||
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
lastCreated, err := runner.checkLastCreated(context.Background(), &logging.NoOpLogger{})
|
||||
lastCreated, err := runner.checkLastCreated(context.Background())
|
||||
assert.Error(t, err)
|
||||
assert.True(t, lastCreated.IsZero())
|
||||
}
|
||||
|
||||
func TestRunner_checkLastCreated_UnprocessedCheck(t *testing.T) {
|
||||
patchOperation := resource.PatchOperation{}
|
||||
identifier := resource.Identifier{}
|
||||
|
||||
mockClient := &MockClient{
|
||||
listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
|
||||
return &advisorv0alpha1.CheckList{
|
||||
Items: []advisorv0alpha1.Check{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "check-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
patchFunc: func(ctx context.Context, id resource.Identifier, patch resource.PatchRequest, options resource.PatchOptions, into resource.Object) error {
|
||||
patchOperation = patch.Operations[0]
|
||||
identifier = id
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
}
|
||||
|
||||
lastCreated, err := runner.checkLastCreated(context.Background(), &logging.NoOpLogger{})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, lastCreated.IsZero())
|
||||
assert.Equal(t, "check-1", identifier.Name)
|
||||
assert.Equal(t, "/metadata/annotations", patchOperation.Path)
|
||||
expectedAnnotations := map[string]string{
|
||||
checks.StatusAnnotation: "error",
|
||||
}
|
||||
assert.Equal(t, expectedAnnotations, patchOperation.Value)
|
||||
}
|
||||
|
||||
func TestRunner_createChecks_ErrorOnCreate(t *testing.T) {
|
||||
mockCheckService := &MockCheckService{checks: []checks.Check{&mockCheck{id: "check-1"}}}
|
||||
|
||||
mockCheckService := &MockCheckService{
|
||||
checks: []checks.Check{
|
||||
&mockCheck{
|
||||
id: "check-1",
|
||||
},
|
||||
},
|
||||
}
|
||||
mockClient := &MockClient{
|
||||
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return nil, errors.New("create error")
|
||||
},
|
||||
}
|
||||
|
||||
mockTypesClient := &MockClient{
|
||||
listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
|
||||
checkType := &advisorv0alpha1.CheckType{}
|
||||
checkType.Spec.Name = "check-1"
|
||||
return &advisorv0alpha1.CheckTypeList{
|
||||
Items: []advisorv0alpha1.CheckType{*checkType},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
runner := &Runner{
|
||||
checkRegistry: mockCheckService,
|
||||
client: mockClient,
|
||||
typesClient: mockTypesClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
err := runner.createChecks(context.Background(), &logging.NoOpLogger{})
|
||||
err := runner.createChecks(context.Background())
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRunner_createChecks_Success(t *testing.T) {
|
||||
mockCheckService := &MockCheckService{checks: []checks.Check{&mockCheck{id: "check-1"}}}
|
||||
|
||||
mockCheckService := &MockCheckService{
|
||||
checks: []checks.Check{
|
||||
&mockCheck{
|
||||
id: "check-1",
|
||||
},
|
||||
},
|
||||
}
|
||||
mockClient := &MockClient{
|
||||
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return &advisorv0alpha1.Check{}, nil
|
||||
},
|
||||
}
|
||||
|
||||
mockTypesClient := &MockClient{
|
||||
listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
|
||||
checkType := &advisorv0alpha1.CheckType{}
|
||||
checkType.Spec.Name = "check-1"
|
||||
return &advisorv0alpha1.CheckTypeList{
|
||||
Items: []advisorv0alpha1.CheckType{*checkType},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
runner := &Runner{
|
||||
checkRegistry: mockCheckService,
|
||||
client: mockClient,
|
||||
typesClient: mockTypesClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
err := runner.createChecks(context.Background(), &logging.NoOpLogger{})
|
||||
err := runner.createChecks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -172,10 +116,10 @@ func TestRunner_cleanupChecks_ErrorOnList(t *testing.T) {
|
||||
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
err := runner.cleanupChecks(context.Background(), &logging.NoOpLogger{})
|
||||
err := runner.cleanupChecks(context.Background())
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -193,10 +137,10 @@ func TestRunner_cleanupChecks_WithinMax(t *testing.T) {
|
||||
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
err := runner.cleanupChecks(context.Background(), &logging.NoOpLogger{})
|
||||
err := runner.cleanupChecks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -223,9 +167,9 @@ func TestRunner_cleanupChecks_ErrorOnDelete(t *testing.T) {
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
maxHistory: defaultMaxHistory,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
err := runner.cleanupChecks(context.Background(), &logging.NoOpLogger{})
|
||||
err := runner.cleanupChecks(context.Background())
|
||||
assert.ErrorContains(t, err, "delete error")
|
||||
}
|
||||
|
||||
@@ -259,9 +203,9 @@ func TestRunner_cleanupChecks_Success(t *testing.T) {
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
maxHistory: defaultMaxHistory,
|
||||
log: &logging.NoOpLogger{},
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
err := runner.cleanupChecks(context.Background(), &logging.NoOpLogger{})
|
||||
err := runner.cleanupChecks(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"check-0"}, itemsDeleted)
|
||||
}
|
||||
@@ -270,7 +214,7 @@ func Test_getEvaluationInterval(t *testing.T) {
|
||||
t.Run("default", func(t *testing.T) {
|
||||
interval, err := getEvaluationInterval(map[string]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 7*24*time.Hour, interval)
|
||||
assert.Equal(t, 24*time.Hour, interval)
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
@@ -306,15 +250,47 @@ func Test_getMaxHistory(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_getNextSendInterval(t *testing.T) {
|
||||
lastCreated := time.Now().Add(-7 * 24 * time.Hour)
|
||||
evaluationInterval := 7 * 24 * time.Hour
|
||||
nextSendInterval := getNextSendInterval(lastCreated, evaluationInterval)
|
||||
// The next send interval should be in < 1 hour
|
||||
assert.True(t, nextSendInterval < time.Hour)
|
||||
// Calculate the next send interval again and it should be different
|
||||
nextSendInterval2 := getNextSendInterval(lastCreated, evaluationInterval)
|
||||
assert.NotEqual(t, nextSendInterval, nextSendInterval2)
|
||||
func Test_markUnprocessedChecksAsErrored(t *testing.T) {
|
||||
checkList := &advisorv0alpha1.CheckList{
|
||||
Items: []advisorv0alpha1.Check{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "check-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
patchOperation := resource.PatchOperation{}
|
||||
identifier := resource.Identifier{}
|
||||
mockClient := &MockClient{
|
||||
listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
|
||||
return checkList, nil
|
||||
},
|
||||
patchFunc: func(ctx context.Context, id resource.Identifier, patch resource.PatchRequest, options resource.PatchOptions, into resource.Object) error {
|
||||
patchOperation = patch.Operations[0]
|
||||
identifier = id
|
||||
return nil
|
||||
},
|
||||
}
|
||||
runner := &Runner{
|
||||
client: mockClient,
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
runner.markUnprocessedChecksAsErrored(context.Background())
|
||||
assert.Equal(t, "check-1", identifier.Name)
|
||||
assert.Equal(t, "/metadata/annotations", patchOperation.Path)
|
||||
expectedAnnotations := map[string]string{
|
||||
checks.StatusAnnotation: "error",
|
||||
}
|
||||
assert.Equal(t, expectedAnnotations, patchOperation.Value)
|
||||
}
|
||||
|
||||
type MockCheckService struct {
|
||||
checks []checks.Check
|
||||
}
|
||||
|
||||
func (m *MockCheckService) Checks() []checks.Check {
|
||||
return m.checks
|
||||
}
|
||||
|
||||
type MockClient struct {
|
||||
@@ -341,14 +317,6 @@ func (m *MockClient) PatchInto(ctx context.Context, identifier resource.Identifi
|
||||
return m.patchFunc(ctx, identifier, patch, options, into)
|
||||
}
|
||||
|
||||
type MockCheckService struct {
|
||||
checks []checks.Check
|
||||
}
|
||||
|
||||
func (m *MockCheckService) Checks() []checks.Check {
|
||||
return m.checks
|
||||
}
|
||||
|
||||
type mockCheck struct {
|
||||
checks.Check
|
||||
|
||||
|
||||
@@ -3,17 +3,15 @@ package checktyperegisterer
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/k8s"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@@ -25,13 +23,13 @@ type Runner struct {
|
||||
checkRegistry checkregistry.CheckService
|
||||
client resource.Client
|
||||
namespace string
|
||||
log logging.Logger
|
||||
log log.Logger
|
||||
retryAttempts int
|
||||
retryDelay time.Duration
|
||||
}
|
||||
|
||||
// NewRunner creates a new Runner.
|
||||
func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
|
||||
func New(cfg app.Config) (app.Runnable, error) {
|
||||
// Read config
|
||||
specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig)
|
||||
if !ok {
|
||||
@@ -54,46 +52,33 @@ func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
|
||||
checkRegistry: checkRegistry,
|
||||
client: client,
|
||||
namespace: namespace,
|
||||
log: log.With("runner", "advisor.checktyperegisterer"),
|
||||
retryAttempts: 5,
|
||||
retryDelay: time.Second * 10,
|
||||
log: log.New("advisor.checktyperegisterer"),
|
||||
retryAttempts: 3,
|
||||
retryDelay: time.Second * 5,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj resource.Object) error {
|
||||
func (r *Runner) createOrUpdate(ctx context.Context, obj resource.Object) error {
|
||||
id := obj.GetStaticMetadata().Identifier()
|
||||
_, err := r.client.Create(ctx, id, obj, resource.CreateOptions{})
|
||||
if err != nil {
|
||||
if errors.IsAlreadyExists(err) {
|
||||
// Already exists, update
|
||||
log.Debug("Check type already exists, updating", "identifier", id)
|
||||
// Retrieve current annotations to avoid overriding them
|
||||
current, err := r.client.Get(ctx, obj.GetStaticMetadata().Identifier())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentAnnotations := current.GetAnnotations()
|
||||
if currentAnnotations == nil {
|
||||
currentAnnotations = make(map[string]string)
|
||||
}
|
||||
annotations := obj.GetAnnotations()
|
||||
maps.Copy(currentAnnotations, annotations)
|
||||
obj.SetAnnotations(currentAnnotations) // This will update the annotations in the object
|
||||
r.log.Debug("Check type already exists, updating", "identifier", id)
|
||||
_, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
|
||||
if err != nil && !errors.IsAlreadyExists(err) {
|
||||
if err != nil {
|
||||
// Ignore the error, it's probably due to a race condition
|
||||
log.Info("Error updating check type, ignoring", "error", err)
|
||||
r.log.Error("Error updating check type", "error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Debug("Check type registered successfully", "identifier", id)
|
||||
r.log.Debug("Check type registered successfully", "identifier", id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) Run(ctx context.Context) error {
|
||||
logger := r.log.WithContext(ctx)
|
||||
for _, t := range r.checkRegistry.Checks() {
|
||||
steps := t.Steps()
|
||||
stepTypes := make([]advisorv0alpha1.CheckTypeStep, len(steps))
|
||||
@@ -109,12 +94,6 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: t.ID(),
|
||||
Namespace: r.namespace,
|
||||
Annotations: map[string]string{
|
||||
checks.NameAnnotation: t.Name(),
|
||||
// Flag to indicate feature availability
|
||||
checks.RetryAnnotation: "1",
|
||||
checks.IgnoreStepsAnnotation: "1",
|
||||
},
|
||||
},
|
||||
Spec: advisorv0alpha1.CheckTypeSpec{
|
||||
Name: t.ID(),
|
||||
@@ -122,23 +101,17 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
},
|
||||
}
|
||||
for i := 0; i < r.retryAttempts; i++ {
|
||||
err := r.createOrUpdate(context.WithoutCancel(ctx), logger, obj)
|
||||
err := r.createOrUpdate(ctx, obj)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "apiserver is shutting down") {
|
||||
logger.Debug("Error creating check type, not retrying", "error", err)
|
||||
return nil
|
||||
}
|
||||
logger.Debug("Error creating check type, retrying", "error", err, "attempt", i+1)
|
||||
r.log.Error("Error creating check type, retrying", "error", err, "attempt", i+1)
|
||||
if i == r.retryAttempts-1 {
|
||||
logger.Error("Unable to register check type", "check_type", t.ID(), "error", err)
|
||||
r.log.Error("Unable to register check type")
|
||||
} else {
|
||||
// Calculate exponential backoff delay: baseDelay * 2^attempt
|
||||
delay := r.retryDelay * time.Duration(1<<i)
|
||||
time.Sleep(delay)
|
||||
time.Sleep(r.retryDelay)
|
||||
}
|
||||
continue
|
||||
}
|
||||
logger.Debug("Check type registered successfully", "check_type", t.ID())
|
||||
r.log.Debug("Check type registered successfully", "check_type", t.ID())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
k8sErrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
@@ -19,7 +18,6 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
checks []checks.Check
|
||||
getFunc func(ctx context.Context, id resource.Identifier) (resource.Object, error)
|
||||
createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error)
|
||||
updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error)
|
||||
expectedErr error
|
||||
@@ -58,37 +56,6 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "create already exists, with custom annotations",
|
||||
checks: []checks.Check{
|
||||
&mockCheck{
|
||||
id: "check1",
|
||||
steps: []checks.Step{
|
||||
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
|
||||
return &advisorv0alpha1.CheckType{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "check1",
|
||||
Annotations: map[string]string{
|
||||
checks.IgnoreStepsAnnotationList: "step1",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
|
||||
},
|
||||
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
|
||||
if obj.GetAnnotations()[checks.IgnoreStepsAnnotationList] != "step1" {
|
||||
return nil, fmt.Errorf("expected annotation %s, got %s", "step1", obj.GetAnnotations()[checks.IgnoreStepsAnnotationList])
|
||||
}
|
||||
return obj, nil
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "create error",
|
||||
checks: []checks.Check{
|
||||
@@ -123,24 +90,6 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
||||
},
|
||||
expectedErr: errors.New("update error"),
|
||||
},
|
||||
{
|
||||
name: "shutting down error",
|
||||
checks: []checks.Check{
|
||||
&mockCheck{
|
||||
id: "check1",
|
||||
steps: []checks.Step{
|
||||
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
|
||||
},
|
||||
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
|
||||
return nil, errors.New("apiserver is shutting down")
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "custom namespace",
|
||||
checks: []checks.Check{
|
||||
@@ -166,12 +115,11 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
||||
r := &Runner{
|
||||
checkRegistry: &mockCheckRegistry{checks: tt.checks},
|
||||
client: &mockClient{
|
||||
getFunc: tt.getFunc,
|
||||
createFunc: tt.createFunc,
|
||||
updateFunc: tt.updateFunc,
|
||||
},
|
||||
namespace: "custom-namespace",
|
||||
log: logging.DefaultLogger,
|
||||
log: log.New("test"),
|
||||
retryAttempts: 1,
|
||||
retryDelay: 0,
|
||||
}
|
||||
@@ -196,34 +144,20 @@ func (m *mockCheckRegistry) Checks() []checks.Check {
|
||||
}
|
||||
|
||||
type mockCheck struct {
|
||||
checks.Check
|
||||
|
||||
id string
|
||||
steps []checks.Step
|
||||
}
|
||||
|
||||
func (m *mockCheck) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockCheck) ID() string {
|
||||
return m.id
|
||||
}
|
||||
|
||||
func (m *mockCheck) Name() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
func (m *mockCheck) Steps() []checks.Step {
|
||||
return m.steps
|
||||
}
|
||||
|
||||
func (m *mockCheck) Item(ctx context.Context, id string) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockCheck) Items(ctx context.Context) ([]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type mockStep struct {
|
||||
id string
|
||||
title string
|
||||
@@ -246,25 +180,17 @@ func (m *mockStep) Resolution() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *mockStep) Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error) {
|
||||
func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type mockClient struct {
|
||||
resource.Client
|
||||
|
||||
getFunc func(ctx context.Context, id resource.Identifier) (resource.Object, error)
|
||||
createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error)
|
||||
updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error)
|
||||
}
|
||||
|
||||
func (m *mockClient) Get(ctx context.Context, id resource.Identifier) (resource.Object, error) {
|
||||
if m.getFunc != nil {
|
||||
return m.getFunc(ctx, id)
|
||||
}
|
||||
return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil
|
||||
}
|
||||
|
||||
func (m *mockClient) Create(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return m.createFunc(ctx, id, obj, opts)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
@@ -32,7 +29,7 @@ func getCheck(obj resource.Object, checkMap map[string]checks.Check) (checks.Che
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func processCheck(ctx context.Context, log logging.Logger, client resource.Client, typesClient resource.Client, obj resource.Object, check checks.Check) error {
|
||||
func processCheck(ctx context.Context, client resource.Client, obj resource.Object, check checks.Check) error {
|
||||
status := checks.GetStatusAnnotation(obj)
|
||||
if status != "" {
|
||||
// Check already processed
|
||||
@@ -43,33 +40,17 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
|
||||
return fmt.Errorf("invalid object type")
|
||||
}
|
||||
// Get the items to check
|
||||
err := check.Init(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing check: %w", err)
|
||||
}
|
||||
items, err := check.Items(ctx)
|
||||
if err != nil {
|
||||
setErr := checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationError)
|
||||
if setErr != nil {
|
||||
return setErr
|
||||
}
|
||||
return fmt.Errorf("error listing items for check: %w", err)
|
||||
}
|
||||
// Get the check type
|
||||
var checkType resource.Object
|
||||
checkType, err = typesClient.Get(ctx, resource.Identifier{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: check.ID(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error initializing check: %w", err)
|
||||
}
|
||||
// Run the steps
|
||||
steps, err := filterSteps(checkType, check.Steps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
failures, err := runStepsInParallel(ctx, log, &c.Spec, steps, items)
|
||||
steps := check.Steps()
|
||||
failures, err := runStepsInParallel(ctx, &c.Spec, steps, items)
|
||||
if err != nil {
|
||||
setErr := checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationError)
|
||||
if setErr != nil {
|
||||
@@ -82,111 +63,20 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
|
||||
Failures: failures,
|
||||
Count: int64(len(items)),
|
||||
}
|
||||
// Set the status annotation to processed and annotate the steps ignored
|
||||
annotations := checks.AddAnnotations(ctx, obj, map[string]string{
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
checks.IgnoreStepsAnnotationList: checkType.GetAnnotations()[checks.IgnoreStepsAnnotationList],
|
||||
})
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{
|
||||
{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status/report",
|
||||
Value: *report,
|
||||
}, {
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/metadata/annotations",
|
||||
Value: annotations,
|
||||
},
|
||||
},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func processCheckRetry(ctx context.Context, log logging.Logger, client resource.Client, typesClient resource.Client, obj resource.Object, check checks.Check) error {
|
||||
status := checks.GetStatusAnnotation(obj)
|
||||
if status == "" || status == checks.StatusAnnotationError {
|
||||
// Check not processed yet or errored
|
||||
return nil
|
||||
}
|
||||
// Get the item to retry from the annotation
|
||||
itemToRetry := checks.GetRetryAnnotation(obj)
|
||||
if itemToRetry == "" {
|
||||
// No item to retry, nothing to do
|
||||
return nil
|
||||
}
|
||||
c, ok := obj.(*advisorv0alpha1.Check)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid object type")
|
||||
}
|
||||
// Get the items to check
|
||||
err := check.Init(ctx)
|
||||
err = checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationProcessed)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing check: %w", err)
|
||||
return err
|
||||
}
|
||||
failures := []advisorv0alpha1.CheckReportFailure{}
|
||||
item, err := check.Item(ctx, itemToRetry)
|
||||
if err != nil {
|
||||
setErr := checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationError)
|
||||
if setErr != nil {
|
||||
return setErr
|
||||
}
|
||||
return fmt.Errorf("error getting item for check: %w", err)
|
||||
}
|
||||
if item != nil {
|
||||
// Get the check type
|
||||
var checkType resource.Object
|
||||
checkType, err = typesClient.Get(ctx, resource.Identifier{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: check.ID(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Run the steps
|
||||
steps, err := filterSteps(checkType, check.Steps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
failures, err = runStepsInParallel(ctx, log, &c.Spec, steps, []any{item})
|
||||
if err != nil {
|
||||
setErr := checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationError)
|
||||
if setErr != nil {
|
||||
return setErr
|
||||
}
|
||||
return fmt.Errorf("error running steps: %w", err)
|
||||
}
|
||||
}
|
||||
// Pull failures from the report for the items to retry
|
||||
c.CheckStatus.Report.Failures = slices.DeleteFunc(c.CheckStatus.Report.Failures, func(f advisorv0alpha1.CheckReportFailure) bool {
|
||||
if f.ItemID == itemToRetry {
|
||||
for _, newFailure := range failures {
|
||||
if newFailure.StepID == f.StepID {
|
||||
// Same failure found, keep it
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Failure no longer found, remove it
|
||||
return true
|
||||
}
|
||||
// Failure not in the list of items to retry, keep it
|
||||
return false
|
||||
})
|
||||
// Delete the retry annotation to mark the check as processed
|
||||
annotations := checks.DeleteAnnotations(ctx, obj, []string{checks.RetryAnnotation})
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status/report",
|
||||
Value: c.CheckStatus.Report,
|
||||
}, {
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/metadata/annotations",
|
||||
Value: annotations,
|
||||
Value: *report,
|
||||
}},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0alpha1.CheckSpec, steps []checks.Step, items []any) ([]advisorv0alpha1.CheckReportFailure, error) {
|
||||
func runStepsInParallel(ctx context.Context, spec *advisorv0alpha1.CheckSpec, steps []checks.Step, items []any) ([]advisorv0alpha1.CheckReportFailure, error) {
|
||||
reportFailures := []advisorv0alpha1.CheckReportFailure{}
|
||||
var internalErr error
|
||||
var wg sync.WaitGroup
|
||||
@@ -201,25 +91,15 @@ func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0
|
||||
go func(step checks.Step, item any) {
|
||||
defer wg.Done()
|
||||
defer func() { <-limit }()
|
||||
var stepErr []advisorv0alpha1.CheckReportFailure
|
||||
var err error
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Error("panic recovered in step", "step", step.ID(), "error", r, "item", item)
|
||||
}
|
||||
}()
|
||||
logger := log.With("step", step.ID())
|
||||
stepErr, err = step.Run(ctx, logger, spec, item)
|
||||
}()
|
||||
stepErr, err := step.Run(ctx, spec, item)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if err != nil {
|
||||
internalErr = fmt.Errorf("error running step %s: %w", step.ID(), err)
|
||||
return
|
||||
}
|
||||
if len(stepErr) > 0 {
|
||||
reportFailures = append(reportFailures, stepErr...)
|
||||
if stepErr != nil {
|
||||
reportFailures = append(reportFailures, *stepErr)
|
||||
}
|
||||
}(step, item)
|
||||
}
|
||||
@@ -227,18 +107,3 @@ func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0
|
||||
wg.Wait()
|
||||
return reportFailures, internalErr
|
||||
}
|
||||
|
||||
func filterSteps(checkType resource.Object, steps []checks.Step) ([]checks.Step, error) {
|
||||
ignoreStepsList := checkType.GetAnnotations()[checks.IgnoreStepsAnnotationList]
|
||||
if ignoreStepsList != "" {
|
||||
filteredSteps := []checks.Step{}
|
||||
ignoreStepsList := strings.Split(ignoreStepsList, ",")
|
||||
for _, step := range steps {
|
||||
if !slices.Contains(ignoreStepsList, step.ID()) {
|
||||
filteredSteps = append(filteredSteps, step)
|
||||
}
|
||||
}
|
||||
return filteredSteps, nil
|
||||
}
|
||||
return steps, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
@@ -58,15 +57,14 @@ func TestProcessCheck(t *testing.T) {
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
err = processCheck(ctx, client, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessMultipleCheckItems(t *testing.T) {
|
||||
@@ -78,7 +76,6 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
items := make([]any, 100)
|
||||
for i := range items {
|
||||
@@ -92,9 +89,9 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
||||
items: items,
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
err = processCheck(ctx, client, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
r := client.lastValue.(advisorv0alpha1.CheckV0alpha1StatusReport)
|
||||
assert.Equal(t, r.Count, int64(100))
|
||||
assert.Len(t, r.Failures, 50)
|
||||
@@ -102,13 +99,12 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
||||
|
||||
func TestProcessCheck_AlreadyProcessed(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{checks.StatusAnnotation: checks.StatusAnnotationProcessed})
|
||||
obj.SetAnnotations(map[string]string{checks.StatusAnnotation: "processed"})
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
check := &mockCheck{}
|
||||
|
||||
err := processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
err := processCheck(ctx, client, obj, check)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -121,7 +117,6 @@ func TestProcessCheck_RunError(t *testing.T) {
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
@@ -129,164 +124,9 @@ func TestProcessCheck_RunError(t *testing.T) {
|
||||
err: errors.New("run error"),
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
err = processCheck(ctx, client, obj, check)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheck_IgnoreSteps(t *testing.T) {
|
||||
checkType := &advisorv0alpha1.CheckType{}
|
||||
checkType.SetAnnotations(map[string]string{checks.IgnoreStepsAnnotationList: "mock"})
|
||||
typesClient := &mockTypesClient{
|
||||
res: checkType,
|
||||
}
|
||||
ctx := context.TODO()
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
err: errors.New("run error, should not be triggered"),
|
||||
}
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, "mock", obj.GetAnnotations()[checks.IgnoreStepsAnnotationList])
|
||||
}
|
||||
|
||||
func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
runPanics: true,
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_NoRetry(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_RetryError(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
err: errors.New("retry error"),
|
||||
}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_SkipMissingItem(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
{
|
||||
ItemID: "item",
|
||||
StepID: "step",
|
||||
},
|
||||
}
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
items: []any{nil},
|
||||
}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation])
|
||||
assert.Empty(t, obj.CheckStatus.Report.Failures)
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_Success(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
{
|
||||
ItemID: "item",
|
||||
StepID: "step",
|
||||
},
|
||||
}
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation])
|
||||
assert.Empty(t, obj.CheckStatus.Report.Failures)
|
||||
assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
type mockClient struct {
|
||||
@@ -299,64 +139,35 @@ func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockTypesClient struct {
|
||||
resource.Client
|
||||
res resource.Object
|
||||
}
|
||||
|
||||
func (m *mockTypesClient) Get(ctx context.Context, id resource.Identifier) (resource.Object, error) {
|
||||
if m.res == nil {
|
||||
return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil
|
||||
}
|
||||
return m.res, nil
|
||||
}
|
||||
|
||||
type mockCheck struct {
|
||||
err error
|
||||
items []any
|
||||
runPanics bool
|
||||
err error
|
||||
items []any
|
||||
}
|
||||
|
||||
func (m *mockCheck) ID() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
func (m *mockCheck) Name() string {
|
||||
return "Mock"
|
||||
}
|
||||
|
||||
func (m *mockCheck) Items(ctx context.Context) ([]any, error) {
|
||||
return m.items, nil
|
||||
}
|
||||
|
||||
func (m *mockCheck) Item(ctx context.Context, id string) (any, error) {
|
||||
return m.items[0], nil
|
||||
}
|
||||
|
||||
func (m *mockCheck) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockCheck) Steps() []checks.Step {
|
||||
return []checks.Step{
|
||||
&mockStep{err: m.err, panics: m.runPanics},
|
||||
&mockStep{err: m.err},
|
||||
}
|
||||
}
|
||||
|
||||
type mockStep struct {
|
||||
err error
|
||||
panics bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *mockStep) Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, items any) ([]advisorv0alpha1.CheckReportFailure, error) {
|
||||
if m.panics {
|
||||
panic("panic")
|
||||
}
|
||||
func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, items any) (*advisorv0alpha1.CheckReportFailure, error) {
|
||||
if m.err != nil {
|
||||
return nil, m.err
|
||||
}
|
||||
if _, ok := items.(error); ok {
|
||||
return []advisorv0alpha1.CheckReportFailure{{}}, nil
|
||||
return &advisorv0alpha1.CheckReportFailure{}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user