merge main
This commit is contained in:
commit
cf05abc4e9
33 files changed
+458
-87
No files matched your search
@@ -1515,6 +1515,16 @@ Options are `debug`, `info`, `warn`, `error`, and `critical`. Default is `info`.
|
||||
Optional settings to set different levels for specific loggers.
|
||||
For example: `filters = sqlstore:debug`
|
||||
|
||||
You can use multiple filters with a comma-seperated list:
|
||||
For example: `filters = sqlstore:debug,plugins:info`
|
||||
|
||||
The equivalent for a `docker-compose.yaml` looks like this:
|
||||
|
||||
```
|
||||
GF_LOG_FILTERS: sqlstore:debug,plugins:info
|
||||
GF_LOG_LEVEL: error
|
||||
```
|
||||
|
||||
#### `user_facing_default_error`
|
||||
|
||||
Use this configuration option to set the default error message shown to users. This message is displayed instead of sensitive backend errors, which should be obfuscated. The default message is `Please inspect the Grafana server log for details.`.
|
||||
|
||||
@@ -74,7 +74,7 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 // @grafana/alerting-backend
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664 // @grafana/alerting-backend
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 // @grafana/identity-access-team
|
||||
github.com/grafana/dashboard-linter v0.0.0-20241106223805-1e7999311752 // @grafana/grafana-app-platform-squad
|
||||
|
||||
@@ -1528,8 +1528,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 h1:dG/UKAjY/KlKp9fY8aEm+gSQHHRmPm5q+9cea3hRSu8=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU=
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664 h1:avtd90OVg2wMHz5pUsTWypu4rEt5bn5H0kOLXa7xgqc=
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM=
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 h1:NTMmow+74I3Jb033xhbRgWQS7A//5TDhiM4tl7bsVP4=
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7/go.mod h1:T3X4z0ejGfJOiOmZLFeKCRT/yxWJq/RtclAc/PHj/w4=
|
||||
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 h1:EokLC5grHwLPs4tXW8T6E8187H1e5G9AP0QQ5B60HbA=
|
||||
|
||||
+22
-2
@@ -510,6 +510,7 @@ github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJ
|
||||
github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0=
|
||||
github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88=
|
||||
@@ -546,6 +547,7 @@ github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea h1:DfZQkvEbdmOe+JK2TMtBM+0I9GSdzE2y/L1/AmD8xKc=
|
||||
github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea/go.mod h1:Y7Vld91/HRbTBm7JwoI7HejdDB0u+e9AUBO9MB7yuZk=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
@@ -582,9 +584,10 @@ github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGc
|
||||
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
|
||||
github.com/google/go-github/v69 v69.2.0 h1:wR+Wi/fN2zdUx9YxSmYE0ktiX9IAR/BeePzeaUUbEHE=
|
||||
github.com/google/go-github/v69 v69.2.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg=
|
||||
github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0=
|
||||
github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td8=
|
||||
@@ -735,6 +738,7 @@ github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M=
|
||||
github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE=
|
||||
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
|
||||
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
|
||||
@@ -955,6 +959,7 @@ go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw=
|
||||
go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg=
|
||||
go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg=
|
||||
go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opentelemetry.io/collector v0.102.1 h1:M/ciCcReQsSDYG9bJ2Qwqk7pQILDJ2bM/l0MdeCAvJE=
|
||||
go.opentelemetry.io/collector v0.102.1/go.mod h1:yF1lDRgL/Eksb4/LUnkMjvLvHHpi6wqBVlzp+dACnPM=
|
||||
go.opentelemetry.io/collector/component v0.102.1 h1:66z+LN5dVCXhvuVKD1b56/3cYLK+mtYSLIwlskYA9IQ=
|
||||
@@ -1031,6 +1036,7 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.5
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs=
|
||||
@@ -1051,10 +1057,13 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9l
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
@@ -1066,8 +1075,11 @@ go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
|
||||
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
@@ -1081,9 +1093,12 @@ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
@@ -1093,8 +1108,11 @@ golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
@@ -1102,9 +1120,11 @@ golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMe
|
||||
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||
|
||||
@@ -118,6 +118,7 @@ func (d *DualWriterMode3) Get(ctx context.Context, name string, options *metav1.
|
||||
d.recordStorageDuration(err != nil, mode3Str, d.resource, method, startStorage)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to get object in storage")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//nolint:errcheck
|
||||
|
||||
@@ -107,10 +107,7 @@ func TestMode3_Get(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return an error when getting an object in the unified store fails",
|
||||
setupLegacyFn: func(m *mock.Mock, name string) {
|
||||
m.On("Get", mock.Anything, name, mock.Anything).Return(exampleObj, nil)
|
||||
},
|
||||
name: "should return an error when getting an object in the unified store fails, and should not go to legacy",
|
||||
setupStorageFn: func(m *mock.Mock, name string) {
|
||||
m.On("Get", mock.Anything, name, mock.Anything).Return(nil, errors.New("error"))
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/google/go-github/v69/github"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/google/go-github/v69/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
)
|
||||
|
||||
type githubPublishTestCases struct {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/google/go-github/v45/github"
|
||||
"github.com/google/go-github/v69/github"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+1
-2
@@ -15,8 +15,7 @@ require (
|
||||
github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad
|
||||
github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend
|
||||
github.com/google/go-cmp v0.6.0 // @grafana/grafana-backend-group
|
||||
github.com/google/go-github v17.0.0+incompatible // @grafana/grafana-developer-enablement-squad
|
||||
github.com/google/go-github/v45 v45.2.0 // @grafana/grafana-developer-enablement-squad
|
||||
github.com/google/go-github/v69 v69.2.0 // @grafana/grafana-developer-enablement-squad
|
||||
github.com/google/uuid v1.6.0 // indirect; @grafana/grafana-backend-group
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect; @grafana/grafana-backend-group
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect; @grafana/grafana-backend-group
|
||||
|
||||
+2
-4
@@ -128,10 +128,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
|
||||
github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28=
|
||||
github.com/google/go-github/v69 v69.2.0 h1:wR+Wi/fN2zdUx9YxSmYE0ktiX9IAR/BeePzeaUUbEHE=
|
||||
github.com/google/go-github/v69 v69.2.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||
|
||||
@@ -33,6 +33,8 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const folderSearchLimit = 100000
|
||||
|
||||
func (s *Service) getFoldersFromApiServer(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
||||
if q.SignedInUser == nil {
|
||||
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
||||
@@ -171,7 +173,7 @@ func (s *Service) searchFoldersFromApiServer(ctx context.Context, query folder.S
|
||||
Fields: []*resource.Requirement{},
|
||||
Labels: []*resource.Requirement{},
|
||||
},
|
||||
Limit: 100000}
|
||||
Limit: folderSearchLimit}
|
||||
|
||||
if len(query.UIDs) > 0 {
|
||||
request.Options.Fields = []*resource.Requirement{{
|
||||
@@ -269,7 +271,7 @@ func (s *Service) getFolderByIDFromApiServer(ctx context.Context, id int64, orgI
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: 100000}
|
||||
Limit: folderSearchLimit}
|
||||
|
||||
res, err := s.k8sclient.Search(ctx, orgID, request)
|
||||
if err != nil {
|
||||
@@ -322,7 +324,7 @@ func (s *Service) getFolderByTitleFromApiServer(ctx context.Context, orgID int64
|
||||
},
|
||||
Labels: []*resource.Requirement{},
|
||||
},
|
||||
Limit: 100000}
|
||||
Limit: folderSearchLimit}
|
||||
|
||||
if parentUID != nil {
|
||||
req := []*resource.Requirement{{
|
||||
@@ -671,7 +673,7 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: 100000}
|
||||
Limit: folderSearchLimit}
|
||||
|
||||
res, err := s.dashboardK8sClient.Search(ctx, cmd.OrgID, request)
|
||||
if err != nil {
|
||||
|
||||
@@ -554,7 +554,7 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
|
||||
},
|
||||
Labels: []*resource.Requirement{},
|
||||
},
|
||||
Limit: 100000}).Return(&resource.ResourceSearchResponse{
|
||||
Limit: folderSearchLimit}).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{
|
||||
@@ -649,7 +649,7 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: 100000}).Return(&resource.ResourceSearchResponse{
|
||||
Limit: folderSearchLimit}).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{
|
||||
@@ -716,7 +716,7 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
|
||||
},
|
||||
Query: "*test*",
|
||||
Fields: dashboardsearch.IncludeFields,
|
||||
Limit: 100000}).Return(&resource.ResourceSearchResponse{
|
||||
Limit: folderSearchLimit}).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{
|
||||
@@ -829,7 +829,7 @@ func TestDeleteFoldersFromApiServer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: 100000}).Return(&resource.ResourceSearchResponse{
|
||||
Limit: folderSearchLimit}).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{
|
||||
|
||||
@@ -8,14 +8,19 @@ import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
internalfolders "github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashboardsearch "github.com/grafana/grafana/pkg/services/dashboards/service/search"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -159,26 +164,71 @@ func (ss *FolderUnifiedStoreImpl) GetParents(ctx context.Context, q folder.GetPa
|
||||
}
|
||||
|
||||
func (ss *FolderUnifiedStoreImpl) GetChildren(ctx context.Context, q folder.GetChildrenQuery) ([]*folder.Folder, error) {
|
||||
out, err := ss.k8sclient.List(ctx, q.OrgID, v1.ListOptions{})
|
||||
// the general folder is saved as an empty string in the database
|
||||
if q.UID == folder.GeneralFolderUID {
|
||||
q.UID = ""
|
||||
}
|
||||
if q.Limit == 0 {
|
||||
q.Limit = folderSearchLimit
|
||||
}
|
||||
if q.Page == 0 {
|
||||
q.Page = 1
|
||||
}
|
||||
|
||||
req := &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Fields: []*resource.Requirement{
|
||||
{
|
||||
Key: resource.SEARCH_FIELD_FOLDER,
|
||||
Operator: string(selection.In),
|
||||
Values: []string{q.UID},
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: q.Limit,
|
||||
// legacy fallback search requires page, unistore requires offset,
|
||||
// so set both
|
||||
Offset: q.Limit * (q.Page - 1),
|
||||
Page: q.Page,
|
||||
}
|
||||
|
||||
// only filter the folder UIDs if they are provided in the query
|
||||
if len(q.FolderUIDs) > 0 {
|
||||
req.Options.Fields = append(req.Options.Fields, &resource.Requirement{
|
||||
Key: resource.SEARCH_FIELD_NAME,
|
||||
Operator: string(selection.In),
|
||||
Values: q.FolderUIDs,
|
||||
})
|
||||
}
|
||||
|
||||
// now, get children of the parent folder
|
||||
out, err := ss.k8sclient.Search(ctx, q.OrgID, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := dashboardsearch.ParseResults(out, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allowK6Folder := (q.SignedInUser != nil && q.SignedInUser.IsIdentityType(claims.TypeServiceAccount))
|
||||
hits := make([]*folder.Folder, 0)
|
||||
for _, item := range out.Items {
|
||||
// convert item to legacy folder format
|
||||
f, err := ss.UnstructuredToLegacyFolder(ctx, &item)
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("unable to convert unstructured item to legacy folder %w", err)
|
||||
for _, item := range res.Hits {
|
||||
// filter out k6 folders if request is not from a service account
|
||||
if item.Name == accesscontrol.K6FolderUID && !allowK6Folder {
|
||||
continue
|
||||
}
|
||||
|
||||
// it we are at root level, skip subfolder
|
||||
if q.UID == "" && f.ParentUID != "" {
|
||||
continue // query filter
|
||||
// search only returns a subset of info, get all info of the folder
|
||||
item, err := ss.k8sclient.Get(ctx, item.Name, q.OrgID, v1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// if we are at a nested folder, then skip folders that don't belong to parentUid
|
||||
if q.UID != "" && !strings.EqualFold(f.ParentUID, q.UID) {
|
||||
continue
|
||||
|
||||
f, err := ss.UnstructuredToLegacyFolder(ctx, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hits = append(hits, f)
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package folderimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
)
|
||||
|
||||
func TestComputeFullPath(t *testing.T) {
|
||||
@@ -75,3 +84,155 @@ func TestComputeFullPath(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChildren(t *testing.T) {
|
||||
mockCli := new(client.MockK8sHandler)
|
||||
store := FolderUnifiedStoreImpl{
|
||||
k8sclient: mockCli,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
orgID := int64(2)
|
||||
|
||||
t.Run("should be able to find children folders, and set defaults for pages", func(t *testing.T) {
|
||||
mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Fields: []*resource.Requirement{
|
||||
{
|
||||
Key: resource.SEARCH_FIELD_FOLDER,
|
||||
Operator: string(selection.In),
|
||||
Values: []string{"folder1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: folderSearchLimit, // should default to folderSearchLimit
|
||||
Offset: 0, // should be set as limit * (page - 1)
|
||||
Page: 1, // should be set to 1 by default
|
||||
}).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING},
|
||||
},
|
||||
Rows: []*resource.ResourceTableRow{
|
||||
{
|
||||
Key: &resource.ResourceKey{Name: "folder2", Resource: "folder"},
|
||||
Cells: [][]byte{[]byte("folder1")},
|
||||
},
|
||||
{
|
||||
Key: &resource.ResourceKey{Name: "folder3", Resource: "folder"},
|
||||
Cells: [][]byte{[]byte("folder1")},
|
||||
},
|
||||
},
|
||||
},
|
||||
TotalHits: 1,
|
||||
}, nil).Once()
|
||||
mockCli.On("Get", mock.Anything, "folder2", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"name": "folder2"},
|
||||
},
|
||||
}, nil).Once()
|
||||
mockCli.On("Get", mock.Anything, "folder3", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"name": "folder3"},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// don't set page or limit - should be automatically added
|
||||
result, err := store.GetChildren(ctx, folder.GetChildrenQuery{
|
||||
UID: "folder1",
|
||||
OrgID: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result, 2)
|
||||
require.Equal(t, "folder2", result[0].UID)
|
||||
require.Equal(t, "folder3", result[1].UID)
|
||||
})
|
||||
|
||||
t.Run("pages should be able to be set, general folder should be turned to empty string, and folder uids should be passed in", func(t *testing.T) {
|
||||
mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Fields: []*resource.Requirement{
|
||||
{
|
||||
Key: resource.SEARCH_FIELD_FOLDER,
|
||||
Operator: string(selection.In),
|
||||
Values: []string{""}, // should be an empty string if general is passed in
|
||||
},
|
||||
{
|
||||
Key: resource.SEARCH_FIELD_NAME,
|
||||
Operator: string(selection.In),
|
||||
Values: []string{"folder2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Limit: 10,
|
||||
Offset: 20, // should be set as limit * (page - 1)
|
||||
Page: 3,
|
||||
}).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING},
|
||||
},
|
||||
Rows: []*resource.ResourceTableRow{
|
||||
{
|
||||
Key: &resource.ResourceKey{Name: "folder2", Resource: "folder"},
|
||||
Cells: [][]byte{[]byte("folder1")},
|
||||
},
|
||||
},
|
||||
},
|
||||
TotalHits: 1,
|
||||
}, nil).Once()
|
||||
mockCli.On("Get", mock.Anything, "folder2", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"name": "folder2"},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
result, err := store.GetChildren(ctx, folder.GetChildrenQuery{
|
||||
UID: "general",
|
||||
OrgID: orgID,
|
||||
Limit: 10,
|
||||
Page: 3,
|
||||
FolderUIDs: []string{"folder2"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result, 1)
|
||||
require.Equal(t, "folder2", result[0].UID)
|
||||
})
|
||||
|
||||
t.Run("k6 folder should only be returned to service accounts", func(t *testing.T) {
|
||||
mockCli.On("Search", mock.Anything, orgID, mock.Anything).Return(&resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING},
|
||||
},
|
||||
Rows: []*resource.ResourceTableRow{
|
||||
{
|
||||
Key: &resource.ResourceKey{Name: accesscontrol.K6FolderUID, Resource: "folder"},
|
||||
Cells: [][]byte{[]byte("folder1")},
|
||||
},
|
||||
},
|
||||
},
|
||||
TotalHits: 1,
|
||||
}, nil)
|
||||
mockCli.On("Get", mock.Anything, accesscontrol.K6FolderUID, orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"name": accesscontrol.K6FolderUID},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
result, err := store.GetChildren(ctx, folder.GetChildrenQuery{
|
||||
UID: "folder",
|
||||
OrgID: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result, 0)
|
||||
|
||||
result, err = store.GetChildren(ctx, folder.GetChildrenQuery{
|
||||
UID: "folder",
|
||||
OrgID: orgID,
|
||||
SignedInUser: &identity.StaticRequester{Type: claims.TypeServiceAccount},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result, 1)
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
alertingHttp "github.com/grafana/alerting/http"
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/notify/stages"
|
||||
"github.com/grafana/alerting/receivers"
|
||||
@@ -408,9 +409,7 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
|
||||
tmpl,
|
||||
img,
|
||||
LoggerFactory,
|
||||
func(n receivers.Metadata) (receivers.WebhookSender, error) {
|
||||
return s, nil
|
||||
},
|
||||
alertingHttp.DefaultClientConfiguration,
|
||||
func(n receivers.Metadata) (receivers.EmailSender, error) {
|
||||
return s, nil
|
||||
},
|
||||
|
||||
@@ -191,7 +191,7 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664 // indirect
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 // indirect
|
||||
github.com/grafana/dataplane/sdata v0.0.9 // indirect
|
||||
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect
|
||||
|
||||
@@ -564,8 +564,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 h1:dG/UKAjY/KlKp9fY8aEm+gSQHHRmPm5q+9cea3hRSu8=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU=
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664 h1:avtd90OVg2wMHz5pUsTWypu4rEt5bn5H0kOLXa7xgqc=
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM=
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 h1:NTMmow+74I3Jb033xhbRgWQS7A//5TDhiM4tl7bsVP4=
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7/go.mod h1:T3X4z0ejGfJOiOmZLFeKCRT/yxWJq/RtclAc/PHj/w4=
|
||||
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 h1:EokLC5grHwLPs4tXW8T6E8187H1e5G9AP0QQ5B60HbA=
|
||||
|
||||
@@ -171,7 +171,7 @@ func (s *Storage) Create(ctx context.Context, key string, obj runtime.Object, ou
|
||||
}
|
||||
rsp, err := s.store.Create(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
if rsp.Error != nil {
|
||||
if rsp.Error.Code == http.StatusConflict {
|
||||
@@ -249,7 +249,7 @@ func (s *Storage) Delete(
|
||||
}
|
||||
rsp, err := s.store.Delete(ctx, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
if rsp.Error != nil {
|
||||
return resource.GetError(rsp.Error)
|
||||
@@ -289,7 +289,8 @@ func (s *Storage) Watch(ctx context.Context, key string, opts storage.ListOption
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.EOF) {
|
||||
return watch.NewEmptyWatch(), nil
|
||||
}
|
||||
return nil, err
|
||||
|
||||
return nil, resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
|
||||
reporter := apierrors.NewClientErrorReporter(500, "WATCH", "")
|
||||
@@ -323,7 +324,7 @@ func (s *Storage) Get(ctx context.Context, key string, opts storage.GetOptions,
|
||||
|
||||
rsp, err := s.store.Read(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
if rsp.Error != nil {
|
||||
if rsp.Error.Code == http.StatusNotFound {
|
||||
@@ -361,7 +362,7 @@ func (s *Storage) GetList(ctx context.Context, key string, opts storage.ListOpti
|
||||
|
||||
rsp, err := s.store.List(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
if rsp.Error != nil {
|
||||
return resource.GetError(rsp.Error)
|
||||
@@ -469,7 +470,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
// Read the latest value
|
||||
rsp, err := s.store.Read(ctx, &resource.ReadRequest{Key: req.Key})
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
|
||||
if rsp.Error != nil {
|
||||
@@ -554,7 +555,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
Value: value,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
if rsp2.Error != nil {
|
||||
return resource.GetError(rsp2.Error)
|
||||
@@ -567,7 +568,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
}
|
||||
rsp2, err := s.store.Update(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
if rsp2.Error != nil {
|
||||
return resource.GetError(rsp2.Error)
|
||||
|
||||
@@ -7,10 +7,17 @@ package apistore_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
@@ -19,10 +26,13 @@ import (
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
storagetesting "github.com/grafana/grafana/pkg/apiserver/storage/testing"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -142,6 +152,45 @@ func TestDeleteWithSuggestionAndConflict(t *testing.T) {
|
||||
storagetesting.RunTestDeleteWithSuggestionAndConflict(ctx, t, store)
|
||||
}
|
||||
|
||||
type resourceClientMock struct {
|
||||
resource.ResourceStoreClient
|
||||
resource.ResourceIndexClient
|
||||
resource.RepositoryIndexClient
|
||||
resource.BatchStoreClient
|
||||
resource.BlobStoreClient
|
||||
resource.DiagnosticsClient
|
||||
}
|
||||
|
||||
// always return GRPC Unauthenticated code
|
||||
func (r resourceClientMock) List(ctx context.Context, in *resource.ListRequest, opts ...grpc.CallOption) (*resource.ListResponse, error) {
|
||||
return &resource.ListResponse{}, status.Error(codes.Unauthenticated, "missing token")
|
||||
}
|
||||
|
||||
func TestGRPCtoHTTPStatusMapping(t *testing.T) {
|
||||
t.Run("ensure that GRPC Unauthenticated code gets translated to HTTP StatusUnauthorized", func(t *testing.T) {
|
||||
s, _, err := apistore.NewStorage(
|
||||
&storagebackend.ConfigForResource{},
|
||||
resourceClientMock{},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
apistore.StorageOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.GetList(context.Background(), "/group/resource.grafana.app/resource/resources/namespace/default", storage.ListOptions{}, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
var statusError *apierrors.StatusError
|
||||
ok := errors.As(err, &statusError)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, int(statusError.Status().Code), http.StatusUnauthorized)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: this test relies on update
|
||||
//func TestDeleteWithSuggestionOfDeletedObject(t *testing.T) {
|
||||
// ctx, store, destroyFunc, err := testSetup(t)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"gocloud.dev/blob/fileblob"
|
||||
"google.golang.org/grpc"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/grpcclient"
|
||||
"github.com/grafana/dskit/middleware"
|
||||
|
||||
infraDB "github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@@ -39,6 +42,10 @@ type Options struct {
|
||||
Docs resource.DocumentBuilderSupplier
|
||||
}
|
||||
|
||||
type ClientMetrics struct {
|
||||
requestDuration *prometheus.HistogramVec
|
||||
}
|
||||
|
||||
// This adds a UnifiedStorage client into the wire dependency tree
|
||||
func ProvideUnifiedStorageClient(opts *Options) (resource.ResourceClient, error) {
|
||||
// See: apiserver.ApplyGrafanaConfig(cfg, features, o)
|
||||
@@ -104,11 +111,8 @@ func newClient(opts options.StorageOptions,
|
||||
return nil, fmt.Errorf("expecting address for storage_type: %s", opts.StorageType)
|
||||
}
|
||||
|
||||
// Create a connection to the gRPC server
|
||||
conn, err := grpc.NewClient(opts.Address,
|
||||
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
// Create a connection to the gRPC server.
|
||||
conn, err := grpcConn(opts.Address, reg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -153,3 +157,41 @@ func newResourceClient(conn *grpc.ClientConn, cfg *setting.Cfg, features feature
|
||||
}
|
||||
return resource.NewRemoteResourceClient(tracer, conn, clientCfgMapping(grpcutils.ReadGrpcClientConfig(cfg)), cfg.Env == setting.Dev)
|
||||
}
|
||||
|
||||
// grpcConn creates a new gRPC connection to the provided address.
|
||||
func grpcConn(address string, reg prometheus.Registerer) (*grpc.ClientConn, error) {
|
||||
// This works for now as the Provide function is only called once during startup.
|
||||
// We might eventually want to tie this factory to a struct for more runtime control.
|
||||
metrics := ClientMetrics{
|
||||
requestDuration: promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "resource_server_client_request_duration_seconds",
|
||||
Help: "Time spent executing requests to the resource server.",
|
||||
Buckets: prometheus.ExponentialBuckets(0.008, 4, 7),
|
||||
}, []string{"operation", "status_code"}),
|
||||
}
|
||||
|
||||
// Report gRPC status code errors as labels.
|
||||
var instrumentationOptions []middleware.InstrumentationOption
|
||||
instrumentationOptions = append(instrumentationOptions, middleware.ReportGRPCStatusOption)
|
||||
unary, stream := grpcclient.Instrument(metrics.requestDuration, instrumentationOptions...)
|
||||
|
||||
// We can later pass in the gRPC config here, i.e. to set MaxRecvMsgSize etc.
|
||||
cfg := grpcclient.Config{}
|
||||
opts, err := cfg.DialOption(unary, stream)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not instrument grpc client: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, grpc.WithStatsHandler(otelgrpc.NewClientHandler()))
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
|
||||
// Use round_robin to balance requests more evenly over the available Storage server.
|
||||
opts = append(opts, grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`))
|
||||
|
||||
// Disable looking up service config from TXT DNS records.
|
||||
// This reduces the number of requests made to the DNS servers.
|
||||
opts = append(opts, grpc.WithDisableServiceConfig())
|
||||
|
||||
// Create a connection to the gRPC server
|
||||
return grpc.NewClient(address, opts...)
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
grpcstatus "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Package-level errors.
|
||||
@@ -67,10 +70,16 @@ func AsErrorResult(err error) *ErrorResult {
|
||||
return res
|
||||
}
|
||||
|
||||
// TODO... better conversion??
|
||||
code := 500
|
||||
|
||||
st, ok := grpcstatus.FromError(err)
|
||||
if ok {
|
||||
code = runtime.HTTPStatusFromCode(st.Code())
|
||||
}
|
||||
|
||||
return &ErrorResult{
|
||||
Message: err.Error(),
|
||||
Code: 500,
|
||||
Code: int32(code),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ require (
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250121113133-e747350fee2d // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/stretchr/testify v1.10.0
|
||||
@@ -117,7 +118,7 @@ require (
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664 // indirect
|
||||
github.com/grafana/dataplane/sdata v0.0.9 // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect
|
||||
github.com/grafana/grafana-aws-sdk v0.31.5 // indirect
|
||||
@@ -126,7 +127,6 @@ require (
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
|
||||
github.com/grafana/sqlds/v4 v4.1.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
|
||||
@@ -405,8 +405,8 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
|
||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 h1:dG/UKAjY/KlKp9fY8aEm+gSQHHRmPm5q+9cea3hRSu8=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU=
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664 h1:avtd90OVg2wMHz5pUsTWypu4rEt5bn5H0kOLXa7xgqc=
|
||||
github.com/grafana/alerting v0.0.0-20250218192337-92d5f29b5664/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM=
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7 h1:NTMmow+74I3Jb033xhbRgWQS7A//5TDhiM4tl7bsVP4=
|
||||
github.com/grafana/authlib v0.0.0-20250219100139-6a3b1bbb50e7/go.mod h1:T3X4z0ejGfJOiOmZLFeKCRT/yxWJq/RtclAc/PHj/w4=
|
||||
github.com/grafana/authlib/types v0.0.0-20250219092154-21ce22b49f31 h1:EokLC5grHwLPs4tXW8T6E8187H1e5G9AP0QQ5B60HbA=
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package azmoncredentials
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-azure-sdk-go/v2/azcredentials"
|
||||
"github.com/grafana/grafana-azure-sdk-go/v2/azsettings"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data/utils/maputil"
|
||||
)
|
||||
|
||||
@@ -95,7 +97,7 @@ func getFromLegacy(data map[string]interface{}, secureData map[string]string) (a
|
||||
clientSecret := secureData["clientSecret"]
|
||||
|
||||
if secureData["clientSecret"] == "" {
|
||||
return nil, fmt.Errorf("unable to instantiate credentials, clientSecret must be set")
|
||||
return nil, backend.DownstreamError(errors.New("unable to instantiate credentials, clientSecret must be set"))
|
||||
}
|
||||
|
||||
credentials := &azcredentials.AzureClientSecretCredentials{
|
||||
|
||||
@@ -269,7 +269,11 @@ func (e *AzureMonitorDatasource) retrieveSubscriptionDetails(cli *http.Client, c
|
||||
|
||||
res, err := cli.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to request subscription details: %s", err)
|
||||
err = fmt.Errorf("failed to request subscription details: %s", err)
|
||||
if backend.IsDownstreamHTTPError(err) {
|
||||
err = backend.DownstreamError(err)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
||||
@@ -114,8 +114,10 @@ export class ScopedResourceClient<T = object, S = object, K = string> implements
|
||||
return getBackendSrv().put<Resource<T, S, K>>(`${this.url}/${obj.metadata.name}`, obj);
|
||||
}
|
||||
|
||||
public async delete(name: string): Promise<MetaStatus> {
|
||||
return getBackendSrv().delete<MetaStatus>(`${this.url}/${name}`);
|
||||
public async delete(name: string, showSuccessAlert: boolean): Promise<MetaStatus> {
|
||||
return getBackendSrv().delete<MetaStatus>(`${this.url}/${name}`, undefined, {
|
||||
showSuccessAlert,
|
||||
});
|
||||
}
|
||||
|
||||
private parseListOptionsSelector = parseListOptionsSelector;
|
||||
|
||||
@@ -209,7 +209,7 @@ export interface ResourceClient<T = object, S = object, K = string> {
|
||||
subresource<S>(name: string, path: string): Promise<S>;
|
||||
list(opts?: ListOptions): Promise<ResourceList<T, S, K>>;
|
||||
update(obj: ResourceForCreate<T, K>): Promise<Resource<T, S, K>>;
|
||||
delete(name: string): Promise<MetaStatus>;
|
||||
delete(name: string, showSuccessAlert?: boolean): Promise<MetaStatus>;
|
||||
}
|
||||
|
||||
export interface K8sAPIGroup {
|
||||
|
||||
@@ -305,17 +305,32 @@ export const browseDashboardsAPI = createApi({
|
||||
// Delete all the dashboards sequentially
|
||||
// TODO error handling here
|
||||
for (const dashboardUID of selectedDashboards) {
|
||||
const response = getDashboardAPI().deleteDashboard(dashboardUID, false);
|
||||
const response = await getDashboardAPI().deleteDashboard(dashboardUID, true);
|
||||
|
||||
// @ts-expect-error
|
||||
const name = response?.data?.title;
|
||||
// handling success alerts for these feature toggles
|
||||
// for legacy response, the success alert will be triggered by showSuccessAlert function in public/app/core/services/backend_srv.ts
|
||||
if (config.featureToggles.dashboardRestore) {
|
||||
const name = response?.title;
|
||||
|
||||
if (name) {
|
||||
if (name) {
|
||||
const payload =
|
||||
config.featureToggles.useV2DashboardsAPI || config.featureToggles.kubernetesDashboards
|
||||
? ['Dashboard moved to Recently deleted']
|
||||
: [
|
||||
t('browse-dashboards.soft-delete.success', 'Dashboard {{name}} moved to Recently deleted', {
|
||||
name,
|
||||
}),
|
||||
];
|
||||
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload,
|
||||
});
|
||||
}
|
||||
} else if (config.featureToggles.useV2DashboardsAPI || config.featureToggles.kubernetesDashboards) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [
|
||||
t('browse-dashboards.soft-delete.success', 'Dashboard {{name}} moved to Recently deleted', { name }),
|
||||
],
|
||||
payload: ['Dashboard deleted'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ export class LegacyDashboardAPI implements DashboardAPI<DashboardDTO, Dashboard>
|
||||
}
|
||||
|
||||
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse> {
|
||||
return getBackendSrv().delete<DeleteDashboardResponse>(`/api/dashboards/uid/${uid}`, { showSuccessAlert });
|
||||
return getBackendSrv().delete<DeleteDashboardResponse>(`/api/dashboards/uid/${uid}`, undefined, {
|
||||
showSuccessAlert,
|
||||
});
|
||||
}
|
||||
|
||||
async getDashboardDTO(uid: string, params?: UrlQueryMap) {
|
||||
|
||||
@@ -85,7 +85,7 @@ export class K8sDashboardAPI implements DashboardAPI<DashboardDTO, Dashboard> {
|
||||
}
|
||||
|
||||
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse> {
|
||||
return this.client.delete(uid).then((v) => ({
|
||||
return this.client.delete(uid, showSuccessAlert).then((v) => ({
|
||||
id: 0,
|
||||
message: v.message,
|
||||
title: 'deleted',
|
||||
|
||||
@@ -87,7 +87,11 @@ export class K8sDashboardV2API
|
||||
}
|
||||
|
||||
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
return this.client.delete(uid, showSuccessAlert).then((v) => ({
|
||||
id: 0,
|
||||
message: v.message,
|
||||
title: 'deleted',
|
||||
}));
|
||||
}
|
||||
|
||||
async saveDashboard(options: SaveDashboardCommand<DashboardV2Spec>): Promise<SaveDashboardResponseDTO> {
|
||||
|
||||
@@ -62,21 +62,24 @@ export async function isPluginFrontendSandboxEligible({
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't run grafana-signed plugins in sandbox
|
||||
// grafana signature and internal plugins are not allowed in the sandbox
|
||||
return isPluginSignatureEligibleForSandbox({ pluginId });
|
||||
}
|
||||
|
||||
async function isPluginSignatureEligibleForSandbox({ pluginId }: SandboxEligibilityCheckParams): Promise<boolean> {
|
||||
try {
|
||||
//this can fail if gcom is not accesible
|
||||
const details = await getPluginDetails(pluginId);
|
||||
return details.signatureType !== PluginSignatureType.grafana && details.signature !== 'internal';
|
||||
// this can fail if we are trying to fetch settings of a non-installed plugin
|
||||
const pluginMeta = await getPluginSettings(pluginId, { showErrorAlert: false });
|
||||
return pluginMeta.signatureType !== PluginSignatureType.grafana && pluginMeta.signature !== 'internal';
|
||||
} catch (e) {
|
||||
try {
|
||||
// this can fail if we are trying to fetch settings of a non-installed plugin
|
||||
const pluginMeta = await getPluginSettings(pluginId, { showErrorAlert: false });
|
||||
return pluginMeta.signatureType !== PluginSignatureType.grafana && pluginMeta.signature !== 'internal';
|
||||
//this can fail if gcom is not accesible
|
||||
const details = await getPluginDetails(pluginId);
|
||||
return details.signatureType !== PluginSignatureType.grafana && details.signature !== 'internal';
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user