From a1ee84f7578c71550ead22456bc09d0d110c7367 Mon Sep 17 00:00:00 2001 From: William Wernert Date: Thu, 1 Aug 2024 11:57:14 -0400 Subject: [PATCH 01/25] Alerting: Remove duplicate tracing middleware from prom writer (#91353) Remove duplicate tracing middleware from prom writer --- pkg/services/ngalert/ngalert.go | 6 +++--- pkg/services/ngalert/schedule/recording_rule_test.go | 3 +-- pkg/services/ngalert/writer/prom.go | 11 ++--------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index aac3a453571..c86a1d00c30 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -346,7 +346,7 @@ func (ng *AlertNG) init() error { evalFactory := eval.NewEvaluatorFactory(ng.Cfg.UnifiedAlerting, ng.DataSourceCache, ng.ExpressionService) conditionValidator := eval.NewConditionValidator(ng.DataSourceCache, ng.ExpressionService, ng.pluginsStore) - recordingWriter, err := createRecordingWriter(ng.FeatureToggles, ng.Cfg.UnifiedAlerting.RecordingRules, ng.httpClientProvider, clk, ng.tracer, ng.Metrics.GetRemoteWriterMetrics()) + recordingWriter, err := createRecordingWriter(ng.FeatureToggles, ng.Cfg.UnifiedAlerting.RecordingRules, ng.httpClientProvider, clk, ng.Metrics.GetRemoteWriterMetrics()) if err != nil { return fmt.Errorf("failed to initialize recording writer: %w", err) } @@ -663,11 +663,11 @@ func createRemoteAlertmanager(cfg remote.AlertmanagerConfig, kvstore kvstore.KVS return remote.NewAlertmanager(cfg, notifier.NewFileStore(cfg.OrgID, kvstore), decryptFn, autogenFn, m, tracer) } -func createRecordingWriter(featureToggles featuremgmt.FeatureToggles, settings setting.RecordingRuleSettings, httpClientProvider httpclient.Provider, clock clock.Clock, tracer tracing.Tracer, m *metrics.RemoteWriter) (schedule.RecordingWriter, error) { +func createRecordingWriter(featureToggles featuremgmt.FeatureToggles, settings setting.RecordingRuleSettings, httpClientProvider httpclient.Provider, clock clock.Clock, m *metrics.RemoteWriter) (schedule.RecordingWriter, error) { logger := log.New("ngalert.writer") if featureToggles.IsEnabledGlobally(featuremgmt.FlagGrafanaManagedRecordingRules) { - return writer.NewPrometheusWriter(settings, httpClientProvider, clock, tracer, logger, m) + return writer.NewPrometheusWriter(settings, httpClientProvider, clock, logger, m) } return writer.NoopWriter{}, nil diff --git a/pkg/services/ngalert/schedule/recording_rule_test.go b/pkg/services/ngalert/schedule/recording_rule_test.go index c46144c6d58..8dba179927d 100644 --- a/pkg/services/ngalert/schedule/recording_rule_test.go +++ b/pkg/services/ngalert/schedule/recording_rule_test.go @@ -19,7 +19,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/ngalert/metrics" models "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -532,7 +531,7 @@ func withQueryForHealth(health string) models.AlertRuleMutator { func setupWriter(t *testing.T, target *writer.TestRemoteWriteTarget, reg prometheus.Registerer) *writer.PrometheusWriter { provider := testClientProvider{} m := metrics.NewNGAlert(reg) - wr, err := writer.NewPrometheusWriter(target.ClientSettings(), provider, clock.NewMock(), tracing.InitializeTracerForTest(), log.NewNopLogger(), m.GetRemoteWriterMetrics()) + wr, err := writer.NewPrometheusWriter(target.ClientSettings(), provider, clock.NewMock(), log.NewNopLogger(), m.GetRemoteWriterMetrics()) require.NoError(t, err) return wr } diff --git a/pkg/services/ngalert/writer/prom.go b/pkg/services/ngalert/writer/prom.go index b01baf5197e..ef860a599e7 100644 --- a/pkg/services/ngalert/writer/prom.go +++ b/pkg/services/ngalert/writer/prom.go @@ -11,7 +11,6 @@ import ( "github.com/benbjohnson/clock" "github.com/grafana/dataplane/sdata/numeric" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/setting" @@ -106,7 +105,6 @@ func NewPrometheusWriter( settings setting.RecordingRuleSettings, httpClientProvider HttpClientProvider, clock clock.Clock, - tracer tracing.Tracer, l log.Logger, metrics *metrics.RemoteWriter, ) (*PrometheusWriter, error) { @@ -119,14 +117,9 @@ func NewPrometheusWriter( headers.Add(k, v) } - middlewares := []httpclient.Middleware{ - httpclient.TracingMiddleware(tracer), - } - cl, err := httpClientProvider.New(httpclient.Options{ - Middlewares: middlewares, - BasicAuth: createAuthOpts(settings.BasicAuthUsername, settings.BasicAuthPassword), - Header: headers, + BasicAuth: createAuthOpts(settings.BasicAuthUsername, settings.BasicAuthPassword), + Header: headers, }) if err != nil { return nil, err From dbb64b3ff054f36fe9bf5d28d95a644c8d8d73ca Mon Sep 17 00:00:00 2001 From: Joao Silva <100691367+JoaoSilvaGrafana@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:27:44 +0100 Subject: [PATCH 02/25] Icons: Cache Bookmark icons (#91332) --- public/app/core/icons/cached.json | 4 +- public/app/core/icons/iconBundle.ts | 588 ++++++++++++++-------------- scripts/webpack/webpack.common.js | 2 +- 3 files changed, 300 insertions(+), 294 deletions(-) diff --git a/public/app/core/icons/cached.json b/public/app/core/icons/cached.json index 4bfb615427e..9d2af6a7ff5 100644 --- a/public/app/core/icons/cached.json +++ b/public/app/core/icons/cached.json @@ -25,6 +25,7 @@ "unicons/bell-slash", "unicons/bolt", "unicons/book", + "unicons/bookmark", "unicons/book-open", "unicons/brackets-curly", "unicons/bug", @@ -170,5 +171,6 @@ "mono/heart-break", "mono/panel-add", "mono/library-panel", - "unicons/record-audio" + "unicons/record-audio", + "solid/bookmark" ] diff --git a/public/app/core/icons/iconBundle.ts b/public/app/core/icons/iconBundle.ts index f493d0a71c2..b8ed8ae2cbf 100644 --- a/public/app/core/icons/iconBundle.ts +++ b/public/app/core/icons/iconBundle.ts @@ -33,152 +33,154 @@ import u1022 from '../../../img/icons/unicons/bell.svg'; import u1023 from '../../../img/icons/unicons/bell-slash.svg'; import u1024 from '../../../img/icons/unicons/bolt.svg'; import u1025 from '../../../img/icons/unicons/book.svg'; -import u1026 from '../../../img/icons/unicons/book-open.svg'; -import u1027 from '../../../img/icons/unicons/brackets-curly.svg'; -import u1028 from '../../../img/icons/unicons/bug.svg'; -import u1029 from '../../../img/icons/unicons/building.svg'; -import u1030 from '../../../img/icons/unicons/calculator-alt.svg'; -import u1031 from '../../../img/icons/unicons/calendar-alt.svg'; -import u1032 from '../../../img/icons/unicons/calendar-slash.svg'; -import u1033 from '../../../img/icons/unicons/camera.svg'; -import u1034 from '../../../img/icons/unicons/channel-add.svg'; -import u1035 from '../../../img/icons/unicons/chart-line.svg'; -import u1036 from '../../../img/icons/unicons/check.svg'; -import u1037 from '../../../img/icons/unicons/check-circle.svg'; -import u1038 from '../../../img/icons/unicons/circle.svg'; -import u1039 from '../../../img/icons/unicons/clipboard-alt.svg'; -import u1040 from '../../../img/icons/unicons/clock-nine.svg'; -import u1041 from '../../../img/icons/unicons/cloud.svg'; -import u1042 from '../../../img/icons/unicons/cloud-download.svg'; -import u1043 from '../../../img/icons/unicons/code-branch.svg'; -import u1044 from '../../../img/icons/unicons/cog.svg'; -import u1045 from '../../../img/icons/unicons/columns.svg'; -import u1046 from '../../../img/icons/unicons/comment-alt.svg'; -import u1047 from '../../../img/icons/unicons/comment-alt-share.svg'; -import u1048 from '../../../img/icons/unicons/comments-alt.svg'; -import u1049 from '../../../img/icons/unicons/compass.svg'; -import u1050 from '../../../img/icons/unicons/copy.svg'; -import u1051 from '../../../img/icons/unicons/corner-down-right-alt.svg'; -import u1052 from '../../../img/icons/unicons/cube.svg'; -import u1053 from '../../../img/icons/unicons/dashboard.svg'; -import u1054 from '../../../img/icons/unicons/database.svg'; -import u1055 from '../../../img/icons/unicons/document-info.svg'; -import u1056 from '../../../img/icons/unicons/download-alt.svg'; -import u1057 from '../../../img/icons/unicons/draggabledots.svg'; -import u1058 from '../../../img/icons/unicons/edit.svg'; -import u1059 from '../../../img/icons/unicons/ellipsis-v.svg'; -import u1060 from '../../../img/icons/unicons/ellipsis-h.svg'; -import u1061 from '../../../img/icons/unicons/envelope.svg'; -import u1062 from '../../../img/icons/unicons/exchange-alt.svg'; -import u1063 from '../../../img/icons/unicons/exclamation-circle.svg'; -import u1064 from '../../../img/icons/unicons/exclamation-triangle.svg'; -import u1065 from '../../../img/icons/unicons/external-link-alt.svg'; -import u1066 from '../../../img/icons/unicons/eye.svg'; -import u1067 from '../../../img/icons/unicons/eye-slash.svg'; -import u1068 from '../../../img/icons/unicons/file-alt.svg'; -import u1069 from '../../../img/icons/unicons/file-blank.svg'; -import u1070 from '../../../img/icons/unicons/filter.svg'; -import u1071 from '../../../img/icons/unicons/folder.svg'; -import u1072 from '../../../img/icons/unicons/folder-open.svg'; -import u1073 from '../../../img/icons/unicons/folder-plus.svg'; -import u1074 from '../../../img/icons/unicons/folder-upload.svg'; -import u1075 from '../../../img/icons/unicons/forward.svg'; -import u1076 from '../../../img/icons/unicons/graph-bar.svg'; -import u1077 from '../../../img/icons/unicons/history.svg'; -import u1078 from '../../../img/icons/unicons/history-alt.svg'; -import u1079 from '../../../img/icons/unicons/home-alt.svg'; -import u1080 from '../../../img/icons/unicons/import.svg'; -import u1081 from '../../../img/icons/unicons/info.svg'; -import u1082 from '../../../img/icons/unicons/info-circle.svg'; -import u1083 from '../../../img/icons/unicons/k6.svg'; -import u1084 from '../../../img/icons/unicons/key-skeleton-alt.svg'; -import u1085 from '../../../img/icons/unicons/keyboard.svg'; -import u1086 from '../../../img/icons/unicons/link.svg'; -import u1087 from '../../../img/icons/unicons/list-ul.svg'; -import u1088 from '../../../img/icons/unicons/lock.svg'; -import u1089 from '../../../img/icons/unicons/minus.svg'; -import u1090 from '../../../img/icons/unicons/minus-circle.svg'; -import u1091 from '../../../img/icons/unicons/mobile-android.svg'; -import u1092 from '../../../img/icons/unicons/monitor.svg'; -import u1093 from '../../../img/icons/unicons/pause.svg'; -import u1094 from '../../../img/icons/unicons/pen.svg'; -import u1095 from '../../../img/icons/unicons/play.svg'; -import u1096 from '../../../img/icons/unicons/plug.svg'; -import u1097 from '../../../img/icons/unicons/plus.svg'; -import u1098 from '../../../img/icons/unicons/plus-circle.svg'; -import u1099 from '../../../img/icons/unicons/power.svg'; -import u1100 from '../../../img/icons/unicons/presentation-play.svg'; -import u1101 from '../../../img/icons/unicons/process.svg'; -import u1102 from '../../../img/icons/unicons/question-circle.svg'; -import u1103 from '../../../img/icons/unicons/repeat.svg'; -import u1104 from '../../../img/icons/unicons/rocket.svg'; -import u1105 from '../../../img/icons/unicons/rss.svg'; -import u1106 from '../../../img/icons/unicons/save.svg'; -import u1107 from '../../../img/icons/unicons/search.svg'; -import u1108 from '../../../img/icons/unicons/search-minus.svg'; -import u1109 from '../../../img/icons/unicons/search-plus.svg'; -import u1110 from '../../../img/icons/unicons/share-alt.svg'; -import u1111 from '../../../img/icons/unicons/shield.svg'; -import u1112 from '../../../img/icons/unicons/signal.svg'; -import u1113 from '../../../img/icons/unicons/signin.svg'; -import u1114 from '../../../img/icons/unicons/signout.svg'; -import u1115 from '../../../img/icons/unicons/sitemap.svg'; -import u1116 from '../../../img/icons/unicons/slack.svg'; -import u1117 from '../../../img/icons/unicons/sliders-v-alt.svg'; -import u1118 from '../../../img/icons/unicons/sort-amount-down.svg'; -import u1119 from '../../../img/icons/unicons/sort-amount-up.svg'; -import u1120 from '../../../img/icons/unicons/square-shape.svg'; -import u1121 from '../../../img/icons/unicons/star.svg'; -import u1122 from '../../../img/icons/unicons/step-backward.svg'; -import u1123 from '../../../img/icons/unicons/sync.svg'; -import u1124 from '../../../img/icons/unicons/stopwatch.svg'; -import u1125 from '../../../img/icons/unicons/table.svg'; -import u1126 from '../../../img/icons/unicons/tag-alt.svg'; -import u1127 from '../../../img/icons/unicons/times.svg'; -import u1128 from '../../../img/icons/unicons/trash-alt.svg'; -import u1129 from '../../../img/icons/unicons/unlock.svg'; -import u1130 from '../../../img/icons/unicons/upload.svg'; -import u1131 from '../../../img/icons/unicons/user.svg'; -import u1132 from '../../../img/icons/unicons/users-alt.svg'; -import u1133 from '../../../img/icons/unicons/wrap-text.svg'; -import u1134 from '../../../img/icons/unicons/cloud-upload.svg'; -import u1135 from '../../../img/icons/unicons/credit-card.svg'; -import u1136 from '../../../img/icons/unicons/file-copy-alt.svg'; -import u1137 from '../../../img/icons/unicons/fire.svg'; -import u1138 from '../../../img/icons/unicons/hourglass.svg'; -import u1139 from '../../../img/icons/unicons/layer-group.svg'; -import u1140 from '../../../img/icons/unicons/layers-alt.svg'; -import u1141 from '../../../img/icons/unicons/line-alt.svg'; -import u1142 from '../../../img/icons/unicons/list-ui-alt.svg'; -import u1143 from '../../../img/icons/unicons/message.svg'; -import u1144 from '../../../img/icons/unicons/palette.svg'; -import u1145 from '../../../img/icons/unicons/percentage.svg'; -import u1146 from '../../../img/icons/unicons/shield-exclamation.svg'; -import u1147 from '../../../img/icons/unicons/plus-square.svg'; -import u1148 from '../../../img/icons/unicons/x.svg'; -import u1149 from '../../../img/icons/unicons/capture.svg'; -import u1150 from '../../../img/icons/custom/gf-grid.svg'; -import u1151 from '../../../img/icons/custom/gf-landscape.svg'; -import u1152 from '../../../img/icons/custom/gf-layout-simple.svg'; -import u1153 from '../../../img/icons/custom/gf-portrait.svg'; -import u1154 from '../../../img/icons/custom/gf-show-context.svg'; -import u1155 from '../../../img/icons/custom/gf-bar-alignment-after.svg'; -import u1156 from '../../../img/icons/custom/gf-bar-alignment-before.svg'; -import u1157 from '../../../img/icons/custom/gf-bar-alignment-center.svg'; -import u1158 from '../../../img/icons/custom/gf-interpolation-linear.svg'; -import u1159 from '../../../img/icons/custom/gf-interpolation-smooth.svg'; -import u1160 from '../../../img/icons/custom/gf-interpolation-step-after.svg'; -import u1161 from '../../../img/icons/custom/gf-interpolation-step-before.svg'; -import u1162 from '../../../img/icons/custom/gf-logs.svg'; -import u1163 from '../../../img/icons/custom/gf-movepane-left.svg'; -import u1164 from '../../../img/icons/custom/gf-movepane-right.svg'; -import u1165 from '../../../img/icons/mono/favorite.svg'; -import u1166 from '../../../img/icons/mono/grafana.svg'; -import u1167 from '../../../img/icons/mono/heart.svg'; -import u1168 from '../../../img/icons/mono/heart-break.svg'; -import u1169 from '../../../img/icons/mono/panel-add.svg'; -import u1170 from '../../../img/icons/mono/library-panel.svg'; -import u1171 from '../../../img/icons/unicons/record-audio.svg'; +import u1026 from '../../../img/icons/unicons/bookmark.svg'; +import u1027 from '../../../img/icons/unicons/book-open.svg'; +import u1028 from '../../../img/icons/unicons/brackets-curly.svg'; +import u1029 from '../../../img/icons/unicons/bug.svg'; +import u1030 from '../../../img/icons/unicons/building.svg'; +import u1031 from '../../../img/icons/unicons/calculator-alt.svg'; +import u1032 from '../../../img/icons/unicons/calendar-alt.svg'; +import u1033 from '../../../img/icons/unicons/calendar-slash.svg'; +import u1034 from '../../../img/icons/unicons/camera.svg'; +import u1035 from '../../../img/icons/unicons/channel-add.svg'; +import u1036 from '../../../img/icons/unicons/chart-line.svg'; +import u1037 from '../../../img/icons/unicons/check.svg'; +import u1038 from '../../../img/icons/unicons/check-circle.svg'; +import u1039 from '../../../img/icons/unicons/circle.svg'; +import u1040 from '../../../img/icons/unicons/clipboard-alt.svg'; +import u1041 from '../../../img/icons/unicons/clock-nine.svg'; +import u1042 from '../../../img/icons/unicons/cloud.svg'; +import u1043 from '../../../img/icons/unicons/cloud-download.svg'; +import u1044 from '../../../img/icons/unicons/code-branch.svg'; +import u1045 from '../../../img/icons/unicons/cog.svg'; +import u1046 from '../../../img/icons/unicons/columns.svg'; +import u1047 from '../../../img/icons/unicons/comment-alt.svg'; +import u1048 from '../../../img/icons/unicons/comment-alt-share.svg'; +import u1049 from '../../../img/icons/unicons/comments-alt.svg'; +import u1050 from '../../../img/icons/unicons/compass.svg'; +import u1051 from '../../../img/icons/unicons/copy.svg'; +import u1052 from '../../../img/icons/unicons/corner-down-right-alt.svg'; +import u1053 from '../../../img/icons/unicons/cube.svg'; +import u1054 from '../../../img/icons/unicons/dashboard.svg'; +import u1055 from '../../../img/icons/unicons/database.svg'; +import u1056 from '../../../img/icons/unicons/document-info.svg'; +import u1057 from '../../../img/icons/unicons/download-alt.svg'; +import u1058 from '../../../img/icons/unicons/draggabledots.svg'; +import u1059 from '../../../img/icons/unicons/edit.svg'; +import u1060 from '../../../img/icons/unicons/ellipsis-v.svg'; +import u1061 from '../../../img/icons/unicons/ellipsis-h.svg'; +import u1062 from '../../../img/icons/unicons/envelope.svg'; +import u1063 from '../../../img/icons/unicons/exchange-alt.svg'; +import u1064 from '../../../img/icons/unicons/exclamation-circle.svg'; +import u1065 from '../../../img/icons/unicons/exclamation-triangle.svg'; +import u1066 from '../../../img/icons/unicons/external-link-alt.svg'; +import u1067 from '../../../img/icons/unicons/eye.svg'; +import u1068 from '../../../img/icons/unicons/eye-slash.svg'; +import u1069 from '../../../img/icons/unicons/file-alt.svg'; +import u1070 from '../../../img/icons/unicons/file-blank.svg'; +import u1071 from '../../../img/icons/unicons/filter.svg'; +import u1072 from '../../../img/icons/unicons/folder.svg'; +import u1073 from '../../../img/icons/unicons/folder-open.svg'; +import u1074 from '../../../img/icons/unicons/folder-plus.svg'; +import u1075 from '../../../img/icons/unicons/folder-upload.svg'; +import u1076 from '../../../img/icons/unicons/forward.svg'; +import u1077 from '../../../img/icons/unicons/graph-bar.svg'; +import u1078 from '../../../img/icons/unicons/history.svg'; +import u1079 from '../../../img/icons/unicons/history-alt.svg'; +import u1080 from '../../../img/icons/unicons/home-alt.svg'; +import u1081 from '../../../img/icons/unicons/import.svg'; +import u1082 from '../../../img/icons/unicons/info.svg'; +import u1083 from '../../../img/icons/unicons/info-circle.svg'; +import u1084 from '../../../img/icons/unicons/k6.svg'; +import u1085 from '../../../img/icons/unicons/key-skeleton-alt.svg'; +import u1086 from '../../../img/icons/unicons/keyboard.svg'; +import u1087 from '../../../img/icons/unicons/link.svg'; +import u1088 from '../../../img/icons/unicons/list-ul.svg'; +import u1089 from '../../../img/icons/unicons/lock.svg'; +import u1090 from '../../../img/icons/unicons/minus.svg'; +import u1091 from '../../../img/icons/unicons/minus-circle.svg'; +import u1092 from '../../../img/icons/unicons/mobile-android.svg'; +import u1093 from '../../../img/icons/unicons/monitor.svg'; +import u1094 from '../../../img/icons/unicons/pause.svg'; +import u1095 from '../../../img/icons/unicons/pen.svg'; +import u1096 from '../../../img/icons/unicons/play.svg'; +import u1097 from '../../../img/icons/unicons/plug.svg'; +import u1098 from '../../../img/icons/unicons/plus.svg'; +import u1099 from '../../../img/icons/unicons/plus-circle.svg'; +import u1100 from '../../../img/icons/unicons/power.svg'; +import u1101 from '../../../img/icons/unicons/presentation-play.svg'; +import u1102 from '../../../img/icons/unicons/process.svg'; +import u1103 from '../../../img/icons/unicons/question-circle.svg'; +import u1104 from '../../../img/icons/unicons/repeat.svg'; +import u1105 from '../../../img/icons/unicons/rocket.svg'; +import u1106 from '../../../img/icons/unicons/rss.svg'; +import u1107 from '../../../img/icons/unicons/save.svg'; +import u1108 from '../../../img/icons/unicons/search.svg'; +import u1109 from '../../../img/icons/unicons/search-minus.svg'; +import u1110 from '../../../img/icons/unicons/search-plus.svg'; +import u1111 from '../../../img/icons/unicons/share-alt.svg'; +import u1112 from '../../../img/icons/unicons/shield.svg'; +import u1113 from '../../../img/icons/unicons/signal.svg'; +import u1114 from '../../../img/icons/unicons/signin.svg'; +import u1115 from '../../../img/icons/unicons/signout.svg'; +import u1116 from '../../../img/icons/unicons/sitemap.svg'; +import u1117 from '../../../img/icons/unicons/slack.svg'; +import u1118 from '../../../img/icons/unicons/sliders-v-alt.svg'; +import u1119 from '../../../img/icons/unicons/sort-amount-down.svg'; +import u1120 from '../../../img/icons/unicons/sort-amount-up.svg'; +import u1121 from '../../../img/icons/unicons/square-shape.svg'; +import u1122 from '../../../img/icons/unicons/star.svg'; +import u1123 from '../../../img/icons/unicons/step-backward.svg'; +import u1124 from '../../../img/icons/unicons/sync.svg'; +import u1125 from '../../../img/icons/unicons/stopwatch.svg'; +import u1126 from '../../../img/icons/unicons/table.svg'; +import u1127 from '../../../img/icons/unicons/tag-alt.svg'; +import u1128 from '../../../img/icons/unicons/times.svg'; +import u1129 from '../../../img/icons/unicons/trash-alt.svg'; +import u1130 from '../../../img/icons/unicons/unlock.svg'; +import u1131 from '../../../img/icons/unicons/upload.svg'; +import u1132 from '../../../img/icons/unicons/user.svg'; +import u1133 from '../../../img/icons/unicons/users-alt.svg'; +import u1134 from '../../../img/icons/unicons/wrap-text.svg'; +import u1135 from '../../../img/icons/unicons/cloud-upload.svg'; +import u1136 from '../../../img/icons/unicons/credit-card.svg'; +import u1137 from '../../../img/icons/unicons/file-copy-alt.svg'; +import u1138 from '../../../img/icons/unicons/fire.svg'; +import u1139 from '../../../img/icons/unicons/hourglass.svg'; +import u1140 from '../../../img/icons/unicons/layer-group.svg'; +import u1141 from '../../../img/icons/unicons/layers-alt.svg'; +import u1142 from '../../../img/icons/unicons/line-alt.svg'; +import u1143 from '../../../img/icons/unicons/list-ui-alt.svg'; +import u1144 from '../../../img/icons/unicons/message.svg'; +import u1145 from '../../../img/icons/unicons/palette.svg'; +import u1146 from '../../../img/icons/unicons/percentage.svg'; +import u1147 from '../../../img/icons/unicons/shield-exclamation.svg'; +import u1148 from '../../../img/icons/unicons/plus-square.svg'; +import u1149 from '../../../img/icons/unicons/x.svg'; +import u1150 from '../../../img/icons/unicons/capture.svg'; +import u1151 from '../../../img/icons/custom/gf-grid.svg'; +import u1152 from '../../../img/icons/custom/gf-landscape.svg'; +import u1153 from '../../../img/icons/custom/gf-layout-simple.svg'; +import u1154 from '../../../img/icons/custom/gf-portrait.svg'; +import u1155 from '../../../img/icons/custom/gf-show-context.svg'; +import u1156 from '../../../img/icons/custom/gf-bar-alignment-after.svg'; +import u1157 from '../../../img/icons/custom/gf-bar-alignment-before.svg'; +import u1158 from '../../../img/icons/custom/gf-bar-alignment-center.svg'; +import u1159 from '../../../img/icons/custom/gf-interpolation-linear.svg'; +import u1160 from '../../../img/icons/custom/gf-interpolation-smooth.svg'; +import u1161 from '../../../img/icons/custom/gf-interpolation-step-after.svg'; +import u1162 from '../../../img/icons/custom/gf-interpolation-step-before.svg'; +import u1163 from '../../../img/icons/custom/gf-logs.svg'; +import u1164 from '../../../img/icons/custom/gf-movepane-left.svg'; +import u1165 from '../../../img/icons/custom/gf-movepane-right.svg'; +import u1166 from '../../../img/icons/mono/favorite.svg'; +import u1167 from '../../../img/icons/mono/grafana.svg'; +import u1168 from '../../../img/icons/mono/heart.svg'; +import u1169 from '../../../img/icons/mono/heart-break.svg'; +import u1170 from '../../../img/icons/mono/panel-add.svg'; +import u1171 from '../../../img/icons/mono/library-panel.svg'; +import u1172 from '../../../img/icons/unicons/record-audio.svg'; +import u1173 from '../../../img/icons/solid/bookmark.svg'; // do not edit this list directly // the list of icons live here: @grafana/ui/components/Icon/cached.json @@ -238,152 +240,154 @@ export function initIconCache() { cacheItem(u1023, resolvePath('unicons/bell-slash.svg')); cacheItem(u1024, resolvePath('unicons/bolt.svg')); cacheItem(u1025, resolvePath('unicons/book.svg')); - cacheItem(u1026, resolvePath('unicons/book-open.svg')); - cacheItem(u1027, resolvePath('unicons/brackets-curly.svg')); - cacheItem(u1028, resolvePath('unicons/bug.svg')); - cacheItem(u1029, resolvePath('unicons/building.svg')); - cacheItem(u1030, resolvePath('unicons/calculator-alt.svg')); - cacheItem(u1031, resolvePath('unicons/calendar-alt.svg')); - cacheItem(u1032, resolvePath('unicons/calendar-slash.svg')); - cacheItem(u1033, resolvePath('unicons/camera.svg')); - cacheItem(u1034, resolvePath('unicons/channel-add.svg')); - cacheItem(u1035, resolvePath('unicons/chart-line.svg')); - cacheItem(u1036, resolvePath('unicons/check.svg')); - cacheItem(u1037, resolvePath('unicons/check-circle.svg')); - cacheItem(u1038, resolvePath('unicons/circle.svg')); - cacheItem(u1039, resolvePath('unicons/clipboard-alt.svg')); - cacheItem(u1040, resolvePath('unicons/clock-nine.svg')); - cacheItem(u1041, resolvePath('unicons/cloud.svg')); - cacheItem(u1042, resolvePath('unicons/cloud-download.svg')); - cacheItem(u1043, resolvePath('unicons/code-branch.svg')); - cacheItem(u1044, resolvePath('unicons/cog.svg')); - cacheItem(u1045, resolvePath('unicons/columns.svg')); - cacheItem(u1046, resolvePath('unicons/comment-alt.svg')); - cacheItem(u1047, resolvePath('unicons/comment-alt-share.svg')); - cacheItem(u1048, resolvePath('unicons/comments-alt.svg')); - cacheItem(u1049, resolvePath('unicons/compass.svg')); - cacheItem(u1050, resolvePath('unicons/copy.svg')); - cacheItem(u1051, resolvePath('unicons/corner-down-right-alt.svg')); - cacheItem(u1052, resolvePath('unicons/cube.svg')); - cacheItem(u1053, resolvePath('unicons/dashboard.svg')); - cacheItem(u1054, resolvePath('unicons/database.svg')); - cacheItem(u1055, resolvePath('unicons/document-info.svg')); - cacheItem(u1056, resolvePath('unicons/download-alt.svg')); - cacheItem(u1057, resolvePath('unicons/draggabledots.svg')); - cacheItem(u1058, resolvePath('unicons/edit.svg')); - cacheItem(u1059, resolvePath('unicons/ellipsis-v.svg')); - cacheItem(u1060, resolvePath('unicons/ellipsis-h.svg')); - cacheItem(u1061, resolvePath('unicons/envelope.svg')); - cacheItem(u1062, resolvePath('unicons/exchange-alt.svg')); - cacheItem(u1063, resolvePath('unicons/exclamation-circle.svg')); - cacheItem(u1064, resolvePath('unicons/exclamation-triangle.svg')); - cacheItem(u1065, resolvePath('unicons/external-link-alt.svg')); - cacheItem(u1066, resolvePath('unicons/eye.svg')); - cacheItem(u1067, resolvePath('unicons/eye-slash.svg')); - cacheItem(u1068, resolvePath('unicons/file-alt.svg')); - cacheItem(u1069, resolvePath('unicons/file-blank.svg')); - cacheItem(u1070, resolvePath('unicons/filter.svg')); - cacheItem(u1071, resolvePath('unicons/folder.svg')); - cacheItem(u1072, resolvePath('unicons/folder-open.svg')); - cacheItem(u1073, resolvePath('unicons/folder-plus.svg')); - cacheItem(u1074, resolvePath('unicons/folder-upload.svg')); - cacheItem(u1075, resolvePath('unicons/forward.svg')); - cacheItem(u1076, resolvePath('unicons/graph-bar.svg')); - cacheItem(u1077, resolvePath('unicons/history.svg')); - cacheItem(u1078, resolvePath('unicons/history-alt.svg')); - cacheItem(u1079, resolvePath('unicons/home-alt.svg')); - cacheItem(u1080, resolvePath('unicons/import.svg')); - cacheItem(u1081, resolvePath('unicons/info.svg')); - cacheItem(u1082, resolvePath('unicons/info-circle.svg')); - cacheItem(u1083, resolvePath('unicons/k6.svg')); - cacheItem(u1084, resolvePath('unicons/key-skeleton-alt.svg')); - cacheItem(u1085, resolvePath('unicons/keyboard.svg')); - cacheItem(u1086, resolvePath('unicons/link.svg')); - cacheItem(u1087, resolvePath('unicons/list-ul.svg')); - cacheItem(u1088, resolvePath('unicons/lock.svg')); - cacheItem(u1089, resolvePath('unicons/minus.svg')); - cacheItem(u1090, resolvePath('unicons/minus-circle.svg')); - cacheItem(u1091, resolvePath('unicons/mobile-android.svg')); - cacheItem(u1092, resolvePath('unicons/monitor.svg')); - cacheItem(u1093, resolvePath('unicons/pause.svg')); - cacheItem(u1094, resolvePath('unicons/pen.svg')); - cacheItem(u1095, resolvePath('unicons/play.svg')); - cacheItem(u1096, resolvePath('unicons/plug.svg')); - cacheItem(u1097, resolvePath('unicons/plus.svg')); - cacheItem(u1098, resolvePath('unicons/plus-circle.svg')); - cacheItem(u1099, resolvePath('unicons/power.svg')); - cacheItem(u1100, resolvePath('unicons/presentation-play.svg')); - cacheItem(u1101, resolvePath('unicons/process.svg')); - cacheItem(u1102, resolvePath('unicons/question-circle.svg')); - cacheItem(u1103, resolvePath('unicons/repeat.svg')); - cacheItem(u1104, resolvePath('unicons/rocket.svg')); - cacheItem(u1105, resolvePath('unicons/rss.svg')); - cacheItem(u1106, resolvePath('unicons/save.svg')); - cacheItem(u1107, resolvePath('unicons/search.svg')); - cacheItem(u1108, resolvePath('unicons/search-minus.svg')); - cacheItem(u1109, resolvePath('unicons/search-plus.svg')); - cacheItem(u1110, resolvePath('unicons/share-alt.svg')); - cacheItem(u1111, resolvePath('unicons/shield.svg')); - cacheItem(u1112, resolvePath('unicons/signal.svg')); - cacheItem(u1113, resolvePath('unicons/signin.svg')); - cacheItem(u1114, resolvePath('unicons/signout.svg')); - cacheItem(u1115, resolvePath('unicons/sitemap.svg')); - cacheItem(u1116, resolvePath('unicons/slack.svg')); - cacheItem(u1117, resolvePath('unicons/sliders-v-alt.svg')); - cacheItem(u1118, resolvePath('unicons/sort-amount-down.svg')); - cacheItem(u1119, resolvePath('unicons/sort-amount-up.svg')); - cacheItem(u1120, resolvePath('unicons/square-shape.svg')); - cacheItem(u1121, resolvePath('unicons/star.svg')); - cacheItem(u1122, resolvePath('unicons/step-backward.svg')); - cacheItem(u1123, resolvePath('unicons/sync.svg')); - cacheItem(u1124, resolvePath('unicons/stopwatch.svg')); - cacheItem(u1125, resolvePath('unicons/table.svg')); - cacheItem(u1126, resolvePath('unicons/tag-alt.svg')); - cacheItem(u1127, resolvePath('unicons/times.svg')); - cacheItem(u1128, resolvePath('unicons/trash-alt.svg')); - cacheItem(u1129, resolvePath('unicons/unlock.svg')); - cacheItem(u1130, resolvePath('unicons/upload.svg')); - cacheItem(u1131, resolvePath('unicons/user.svg')); - cacheItem(u1132, resolvePath('unicons/users-alt.svg')); - cacheItem(u1133, resolvePath('unicons/wrap-text.svg')); - cacheItem(u1134, resolvePath('unicons/cloud-upload.svg')); - cacheItem(u1135, resolvePath('unicons/credit-card.svg')); - cacheItem(u1136, resolvePath('unicons/file-copy-alt.svg')); - cacheItem(u1137, resolvePath('unicons/fire.svg')); - cacheItem(u1138, resolvePath('unicons/hourglass.svg')); - cacheItem(u1139, resolvePath('unicons/layer-group.svg')); - cacheItem(u1140, resolvePath('unicons/layers-alt.svg')); - cacheItem(u1141, resolvePath('unicons/line-alt.svg')); - cacheItem(u1142, resolvePath('unicons/list-ui-alt.svg')); - cacheItem(u1143, resolvePath('unicons/message.svg')); - cacheItem(u1144, resolvePath('unicons/palette.svg')); - cacheItem(u1145, resolvePath('unicons/percentage.svg')); - cacheItem(u1146, resolvePath('unicons/shield-exclamation.svg')); - cacheItem(u1147, resolvePath('unicons/plus-square.svg')); - cacheItem(u1148, resolvePath('unicons/x.svg')); - cacheItem(u1149, resolvePath('unicons/capture.svg')); - cacheItem(u1150, resolvePath('custom/gf-grid.svg')); - cacheItem(u1151, resolvePath('custom/gf-landscape.svg')); - cacheItem(u1152, resolvePath('custom/gf-layout-simple.svg')); - cacheItem(u1153, resolvePath('custom/gf-portrait.svg')); - cacheItem(u1154, resolvePath('custom/gf-show-context.svg')); - cacheItem(u1155, resolvePath('custom/gf-bar-alignment-after.svg')); - cacheItem(u1156, resolvePath('custom/gf-bar-alignment-before.svg')); - cacheItem(u1157, resolvePath('custom/gf-bar-alignment-center.svg')); - cacheItem(u1158, resolvePath('custom/gf-interpolation-linear.svg')); - cacheItem(u1159, resolvePath('custom/gf-interpolation-smooth.svg')); - cacheItem(u1160, resolvePath('custom/gf-interpolation-step-after.svg')); - cacheItem(u1161, resolvePath('custom/gf-interpolation-step-before.svg')); - cacheItem(u1162, resolvePath('custom/gf-logs.svg')); - cacheItem(u1163, resolvePath('custom/gf-movepane-left.svg')); - cacheItem(u1164, resolvePath('custom/gf-movepane-right.svg')); - cacheItem(u1165, resolvePath('mono/favorite.svg')); - cacheItem(u1166, resolvePath('mono/grafana.svg')); - cacheItem(u1167, resolvePath('mono/heart.svg')); - cacheItem(u1168, resolvePath('mono/heart-break.svg')); - cacheItem(u1169, resolvePath('mono/panel-add.svg')); - cacheItem(u1170, resolvePath('mono/library-panel.svg')); - cacheItem(u1171, resolvePath('unicons/record-audio.svg')); + cacheItem(u1026, resolvePath('unicons/bookmark.svg')); + cacheItem(u1027, resolvePath('unicons/book-open.svg')); + cacheItem(u1028, resolvePath('unicons/brackets-curly.svg')); + cacheItem(u1029, resolvePath('unicons/bug.svg')); + cacheItem(u1030, resolvePath('unicons/building.svg')); + cacheItem(u1031, resolvePath('unicons/calculator-alt.svg')); + cacheItem(u1032, resolvePath('unicons/calendar-alt.svg')); + cacheItem(u1033, resolvePath('unicons/calendar-slash.svg')); + cacheItem(u1034, resolvePath('unicons/camera.svg')); + cacheItem(u1035, resolvePath('unicons/channel-add.svg')); + cacheItem(u1036, resolvePath('unicons/chart-line.svg')); + cacheItem(u1037, resolvePath('unicons/check.svg')); + cacheItem(u1038, resolvePath('unicons/check-circle.svg')); + cacheItem(u1039, resolvePath('unicons/circle.svg')); + cacheItem(u1040, resolvePath('unicons/clipboard-alt.svg')); + cacheItem(u1041, resolvePath('unicons/clock-nine.svg')); + cacheItem(u1042, resolvePath('unicons/cloud.svg')); + cacheItem(u1043, resolvePath('unicons/cloud-download.svg')); + cacheItem(u1044, resolvePath('unicons/code-branch.svg')); + cacheItem(u1045, resolvePath('unicons/cog.svg')); + cacheItem(u1046, resolvePath('unicons/columns.svg')); + cacheItem(u1047, resolvePath('unicons/comment-alt.svg')); + cacheItem(u1048, resolvePath('unicons/comment-alt-share.svg')); + cacheItem(u1049, resolvePath('unicons/comments-alt.svg')); + cacheItem(u1050, resolvePath('unicons/compass.svg')); + cacheItem(u1051, resolvePath('unicons/copy.svg')); + cacheItem(u1052, resolvePath('unicons/corner-down-right-alt.svg')); + cacheItem(u1053, resolvePath('unicons/cube.svg')); + cacheItem(u1054, resolvePath('unicons/dashboard.svg')); + cacheItem(u1055, resolvePath('unicons/database.svg')); + cacheItem(u1056, resolvePath('unicons/document-info.svg')); + cacheItem(u1057, resolvePath('unicons/download-alt.svg')); + cacheItem(u1058, resolvePath('unicons/draggabledots.svg')); + cacheItem(u1059, resolvePath('unicons/edit.svg')); + cacheItem(u1060, resolvePath('unicons/ellipsis-v.svg')); + cacheItem(u1061, resolvePath('unicons/ellipsis-h.svg')); + cacheItem(u1062, resolvePath('unicons/envelope.svg')); + cacheItem(u1063, resolvePath('unicons/exchange-alt.svg')); + cacheItem(u1064, resolvePath('unicons/exclamation-circle.svg')); + cacheItem(u1065, resolvePath('unicons/exclamation-triangle.svg')); + cacheItem(u1066, resolvePath('unicons/external-link-alt.svg')); + cacheItem(u1067, resolvePath('unicons/eye.svg')); + cacheItem(u1068, resolvePath('unicons/eye-slash.svg')); + cacheItem(u1069, resolvePath('unicons/file-alt.svg')); + cacheItem(u1070, resolvePath('unicons/file-blank.svg')); + cacheItem(u1071, resolvePath('unicons/filter.svg')); + cacheItem(u1072, resolvePath('unicons/folder.svg')); + cacheItem(u1073, resolvePath('unicons/folder-open.svg')); + cacheItem(u1074, resolvePath('unicons/folder-plus.svg')); + cacheItem(u1075, resolvePath('unicons/folder-upload.svg')); + cacheItem(u1076, resolvePath('unicons/forward.svg')); + cacheItem(u1077, resolvePath('unicons/graph-bar.svg')); + cacheItem(u1078, resolvePath('unicons/history.svg')); + cacheItem(u1079, resolvePath('unicons/history-alt.svg')); + cacheItem(u1080, resolvePath('unicons/home-alt.svg')); + cacheItem(u1081, resolvePath('unicons/import.svg')); + cacheItem(u1082, resolvePath('unicons/info.svg')); + cacheItem(u1083, resolvePath('unicons/info-circle.svg')); + cacheItem(u1084, resolvePath('unicons/k6.svg')); + cacheItem(u1085, resolvePath('unicons/key-skeleton-alt.svg')); + cacheItem(u1086, resolvePath('unicons/keyboard.svg')); + cacheItem(u1087, resolvePath('unicons/link.svg')); + cacheItem(u1088, resolvePath('unicons/list-ul.svg')); + cacheItem(u1089, resolvePath('unicons/lock.svg')); + cacheItem(u1090, resolvePath('unicons/minus.svg')); + cacheItem(u1091, resolvePath('unicons/minus-circle.svg')); + cacheItem(u1092, resolvePath('unicons/mobile-android.svg')); + cacheItem(u1093, resolvePath('unicons/monitor.svg')); + cacheItem(u1094, resolvePath('unicons/pause.svg')); + cacheItem(u1095, resolvePath('unicons/pen.svg')); + cacheItem(u1096, resolvePath('unicons/play.svg')); + cacheItem(u1097, resolvePath('unicons/plug.svg')); + cacheItem(u1098, resolvePath('unicons/plus.svg')); + cacheItem(u1099, resolvePath('unicons/plus-circle.svg')); + cacheItem(u1100, resolvePath('unicons/power.svg')); + cacheItem(u1101, resolvePath('unicons/presentation-play.svg')); + cacheItem(u1102, resolvePath('unicons/process.svg')); + cacheItem(u1103, resolvePath('unicons/question-circle.svg')); + cacheItem(u1104, resolvePath('unicons/repeat.svg')); + cacheItem(u1105, resolvePath('unicons/rocket.svg')); + cacheItem(u1106, resolvePath('unicons/rss.svg')); + cacheItem(u1107, resolvePath('unicons/save.svg')); + cacheItem(u1108, resolvePath('unicons/search.svg')); + cacheItem(u1109, resolvePath('unicons/search-minus.svg')); + cacheItem(u1110, resolvePath('unicons/search-plus.svg')); + cacheItem(u1111, resolvePath('unicons/share-alt.svg')); + cacheItem(u1112, resolvePath('unicons/shield.svg')); + cacheItem(u1113, resolvePath('unicons/signal.svg')); + cacheItem(u1114, resolvePath('unicons/signin.svg')); + cacheItem(u1115, resolvePath('unicons/signout.svg')); + cacheItem(u1116, resolvePath('unicons/sitemap.svg')); + cacheItem(u1117, resolvePath('unicons/slack.svg')); + cacheItem(u1118, resolvePath('unicons/sliders-v-alt.svg')); + cacheItem(u1119, resolvePath('unicons/sort-amount-down.svg')); + cacheItem(u1120, resolvePath('unicons/sort-amount-up.svg')); + cacheItem(u1121, resolvePath('unicons/square-shape.svg')); + cacheItem(u1122, resolvePath('unicons/star.svg')); + cacheItem(u1123, resolvePath('unicons/step-backward.svg')); + cacheItem(u1124, resolvePath('unicons/sync.svg')); + cacheItem(u1125, resolvePath('unicons/stopwatch.svg')); + cacheItem(u1126, resolvePath('unicons/table.svg')); + cacheItem(u1127, resolvePath('unicons/tag-alt.svg')); + cacheItem(u1128, resolvePath('unicons/times.svg')); + cacheItem(u1129, resolvePath('unicons/trash-alt.svg')); + cacheItem(u1130, resolvePath('unicons/unlock.svg')); + cacheItem(u1131, resolvePath('unicons/upload.svg')); + cacheItem(u1132, resolvePath('unicons/user.svg')); + cacheItem(u1133, resolvePath('unicons/users-alt.svg')); + cacheItem(u1134, resolvePath('unicons/wrap-text.svg')); + cacheItem(u1135, resolvePath('unicons/cloud-upload.svg')); + cacheItem(u1136, resolvePath('unicons/credit-card.svg')); + cacheItem(u1137, resolvePath('unicons/file-copy-alt.svg')); + cacheItem(u1138, resolvePath('unicons/fire.svg')); + cacheItem(u1139, resolvePath('unicons/hourglass.svg')); + cacheItem(u1140, resolvePath('unicons/layer-group.svg')); + cacheItem(u1141, resolvePath('unicons/layers-alt.svg')); + cacheItem(u1142, resolvePath('unicons/line-alt.svg')); + cacheItem(u1143, resolvePath('unicons/list-ui-alt.svg')); + cacheItem(u1144, resolvePath('unicons/message.svg')); + cacheItem(u1145, resolvePath('unicons/palette.svg')); + cacheItem(u1146, resolvePath('unicons/percentage.svg')); + cacheItem(u1147, resolvePath('unicons/shield-exclamation.svg')); + cacheItem(u1148, resolvePath('unicons/plus-square.svg')); + cacheItem(u1149, resolvePath('unicons/x.svg')); + cacheItem(u1150, resolvePath('unicons/capture.svg')); + cacheItem(u1151, resolvePath('custom/gf-grid.svg')); + cacheItem(u1152, resolvePath('custom/gf-landscape.svg')); + cacheItem(u1153, resolvePath('custom/gf-layout-simple.svg')); + cacheItem(u1154, resolvePath('custom/gf-portrait.svg')); + cacheItem(u1155, resolvePath('custom/gf-show-context.svg')); + cacheItem(u1156, resolvePath('custom/gf-bar-alignment-after.svg')); + cacheItem(u1157, resolvePath('custom/gf-bar-alignment-before.svg')); + cacheItem(u1158, resolvePath('custom/gf-bar-alignment-center.svg')); + cacheItem(u1159, resolvePath('custom/gf-interpolation-linear.svg')); + cacheItem(u1160, resolvePath('custom/gf-interpolation-smooth.svg')); + cacheItem(u1161, resolvePath('custom/gf-interpolation-step-after.svg')); + cacheItem(u1162, resolvePath('custom/gf-interpolation-step-before.svg')); + cacheItem(u1163, resolvePath('custom/gf-logs.svg')); + cacheItem(u1164, resolvePath('custom/gf-movepane-left.svg')); + cacheItem(u1165, resolvePath('custom/gf-movepane-right.svg')); + cacheItem(u1166, resolvePath('mono/favorite.svg')); + cacheItem(u1167, resolvePath('mono/grafana.svg')); + cacheItem(u1168, resolvePath('mono/heart.svg')); + cacheItem(u1169, resolvePath('mono/heart-break.svg')); + cacheItem(u1170, resolvePath('mono/panel-add.svg')); + cacheItem(u1171, resolvePath('mono/library-panel.svg')); + cacheItem(u1172, resolvePath('unicons/record-audio.svg')); + cacheItem(u1173, resolvePath('solid/bookmark.svg')); // do not edit this list directly // the list of icons live here: @grafana/ui/components/Icon/cached.json } diff --git a/scripts/webpack/webpack.common.js b/scripts/webpack/webpack.common.js index 599c4a1fb41..684b2ea1407 100644 --- a/scripts/webpack/webpack.common.js +++ b/scripts/webpack/webpack.common.js @@ -106,7 +106,7 @@ module.exports = { }, // for pre-caching SVGs as part of the JS bundles { - test: /(unicons|mono|custom)[\\/].*\.svg$/, + test: /(unicons|mono|custom|solid)[\\/].*\.svg$/, type: 'asset/source', }, { From c0f01c30c8b5cb9996ddf5a099b2fe9073d70a8e Mon Sep 17 00:00:00 2001 From: "Alyssa (Bull) Joyner" <58453566+alyssabull@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:59:40 -0600 Subject: [PATCH 03/25] Add CockroachDB as an enterprise data source (#90860) --- docs/sources/introduction/grafana-enterprise.md | 1 + .../datasources/state/buildCategories.test.ts | 2 +- .../datasources/state/buildCategories.ts | 6 ++++++ public/img/plugins/cockroachdb.jpg | Bin 0 -> 19276 bytes 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 public/img/plugins/cockroachdb.jpg diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index 2d29889a0d6..2eae40170b8 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -80,6 +80,7 @@ With a Grafana Enterprise license, you also get access to premium data sources, - [Azure Devops](/grafana/plugins/grafana-azuredevops-datasource) - [Catchpoint](/grafana/plugins/grafana-catchpoint-datasource) - [Cloudflare](/grafana/plugins/grafana-cloudflare-datasource) +- [CockroachDB](/grafana/plugins/grafana-cockroachdb-datasource) - [Databricks](/grafana/plugins/grafana-databricks-datasource) - [DataDog](/grafana/plugins/grafana-datadog-datasource) - [Dynatrace](/grafana/plugins/grafana-dynatrace-datasource) diff --git a/public/app/features/datasources/state/buildCategories.test.ts b/public/app/features/datasources/state/buildCategories.test.ts index 8d964b29288..ad39fa4908d 100644 --- a/public/app/features/datasources/state/buildCategories.test.ts +++ b/public/app/features/datasources/state/buildCategories.test.ts @@ -53,7 +53,7 @@ describe('buildCategories', () => { it('should add enterprise phantom plugins', () => { const enterprisePluginsCategory = categories[3]; expect(enterprisePluginsCategory.title).toBe('Enterprise plugins'); - expect(enterprisePluginsCategory.plugins.length).toBe(23); + expect(enterprisePluginsCategory.plugins.length).toBe(24); expect(enterprisePluginsCategory.plugins[0].name).toBe('Adobe Analytics'); expect(enterprisePluginsCategory.plugins[enterprisePluginsCategory.plugins.length - 1].name).toBe('Wavefront'); }); diff --git a/public/app/features/datasources/state/buildCategories.ts b/public/app/features/datasources/state/buildCategories.ts index d8288e02aaf..6f59f4da835 100644 --- a/public/app/features/datasources/state/buildCategories.ts +++ b/public/app/features/datasources/state/buildCategories.ts @@ -233,6 +233,12 @@ function getEnterprisePhantomPlugins(): DataSourcePluginMeta[] { name: 'Cloudflare', imgUrl: 'public/img/plugins/cloudflare.jpg', }), + getPhantomPlugin({ + id: 'grafana-cockroachdb-datasource', + description: 'CockroachDB datasource', + name: 'CockroachDB', + imgUrl: 'public/img/plugins/cockroachdb.jpg', + }), ]; } diff --git a/public/img/plugins/cockroachdb.jpg b/public/img/plugins/cockroachdb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4edbb1945d499d6999d979c5ce4bccb8be10414e GIT binary patch literal 19276 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 n(bZo){}llIulDtyuKOz> Date: Thu, 1 Aug 2024 13:01:00 -0400 Subject: [PATCH 04/25] Alerting: Fix permissions for prometheus rule endpoints (#91409) --- public/app/plugins/datasource/prometheus/plugin.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/plugin.json b/public/app/plugins/datasource/prometheus/plugin.json index 356b924dbb0..c11bd2e9809 100644 --- a/public/app/plugins/datasource/prometheus/plugin.json +++ b/public/app/plugins/datasource/prometheus/plugin.json @@ -38,31 +38,31 @@ "method": "GET", "path": "/rules", "reqRole": "Viewer", - "reqAction": "datasources:query" + "reqAction": "alert.rules.external:read" }, { "method": "POST", "path": "/rules", "reqRole": "Editor", - "reqAction": "datasources:write" + "reqAction": "alert.rules.external:write" }, { "method": "DELETE", "path": "/rules", "reqRole": "Editor", - "reqAction": "datasources:write" + "reqAction": "alert.rules.external:write" }, { "method": "DELETE", "path": "/config/v1/rules", "reqRole": "Editor", - "reqAction": "datasources:write" + "reqAction": "alert.rules.external:write" }, { "method": "POST", "path": "/config/v1/rules", "reqRole": "Editor", - "reqAction": "datasources:write" + "reqAction": "alert.rules.external:write" } ], "includes": [ From 3952f627eb23c513c6fcf38527b084e397254398 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Thu, 1 Aug 2024 19:03:47 +0200 Subject: [PATCH 05/25] Alerting: Parse secret fields case-insensitively when creating or updating a contact point (#90968) * Alerting: Handle case-insensitive secret fields in contact point settings --- .../ngalert/provisioning/contactpoints.go | 31 ++++++++++- .../provisioning/contactpoints_test.go | 51 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/pkg/services/ngalert/provisioning/contactpoints.go b/pkg/services/ngalert/provisioning/contactpoints.go index 78959045adf..539b81ce0cb 100644 --- a/pkg/services/ngalert/provisioning/contactpoints.go +++ b/pkg/services/ngalert/provisioning/contactpoints.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/alertmanager/config" "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -516,13 +517,39 @@ func RemoveSecretsForContactPoint(e *apimodels.EmbeddedContactPoint) (map[string return nil, err } for _, secretKey := range secretKeys { - secretValue := e.Settings.Get(secretKey).MustString() - e.Settings.Del(secretKey) + foundSecretKey, secretValue, err := getCaseInsensitive(e.Settings, secretKey) + if err != nil { + return nil, err + } + e.Settings.Del(foundSecretKey) s[secretKey] = secretValue } return s, nil } +// getCaseInsensitive returns the value of the specified key, preferring an exact match but accepting a case-insensitive match. +// If no key matches, the second return value is an empty string. +func getCaseInsensitive(jsonObj *simplejson.Json, key string) (string, string, error) { + // Check for an exact key match first. + if value, ok := jsonObj.CheckGet(key); ok { + return key, value.MustString(), nil + } + + // If no exact match is found, look for a case-insensitive match. + settingsMap, err := jsonObj.Map() + if err != nil { + return "", "", err + } + + for k, v := range settingsMap { + if strings.EqualFold(k, key) { + return k, v.(string), nil + } + } + + return key, "", nil +} + // convertRecSvcErr converts errors from notifier.ReceiverService to errors expected from ContactPointService. func convertRecSvcErr(err error) error { if errors.Is(err, store.ErrNoAlertmanagerConfiguration) { diff --git a/pkg/services/ngalert/provisioning/contactpoints_test.go b/pkg/services/ngalert/provisioning/contactpoints_test.go index 9ce9af52f93..a502f9c8798 100644 --- a/pkg/services/ngalert/provisioning/contactpoints_test.go +++ b/pkg/services/ngalert/provisioning/contactpoints_test.go @@ -40,6 +40,11 @@ func TestContactPointService(t *testing.T) { accesscontrol.ActionAlertingProvisioningRead: nil, }, }} + decryptedUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{ + 1: { + accesscontrol.ActionAlertingProvisioningReadSecrets: nil, + }, + }} t.Run("service gets contact points from AM config", func(t *testing.T) { sut := createContactPointServiceSut(t, secretsService) @@ -265,6 +270,52 @@ func TestContactPointService(t *testing.T) { intercepted := fakeConfigStore.LastSaveCommand require.Equal(t, expectedConcurrencyToken, intercepted.FetchedConfigurationHash) }) + + t.Run("secrets are parsed in a case-insensitive way", func(t *testing.T) { + // JSON unmarshalling is case-insensitive. This means we can have + // a setting named "TOKEN" instead of "token". This test ensures that + // we handle such cases correctly and the token value is properly parsed, + // even if the setting key does not match the JSON key exactly. + tests := []struct { + settingsJSON string + expectedValue string + name string + }{ + { + settingsJSON: `{"recipient":"value_recipient","TOKEN":"some-other-token"}`, + expectedValue: "some-other-token", + name: "token key is uppercased", + }, + + // This test checks that if multiple token keys are present in the settings, + // the key with the exact matching name is used. + { + settingsJSON: `{"recipient":"value_recipient","TOKEN":"some-other-token", "token": "second-token"}`, + expectedValue: "second-token", + name: "multiple token keys", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + sut := createContactPointServiceSut(t, secretsService) + + newCp := createTestContactPoint() + settings, _ := simplejson.NewJson([]byte(tc.settingsJSON)) + newCp.Settings = settings + + _, err := sut.CreateContactPoint(context.Background(), 1, newCp, models.ProvenanceAPI) + require.NoError(t, err) + + q := cpsQueryWithName(1, newCp.Name) + q.Decrypt = true + cps, err := sut.GetContactPoints(context.Background(), q, decryptedUser) + require.NoError(t, err) + require.Len(t, cps, 1) + require.Equal(t, tc.expectedValue, cps[0].Settings.Get("token").MustString()) + }) + } + }) } func TestContactPointServiceDecryptRedact(t *testing.T) { From 6efd52eac844b7eda457833e75aa8e4f20faf1e1 Mon Sep 17 00:00:00 2001 From: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:54:00 -0500 Subject: [PATCH 06/25] Docs: Updates to Traces in Explore doc (#90774) Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com> Co-authored-by: Haris Rozajac <58232930+harisrozajac@users.noreply.github.com> --- docs/sources/explore/trace-integration.md | 193 +++++++++++----------- 1 file changed, 93 insertions(+), 100 deletions(-) diff --git a/docs/sources/explore/trace-integration.md b/docs/sources/explore/trace-integration.md index fbf20324291..da14c571cbc 100644 --- a/docs/sources/explore/trace-integration.md +++ b/docs/sources/explore/trace-integration.md @@ -1,5 +1,4 @@ --- -description: Tracing in Explore keywords: - explore - trace @@ -8,167 +7,161 @@ labels: - cloud - enterprise - oss -title: Tracing in Explore +title: Traces in Explore weight: 20 --- -# Tracing in Explore +# Traces in Explore -You can use Explore to query and visualize traces from tracing data sources. +You can use Explore to query and visualize traces from tracing data sources. Supported data sources include: -Supported data sources are: - -- [Tempo]({{< relref "../datasources/tempo/" >}}) (supported ingestion formats: OpenTelemetry, Jaeger, and Zipkin) -- [Jaeger]({{< relref "../datasources/jaeger/" >}}) -- [Zipkin]({{< relref "../datasources/zipkin/" >}}) +- [Tempo](/docs/grafana//datasources/tempo/) +- [Jaeger](/docs/grafana//datasources/jaeger/) +- [Zipkin](/docs/grafana//datasources/zipkin/) - [X-Ray](https://grafana.com/grafana/plugins/grafana-x-ray-datasource) -- [Azure Monitor Application Insights]({{< relref "../datasources/azure-monitor/" >}}) +- [Azure Monitor](/docs/grafana/latest/datasources/azure-monitor/) - [ClickHouse](https://github.com/grafana/clickhouse-datasource) -- [New Relic](https://grafana.com/grafana/plugins/grafana-newrelic-datasource) -- [Infinity](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource) - -For information on how to configure queries for the data sources listed above, refer to the documentation for specific data source. +- [New Relic](/docs/plugins/grafana-newrelic-datasource/latest/) +- [Infinity](/docs/plugins/yesoreyeram-infinity-datasource/latest/) -## Query editor +Here are some references to learn more about traces and how you can use them: -You can query and search tracing data using a data source's query editor. +- [Introduction to tracing](https://grafana.com/docs/tempo//introduction/) +- [Trace structure](https://grafana.com/docs/tempo//traceql/trace-structure/#trace-structure) +- [Traces and telemetry](https://grafana.com/docs/tempo//introduction/telemetry/) +- [Using traces to find solutions to problems](https://grafana.com/docs/tempo//introduction/solutions-with-traces/) +- [Best practices for tracing](/docs/grafana//datasources/tempo/tracing-best-practices/) -Each data source can have it's own query editor. The query editor for the Tempo data source is slightly different than the query editor for the Jaeger data source. +## Query editors -For information on querying each data source, refer to their documentation: +You can query and search tracing data using a data source's query editor. Note that data sources in Grafana have unique query editors. -- [Tempo query editor]({{< relref "../datasources/tempo/query-editor" >}}) -- [Jaeger query editor]({{< relref "../datasources/jaeger/#query-the-data-source" >}}) -- [Zipkin query editor]({{< relref "../datasources/zipkin/#query-the-data-source" >}}) -- [Azure Monitor Application Insights query editor]({{< relref "../datasources/azure-monitor/query-editor/#query-application-insights-traces" >}}) -- [ClickHouse query editor](https://clickhouse.com/docs/en/integrations/grafana/query-builder#traces) +For information on how to use the query editor to create queries for tracing data sources, refer to the documentation for each individual data source. ## Trace view -This section explains the elements of the Trace View. +Grafana's trace view provides an overview of a request as it travels through your system. The following sections provide detail on various elements of the trace view. -{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the trace view" >}} +{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view.png" class="docs-image--no-shadow" max-width= "900px" caption="Trace view" >}} ### Header -{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-header.png" class="docs-image--no-shadow" max-width= "750px" caption="Screenshot of the trace view header" >}} +The trace view header includes the following: -- Header title: Shows the name of the root span and trace ID. -- Search: Highlights spans containing the searched text. -- Metadata: Various metadata about the trace. +- **Header title** - Shows the name of the root span and trace ID. +- **Search** - Highlights spans containing the searched text. +- **Metadata** - Various metadata about the trace. + +{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-header.png" class="docs-image--no-shadow" max-width= "750px" caption="Trace view header" >}} ### Minimap -{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-minimap.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the trace view minimap" >}} +**Minimap** displays a condensed view of the trace timeline. Drag your mouse over the minimap to zoom into a smaller time range. This also updates the main timeline, making it easier to view shorter spans +When zoomed in, hovering over the minimap displays **Reset selection**, which resets the zoom. -Shows condensed view or the trace timeline. Drag your mouse over the minimap to zoom into smaller time range. Zooming will also update the main timeline, so it is easy to see shorter spans. Hovering over the minimap, when zoomed, will show Reset Selection button which resets the zoom. +{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-minimap.png" class="docs-image--no-shadow" max-width= "900px" caption="Trace view minimap example" >}} -### Span filters +### Timeline -![Screenshot of span filtering](/media/docs/tempo/screenshot-grafana-tempo-span-filters-v10-1.png) +Timeline shows list of spans within the trace. Each span row consists of the following components: -Using span filters, you can filter your spans in the trace timeline viewer. The more filters you add, the more specific are the filtered spans. +- **Expand children** - Expands or collapses all the children spans of the selected span. +- **Service name** - Name of the service logged the span. +- **Operation name** - Name of the operation that this span represents. +- **Span duration bar** - Visual representation of the operation duration within the trace. -You can add one or more of the following filters: +Click anywhere on the span row to reveal span details. -- Resource service name -- Span name -- Duration -- Tags (which include tags, process tags, and log fields) +{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-timeline.png" class="docs-image--no-shadow" max-width= "900px" caption="Trace view timeline" >}} -To only show the spans you have matched, you can press the `Show matches only` toggle. +### Span details -{{< youtube id="VP2XV3IIc80" >}} +Traces are composed of one or more spans. +A span is a unit of work within a trace that has a start time relative to the beginning of the trace, a duration and an operation name for the unit of work. +It usually has a reference to a parent span, unless it’s the first span, the root span, in a trace. +It frequently includes key/value attributes that are relevant to the span itself, for example the HTTP method used in the request, as well as other metadata such as the service name, sub-span events, or links to other spans. -### Timeline +You can expand any span in a trace and view the details, including the span and resource attributes. -{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-timeline.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the trace view timeline" >}} +For more information about spans and traces, refer to [Introduction to tracing](https://grafana.com/docs/tempo/latest/introduction/) in the Tempo documentation. -Shows list of spans within the trace. Each span row consists of these components: +Span details include: -- Expand children button: Expands or collapses all the children spans of selected span. -- Service name: Name of the service logged the span. -- Operation name: Name of the operation that this span represents. -- Span duration bar: Visual representation of the operation duration within the trace. +- **Span attributes** - Key/value pairs that provides context for spans. For example, if the span deals with calling another service via HTTP, an attribute could include the HTTP URL (maybe as the span attribute key `http.url`) and the HTTP status code returned (as the span attribute `http.status_code`). -Clicking anywhere on the span row shows span details. +- **Resource attributes** - Key/value pairs that describe the context of how the span was collected. -### Span details +Refer to [Span and resource attributes](/docs/tempo//operations/best-practices/#span-and-resource-attributes) for more detail. -{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-span-details.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the trace view span details" >}} +{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-span-details.png" class="docs-image--no-shadow" max-width= "900px" caption="Trace view span details" >}} -- Operation name. -- Span metadata. -- Tags: Any tags associated with this span. -- Process metadata: Metadata about the process that logged this span. -- Logs: List of logs logged by this span and associated key values. In case of Zipkin logs section shows Zipkin annotations. +### Span filters -### Trace to logs +Span filters allow you to refine the spans displayed in the trace timeline viewer. +The more filters you add, the more specific the filtered spans become. +Click on a trace to access Span filters. -You can navigate from a span in a trace view directly to logs relevant for that span. This feature is available for Tempo, Jaeger, and Zipkin data sources. Refer to their [relevant documentation](/docs/grafana/latest/datasources/tempo/#trace-to-logs) for configuration instructions. +![Screenshot of span filtering](/media/docs/tempo/screenshot-grafana-tempo-span-filters-v10-1.png) -{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-trace-to-logs.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the trace view in Explore with icon next to the spans" >}} +You can add one or more of the following filters: -Click the document icon to open a split view in Explore with the configured data source and query relevant logs for the span. +- **Service name** - Filter by selecting a service name from the dropdown. +- **Span name** - Filter by selecting a span name from the dropdown. +- **Duration** - Filter by duration. Accepted units include ns, us, ms, s, m, h. +- **Tags** - Filter by tags, process tags, or log fields in your span. -### Trace to metrics +To only show the spans you have matched, toggle **Show matches only**. -{{% admonition type="note" %}} -This feature is currently in beta and behind the `traceToMetrics` feature toggle. -{{% /admonition %}} +Refer to [Span filters](/docs/grafana//datasources/tempo/span-filters/) for more in depth information. -You can navigate from a span in a trace view directly to metrics relevant for that span. This feature is available for Tempo, Jaeger, and Zipkin data sources. Refer to their [relevant documentation](/docs/grafana/latest/datasources/tempo/configure-tempo-data-source/#trace-to-metrics) for configuration instructions. +Watch the following video to learn more about filtering trace spans in Grafana: +{{< youtube id="VP2XV3IIc80" >}} -### Trace to profiles +### Trace to logs -Using Trace to profiles, you can use Grafana’s ability to correlate different signals by adding the functionality to link between traces and profiles. -Refer to the [relevant documentation](/docs/grafana/latest/datasources/tempo/configure-tempo-data-source#trace-to-profiles) for configuration instructions. +You can navigate from a span in a trace view directly to logs relevant for that span. +This feature is available for the Tempo, Jaeger, and Zipkin data sources. +Refer to each individual data source's documentation for configuration instructions. -{{< figure src="/static/img/docs/tempo/profiles/tempo-trace-to-profile.png" max-width="900px" class="docs-image--no-shadow" alt="Selecting a link in the span queries the profile data source" >}} +Click the document icon to open a split view in Explore with the configured data source and query relevant logs for the span. -## Node graph +{{< figure src="/media/docs/tempo/screenshot-grafana-trace-view-trace-to-logs.png" class="docs-image--no-shadow" max-width= "900px" caption="Trace to logs" >}} -You can optionally expand the node graph for the displayed trace. Depending on the data source, this can show spans of the trace as nodes in the graph, or as some additional context like service graph based on the current trace. +### Trace to metrics + +You can navigate from a span in a trace view directly to metrics relevant for that span. +This feature is available for the Tempo, Jaeger, and Zipkin data sources. -{{< figure src="/media/docs/tempo/screenshot-grafana-node-graph.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the node graph" >}} +Refer to each individual data source's documentation for configuration instructions. +For Tempo, refer to [Trace to metrics configuration](https://grafana.com/docs/grafana//datasources/tempo/configure-tempo-data-source/#trace-to-metrics). -## Service Graph +### Trace to profiles -The Service Graph visualizes the span metrics (traces data for rates, error rates, and durations (RED)) and service graphs. -Once the requirements are set up, this pre-configured view is immediately available. +Using Trace to profiles, you can use Grafana’s ability to correlate different signals by adding the functionality to link between traces and profiles. -For more information, refer to the [Service Graph view section]({{< relref "../datasources/tempo/#open-the-service-graph-view" >}}) of the Tempo data source page and the [service graph view page](/docs/tempo/latest/metrics-generator/service-graph-view/) in the Tempo documentation. +For Tempo refer to [Trace to profiles](/docs/grafana//datasources/tempo/configure-tempo-data-source#trace-to-profiles) for configuration instructions. -{{< figure src="/static/img/docs/grafana-cloud/apm-overview.png" class="docs-image--no-shadow" max-width= "900px" caption="Screenshot of the Service Graph view" >}} +{{< figure src="/static/img/docs/tempo/profiles/tempo-trace-to-profile.png" max-width="900px" class="docs-image--no-shadow" alt="Selecting a link in the span queries the profile data source" >}} + +## Node graph -## Data API +You can also expand the node graph for a displayed trace. If the data source supports it, this displays spans of the trace as nodes in the graph, or provides additional context, such as a service graph based on the current trace. -This visualization needs a specific shape of the data to be returned from the data source in order to correctly display it. +Refer to [Node graph](/docs/grafana//panels-visualizations/visualizations/node-graph/) for additional information. -Data source needs to return data frame and set `frame.meta.preferredVisualisationType = 'trace'`. +{{< admonition type="note" >}} +The node graph requires data to be returned from the data source in a specific format to display correctly. Refer to [Data API](/docs/grafana//panels-visualizations/visualizations/node-graph/#data-api), [Nodes data frame structure](/docs/grafana/latest/panels-visualizations/visualizations/node-graph/#nodes-data-frame-structure) and [Node graph data requirements](/docs/grafana/latest/panels-visualizations/visualizations/node-graph/#data-requirements) for additional information and configuration instructions. +{{< /admonition >}} -### Data frame structure +{{< figure src="/media/docs/tempo/screenshot-grafana-node-graph.png" class="docs-image--no-shadow" max-width= "900px" caption="Node graph" >}} -Required fields: +## Service graph -| Field name | Type | Description | -| ------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| traceID | string | Identifier for the entire trace. There should be only one trace in the data frame. | -| spanID | string | Identifier for the current span. SpanIDs should be unique per trace. | -| parentSpanID | string | SpanID of the parent span to create child parent relationship in the trace view. Can be `undefined` for root span without a parent. | -| serviceName | string | Name of the service this span is part of. | -| serviceTags | TraceKeyValuePair[] | List of tags relevant for the service. | -| startTime | number | Start time of the span in millisecond epoch time. | -| duration | number | Duration of the span in milliseconds. | +A service graph visualizes span metrics, including rates, error rates, and durations (RED), along with service relationships. Once the requirements are configured, this pre-configured view is immediately available. -Optional fields: +For additional information refer to the following documentation: -| Field name | Type | Description | -| -------------- | ------------------- | ------------------------------------------------------------------ | -| logs | TraceLog[] | List of logs associated with the current span. | -| tags | TraceKeyValuePair[] | List of tags associated with the current span. | -| warnings | string[] | List of warnings associated with the current span. | -| stackTraces | string[] | List of stack traces associated with the current span. | -| errorIconColor | string | Color of the error icon in case span is tagged with `error: true`. | +- [Service Graph and Service Graph view](/docs/grafana//datasources/tempo/service-graph/) +- [Service graph view](/docs/tempo//metrics-generator/service-graph-view/) in Tempo documentation -For details about the types see [TraceSpanRow](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/types/trace.ts#L28), [TraceKeyValuePair](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/types/trace.ts#L4) and [TraceLog](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/types/trace.ts#L12). +{{< figure src="/static/img/docs/grafana-cloud/apm-overview.png" class="docs-image--no-shadow" max-width= "900px" caption="Service graph view" >}} From 7c0ee6ebe44372982d3714901d876a8b92386b3f Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Thu, 1 Aug 2024 15:40:43 -0500 Subject: [PATCH 07/25] Heatmap: Skip null values instead of treating as 0 (#91424) --- public/app/plugins/panel/heatmap/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/heatmap/utils.ts b/public/app/plugins/panel/heatmap/utils.ts index 04919f2e809..3618dcdef2d 100644 --- a/public/app/plugins/panel/heatmap/utils.ts +++ b/public/app/plugins/panel/heatmap/utils.ts @@ -597,7 +597,7 @@ export function heatmapPathsDense(opts: PathbuilderOpts) { ); for (let i = 0; i < dlen; i++) { - if (counts[i] > hideLE && counts[i] < hideGE) { + if (counts[i] != null && counts[i] > hideLE && counts[i] < hideGE) { let cx = cxs[~~(i / yBinQty)]; let cy = cys[i % yBinQty]; @@ -826,7 +826,7 @@ export const boundedMinMax = ( minValue = Infinity; for (let i = 0; i < values.length; i++) { - if (values[i] > hideLE && values[i] < hideGE) { + if (values[i] != null && values[i] > hideLE && values[i] < hideGE) { minValue = Math.min(minValue, values[i]); } } @@ -836,7 +836,7 @@ export const boundedMinMax = ( maxValue = -Infinity; for (let i = 0; i < values.length; i++) { - if (values[i] > hideLE && values[i] < hideGE) { + if (values[i] != null && values[i] > hideLE && values[i] < hideGE) { maxValue = Math.max(maxValue, values[i]); } } From 21e40908fe9044f569b6079044e4fc92b724741f Mon Sep 17 00:00:00 2001 From: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:58:53 -0400 Subject: [PATCH 08/25] Docs: time series overall edit (#91220) Co-authored-by: Nathan Marrs Co-authored-by: Nathan Marrs --- .../visualizations/time-series/index.md | 164 +++++++++--------- .../visualizations/config-options-intro.md | 7 + .../visualizations/connect-null-values.md | 6 +- .../visualizations/disconnect-values.md | 4 +- .../shared/visualizations/legend-options-1.md | 2 +- .../shared/visualizations/multiple-y-axes.md | 8 +- .../visualizations/overrides-options.md | 2 +- .../shared/visualizations/panel-options.md | 2 +- .../shared/visualizations/standard-options.md | 2 - .../visualizations/thresholds-options-1.md | 2 +- 10 files changed, 103 insertions(+), 96 deletions(-) create mode 100644 docs/sources/shared/visualizations/config-options-intro.md diff --git a/docs/sources/panels-visualizations/visualizations/time-series/index.md b/docs/sources/panels-visualizations/visualizations/time-series/index.md index 00aa41bb1d7..5a1907da8d2 100644 --- a/docs/sources/panels-visualizations/visualizations/time-series/index.md +++ b/docs/sources/panels-visualizations/visualizations/time-series/index.md @@ -59,21 +59,26 @@ refs: destination: /docs/grafana//alerting/alerting-rules/create-grafana-managed-rule/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/ + panel-editor-alerts: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/panel-editor-overview/#data-section + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/panel-editor-overview/#data-section --- # Time series -Time series visualizations are the default and primary way to visualize data points over intervals of time as a graph. They can render series as lines, points, or bars. They're versatile enough to display almost any time-series data. +Time series visualizations are the default way to visualize data points over intervals of time, as a graph. They can render series as lines, points, or bars and are versatile enough to display almost any time-series data. {{< figure src="/static/img/docs/time-series-panel/time_series_small_example.png" max-width="1200px" alt="Time series" >}} {{< admonition type="note" >}} -You can migrate from the old Graph visualization to the new time series visualization. To migrate, open the panel and click the **Migrate** button in the side pane. +You can migrate from the legacy Graph visualization to the time series visualization. To migrate, open the panel and click the **Migrate** button in the side pane. {{< /admonition >}} ## Configure a time series visualization -The following video guides you through the creation steps and common customizations of time series visualizations and is great for beginners: +The following video guides you through the creation steps and common customizations of time series visualizations, and is great for beginners: {{< youtube id="RKtW87cPxsw" >}} @@ -81,40 +86,37 @@ The following video guides you through the creation steps and common customizati ## Supported data formats -Time series visualizations require time series data; that is a sequence of measurements, ordered in time, where every row in the table represents one individual measurement at a specific time. Learn more about [time series data](https://grafana.com/docs/grafana//fundamentals/timeseries/). +Time series visualizations require time-series data—a sequence of measurements, ordered in time, and formatted as a table—where every row in the table represents one individual measurement at a specific time. Learn more about [time-series data](https://grafana.com/docs/grafana//fundamentals/timeseries/). ## Alert rules -You can [link alert rules](ref:link-alert) to time series visualizations to observe when alerts fire and are resolved in the form of annotations. In addition, you can create alert rules from the **Alert** tab within the panel editor. - -## Transform override property +You can [link alert rules](ref:link-alert) to time series visualizations in the form of annotations to observe when alerts fire and are resolved. In addition, you can create alert rules from the **Alert** tab within the [panel editor](ref:panel-editor-alerts). -Use the **Transform** override property to transform series values without affecting the values shown in the tooltip, context menu, or legend. +## Special overrides - +The following overrides help you further refine a time series visualization. -- **Negative Y transform:** Flip the results to negative values on the Y axis. -- **Constant:** Show the first value as a constant line. +### Transform override property -{{< docs/shared lookup="visualizations/multiple-y-axes.md" source="grafana" version="" leveloffset="+1" >}} +Use the **Graph styles > Transform** [override property](#field-overrides) to transform series values without affecting the values shown in the tooltip, context menu, or legend. Choose from the following transform options: - +- **Constant** - Show the first value as a constant line. +- **Negative Y transform** - Flip the results to negative values on the y-axis. -## Add the Fill below to override +### Fill below to override property -The **Fill below to** option fills the area between two series. This option is only available as a series/field override. - -1. Edit the panel and click **Overrides**. -1. Select the fields to fill below. -1. In **Add override property**, select **Fill below to**. -1. Select the series for which you want the fill to stop. +The **Graph styles > Fill below to** [override property](#field-overrides) fills the area between two series. When you configure the property, select the series for which you want the fill to stop. The following example shows three series: Min, Max, and Value. The Min and Max series have **Line width** set to 0. Max has a **Fill below to** override set to Min, which fills the area between Max and Min with the Max line color. {{< figure src="/static/img/docs/time-series-panel/fill-below-to-7-4.png" max-width="600px" alt="Fill below to example" >}} +{{< docs/shared lookup="visualizations/multiple-y-axes.md" source="grafana" version="" leveloffset="+2" >}} + ## Configuration options +{{< docs/shared lookup="visualizations/config-options-intro.md" source="grafana" version="" >}} + ### Panel options {{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} @@ -129,30 +131,30 @@ The following example shows three series: Min, Max, and Value. The Min and Max s ### Axis options -Options under the axis category change how the x- and y-axes are rendered. Some options do not take effect until you click outside of the field option box you are editing. You can also or press `Enter`. - -| Option | Description | -| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Time zone | Set the desired time zone(s) to display along the x-axis. | -| [Placement](#placement) | Select the placement of the y-axis. | -| Label | Set a y-axis text label. If you have more than one y-axis, then you can assign different labels using an override. | -| Width | Set a fixed width of the axis. By default, Grafana dynamically calculates the width of an axis. By setting the width of the axis, data with different axes types can share the same display proportions. This setting makes it easier for you to compare more than one graph’s worth of data because the axes are not shifted or stretched within visual proximity to each other. | -| Show grid lines | Set the axis grid line visibility.
| -| Color | Set the color of the axis. | -| Show border | Set the axis border visibility. | -| Scale | Set the y-axis values scale.
| -| Centered zero | Set the y-axis to be centered on zero. | -| [Soft min](#soft-min-and-soft-max) | Set a soft min to better control the y-axis limits. zero. | -| [Soft max](#soft-min-and-soft-max) | Set a soft max to better control the y-axis limits. zero. | +Options under the **Axis** section control how the x- and y-axes are rendered. Some options don't take effect until you click outside of the field option box you're editing. You can also press `Enter`. + +| Option | Description | +| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Time zone | Set the desired time zones to display along the x-axis. | +| [Placement](#placement) | Select the placement of the y-axis. | +| Label | Set a y-axis text label. If you have more than one y-axis, then you can assign different labels using an override. | +| Width | Set a fixed width of the axis. By default, Grafana dynamically calculates the width of an axis. By setting the width of the axis, data with different axes types can share the same display proportions. This setting makes it easier for you to compare more than one graph’s worth of data because the axes aren't shifted or stretched within visual proximity to each other. | +| Show grid lines | Set the axis grid line visibility.
| +| Color | Set the color of the axis. | +| Show border | Set the axis border visibility. | +| Scale | Set the y-axis values scale.
| +| Centered zero | Set the y-axis so it's centered on zero. | +| [Soft min](#soft-min-and-soft-max) | Set a soft min to better control the y-axis limits. zero. | +| [Soft max](#soft-min-and-soft-max) | Set a soft max to better control the y-axis limits. zero. | #### Placement -Select the placement of the y-axis. +Select the placement of the y-axis. Choose from the following: -- **Auto:** Automatically assigns the y-axis to the series. When there are two or more series with different units, Grafana assigns the left axis to the first unit and the right axis to the units that follow. -- **Left:** Display all y-axes on the left side. -- **Right:** Display all y-axes on the right side. -- **Hidden:** Hide all axes. To selectively hide axes, [Add a field override](ref:add-a-field-override) that targets specific fields. +- **Auto** - Automatically assigns the y-axis to the series. When there are two or more series with different units, Grafana assigns the left axis to the first unit and the right axis to the units that follow. +- **Left** - Display all y-axes on the left side. +- **Right** - Display all y-axes on the right side. +- **Hidden** - Hide all axes. To selectively hide axes, [Add a field override](ref:add-a-field-override) that targets specific fields. #### Soft min and soft max @@ -166,71 +168,69 @@ To define hard limits of the y-axis, set standard min/max options. For more info ### Graph styles options +The options under the **Graph styles** section let you control the general appearance of the graph, excluding [color](#standard-options). + | Option | Description | | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Style](#style) | Use this option to define how to display your time series data. | -| [Line interpolation](#line-interpolation) | This option controls how the graph interpolates the series line. | -| [Line width](#line-width) | Line width is a slider that controls the thickness for series lines or the outline for bars. | -| [Fill opacity](#fill-opacity) | Use opacity to specify the series area fill color. | -| [Gradient mode](#gradient-mode) | Gradient mode specifies the gradient fill, which is based on the series color. | -| [Line style](#line-style) | Set the style of the line. | +| [Style](#style) | Choose whether to display your time-series data as lines, bars, or points. | +| [Line interpolation](#line-interpolation) | Choose how the graph interpolates the series line. | +| Line width | Set the thickness of the series lines or the outline for bars using the **Line width** slider. | +| [Fill opacity](#fill-opacity) | Set the series area fill color using the **Fill opacity** slider. | +| [Gradient mode](#gradient-mode) | Choose a gradient mode to control the gradient fill, which is based on the series color. | +| [Line style](#line-style) | Choose a solid, dashed, or dotted line style. | | [Connect null values](#connect-null-values) | Choose how null values, which are gaps in the data, appear on the graph. | | [Disconnect values](#disconnect-values) | Choose whether to set a threshold above which values in the data should be disconnected. | -| [Show points](#show-points) | You can configure your visualization to add points to lines or bars. | +| [Show points](#show-points) | Set whether to show data points to lines or bars. | | Point size | Set the size of the points, from 1 to 40 pixels in diameter. | -| [Stack series](#stack-series) | Stacking allows Grafana to display series on top of each other. | +| [Stack series](#stack-series) | Set whether Grafana displays series on top of each other. | | [Bar alignment](#bar-alignment) | Set the position of the bar relative to a data point. | | Bar width factor | Set the width of the bar relative to minimum space between data points. A factor of 0.5 means that the bars take up half of the available space between data points. A factor of 1.0 means that the bars take up all available space. | #### Style -Use this option to define how to display your time series data. You can use overrides to combine multiple styles in the same graph. - -- Lines -- Bars -- Points +Choose whether to display your time-series data as lines, bars, or points. You can use overrides to combine multiple styles in the same graph. Choose from the following: ![Style modes](/static/img/docs/time-series-panel/style-modes-v9.png) #### Line interpolation -This option controls how the graph interpolates the series line. +Choose how the graph interpolates the series line: -- **Linear:** Points are joined by straight lines. -- **Smooth:** Points are joined by curved lines that smooths transitions between points. -- **Step before:** The line is displayed as steps between points. Points are rendered at the end of the step. -- **Step after:** The line is displayed as steps between points. Points are rendered at the beginning of the step. +- **Linear** - Points are joined by straight lines. +- **Smooth** - Points are joined by curved lines that smooths transitions between points. +- **Step before** - The line is displayed as steps between points. Points are rendered at the end of the step. +- **Step after** - The line is displayed as steps between points. Points are rendered at the beginning of the step. #### Line width -Line width is a slider that controls the thickness for series lines or the outline for bars. +Set the thickness of the series lines or the outline for bars using the **Line width** slider. #### Fill opacity -Use opacity to specify the series area fill color. +Set the series area fill color using the **Fill opacity** slider. ![Fill opacity examples](/static/img/docs/time-series-panel/fill-opacity.png) #### Gradient mode -Gradient mode specifies the gradient fill, which is based on the series color. To change the color, use the standard color scheme field option. For more information, refer to [Color scheme](ref:color-scheme). +Choose a gradient mode to control the gradient fill, which is based on the series color. To change the color, use the standard color scheme field option. For more information, refer to [Color scheme](ref:color-scheme). -- **None:** No gradient fill. This is the default setting. -- **Opacity:** An opacity gradient where the opacity of the fill increases as y-axis values increase. -- **Hue:** A subtle gradient that is based on the hue of the series color. -- **Scheme:** A color gradient defined by your [Color scheme](ref:color-scheme). This setting is used for the fill area and line. For more information about scheme, refer to [Scheme gradient mode](#scheme-gradient-mode). +- **None** - No gradient fill. This is the default setting. +- **Opacity** - An opacity gradient where the opacity of the fill increases as y-axis values increase. +- **Hue** - A subtle gradient that's based on the hue of the series color. +- **Scheme** - A color gradient defined by your [Color scheme](ref:color-scheme). This setting is used for the fill area and line. For more information about scheme, refer to [Scheme gradient mode](#scheme-gradient-mode). -Gradient appearance is influenced by the **Fill opacity** setting. The following image show, the **Fill opacity** is set to 50. +Gradient appearance is influenced by the **Fill opacity** setting. The following image shows the **Fill opacity** set to 50. ![Gradient mode examples](/static/img/docs/time-series-panel/gradient-modes-v9.png) ##### Scheme gradient mode -The **Gradient mode** option located under the **Graph styles** has a mode named **Scheme**. When you enable **Scheme**, the line or bar receives a gradient color defined from the selected **Color scheme**. +The **Gradient mode** option located under the **Graph styles** section has a mode called **Scheme**. When you enable **Scheme**, the line or bar receives a gradient color defined from the selected **Color scheme**. ###### From thresholds -If the **Color scheme** is set to **From thresholds (by value)** and **Gradient mode** is set to **Scheme**, then the line or bar color changes as they cross the defined thresholds. +If the **Color scheme** is set to **From thresholds (by value)** and **Gradient mode** is set to **Scheme**, then the line or bar color changes as it crosses the defined thresholds. {{< figure src="/static/img/docs/time-series-panel/gradient_mode_scheme_thresholds_line.png" max-width="1200px" alt="Colors scheme: From thresholds" >}} @@ -242,11 +242,11 @@ The following image shows a line chart with the **Green-Yellow-Red (by value)** #### Line style -Set the style of the line. To change the color, use the standard [color scheme](ref:color-scheme) field option. +Choose a solid, dashed, or dotted line style: -- **Solid:** Display a solid line. This is the default setting. -- **Dash:** Display a dashed line. When you choose this option, a list appears for you to select the length and gap (length, gap) for the line dashes. Dash spacing set to 10, 10 (default). -- **Dots:** Display dotted lines. When you choose this option, a list appears for you to select the gap (length = 0, gap) for the dot spacing. Dot spacing set to 0, 10 (default) +- **Solid** - Display a solid line. This is the default setting. +- **Dash** - Display a dashed line. When you choose this option, a list appears for you to select the length and gap (length, gap) for the line dashes. Dash spacing is 10, 10 by default. +- **Dots** - Display dotted lines. When you choose this option, a list appears for you to select the gap (length = 0, gap) for the dot spacing. Dot spacing is 0, 10 by default. ![Line styles examples](/static/img/docs/time-series-panel/line-styles-examples-v9.png) @@ -254,21 +254,23 @@ Set the style of the line. To change the color, use the standard [color scheme]( {{< docs/shared lookup="visualizations/disconnect-values.md" source="grafana" version="" leveloffset="+1" >}} +To change the color, use the standard [color scheme](ref:color-scheme) field option. + #### Show points -You can configure your visualization to add points to lines or bars. +Set whether to show data points as lines or bars. Choose from the following: -- **Auto:** Grafana determines to show or not to show points based on the density of the data. If the density is low, then points appear. -- **Always:** Show the points regardless of how dense the data set is. -- **Never:** Do not show points. +- **Auto** - Grafana determines a point's visibility based on the density of the data. If the density is low, then points appear. +- **Always** - Show the points regardless of how dense the data set is. +- **Never** - Don't show points. #### Stack series -_Stacking_ allows Grafana to display series on top of each other. Be cautious when using stacking in the visualization as it can easily create misleading graphs. To read more about why stacking might not be the best approach, refer to [The issue with stacking](https://www.data-to-viz.com/caveat/stacking.html). +Set whether Grafana stacks or displays series on top of each other. Be cautious when using stacking because it can create misleading graphs. To read more about why stacking might not be the best approach, refer to [The issue with stacking](https://www.data-to-viz.com/caveat/stacking.html). Choose from the following: -- **Off:** Turns off series stacking. When **Off**, all series share the same space in the visualization. -- **Normal:** Stacks series on top of each other. -- **100%:** Stack by percentage where all series add up to 100%. +- **Off** - Turns off series stacking. When **Off**, all series share the same space in the visualization. +- **Normal** - Stacks series on top of each other. +- **100%** - Stack by percentage where all series add up to 100%. ##### Stack series in groups @@ -283,7 +285,7 @@ The stacking group option is only available as an override. For more information #### Bar alignment -Set the position of the bar relative to a data point. In the examples below, **Show points** is set to **Always** which makes it easier to see the difference this setting makes. The points do not change; the bars change in relationship to the points. +Set the position of the bar relative to a data point. In the examples below, **Show points** is set to **Always** which makes it easier to see the difference this setting makes. The points don't change, but the bars change in relationship to the points. Choose from the following: - **Before** ![Bar alignment before icon](/static/img/docs/time-series-panel/bar-alignment-before.png) The bar is drawn before the point. The point is placed on the trailing corner of the bar. diff --git a/docs/sources/shared/visualizations/config-options-intro.md b/docs/sources/shared/visualizations/config-options-intro.md new file mode 100644 index 00000000000..94173717f11 --- /dev/null +++ b/docs/sources/shared/visualizations/config-options-intro.md @@ -0,0 +1,7 @@ +--- +title: Configuration options intro text +comments: | + This file is used in the following in all visualizations except: alert list, annotiations list, logs, news, text +--- + +The following section describes the configuration options available in the panel editor pane for this visualization. These options are, as much as possible, ordered as they appear in Grafana. diff --git a/docs/sources/shared/visualizations/connect-null-values.md b/docs/sources/shared/visualizations/connect-null-values.md index d1190a63a92..50660321f93 100644 --- a/docs/sources/shared/visualizations/connect-null-values.md +++ b/docs/sources/shared/visualizations/connect-null-values.md @@ -6,6 +6,6 @@ title: Connect null values Choose how null values, which are gaps in the data, appear on the graph. Null values can be connected to form a continuous line or set to a threshold above which gaps in the data are no longer connected. -- **Never:** Time series data points with gaps in the data are never connected. -- **Always:** Time series data points with gaps in the data are always connected. -- **Threshold:** Specify a threshold above which gaps in the data are no longer connected. This can be useful when the connected gaps in the data are of a known size and/or within a known range, and gaps outside this range should no longer be connected. +- **Never** - Time series data points with gaps in the data are never connected. +- **Always** - Time series data points with gaps in the data are always connected. +- **Threshold** - Specify a threshold above which gaps in the data are no longer connected. This can be useful when the connected gaps in the data are of a known size and/or within a known range, and gaps outside this range should no longer be connected. diff --git a/docs/sources/shared/visualizations/disconnect-values.md b/docs/sources/shared/visualizations/disconnect-values.md index 72b40153a2d..cba32cfc6fb 100644 --- a/docs/sources/shared/visualizations/disconnect-values.md +++ b/docs/sources/shared/visualizations/disconnect-values.md @@ -6,5 +6,5 @@ title: Disconnect values Choose whether to set a threshold above which values in the data should be disconnected. -- **Never:** Time series data points in the data are never disconnected. -- **Threshold:** Specify a threshold above which values in the data are disconnected. This can be useful when desired values in the data are of a known size and/or within a known range, and values outside this range should no longer be connected. +- **Never** - Time series data points in the data are never disconnected. +- **Threshold** - Specify a threshold above which values in the data are disconnected. This can be useful when desired values in the data are of a known size and/or within a known range, and values outside this range should no longer be connected. diff --git a/docs/sources/shared/visualizations/legend-options-1.md b/docs/sources/shared/visualizations/legend-options-1.md index 30123dda0d4..a81d1aa45fc 100644 --- a/docs/sources/shared/visualizations/legend-options-1.md +++ b/docs/sources/shared/visualizations/legend-options-1.md @@ -12,6 +12,6 @@ Legend options control the series names and statistics that appear under or to t | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Visibility | Toggle the switch to turn the legend on or off. | | Mode | Use these settings to define how the legend appears in your visualization. **List** displays the legend as a list. This is a default display mode of the legend. **Table** displays the legend as a table. | -| Placement | Choose where to display the legend. **Bottom -** Below the graph. **Right -** To the right of the graph. | +| Placement | Choose where to display the legend. **Bottom** places the legend below the graph. **Right** places the legend to the right of the graph. | | Width | Control how wide the legend is when placed on the right side of the visualization. This option is only displayed if you set the legend placement to **Right**. | | Values | Choose which of the [standard calculations](../../query-transform-data/calculation-types/) to show in the legend. You can have more than one. | diff --git a/docs/sources/shared/visualizations/multiple-y-axes.md b/docs/sources/shared/visualizations/multiple-y-axes.md index 243f73bda5c..4d74d3b9501 100644 --- a/docs/sources/shared/visualizations/multiple-y-axes.md +++ b/docs/sources/shared/visualizations/multiple-y-axes.md @@ -1,9 +1,9 @@ --- -title: Display multiple y-axes +title: Multiple y-axes --- -# Display multiple y-axes +# Multiple y-axes -In some cases, you may want to display multiple y-axes. For example, if you have a dataset showing both temperature and humidity over time, you may want to show two y-axes with different units for these two series. +In some cases, you might want to display multiple y-axes. For example, if you have a dataset showing both temperature and humidity over time, you might want to show two y-axes with different units for the two series. -You can do this by [adding field overrides]({{< relref "../../panels-visualizations/configure-overrides#add-a-field-override" >}}). Follow the steps as many times as required to add as many y-axes as you need. +You can configure multiple y-axes and control where they're displayed in the visualization by adding field overrides. [This example of a dataset that includes temperature and humidity](../../configure-overrides/#example-2-format-temperature-and-humidity) describes how you can configure that. Repeat the steps for every y-axis you wish to display. diff --git a/docs/sources/shared/visualizations/overrides-options.md b/docs/sources/shared/visualizations/overrides-options.md index 71e7ce8ebcd..21b910af4cb 100644 --- a/docs/sources/shared/visualizations/overrides-options.md +++ b/docs/sources/shared/visualizations/overrides-options.md @@ -6,7 +6,7 @@ comments: | Overrides allow you to customize visualization settings for specific fields or series. When you add an override rule, it targets a particular set of fields and lets you define multiple options for how that field is displayed. -Choose from one the following override options: +Choose from the following override options: | Option | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------------------- | diff --git a/docs/sources/shared/visualizations/panel-options.md b/docs/sources/shared/visualizations/panel-options.md index 5455933bb54..c013457d191 100644 --- a/docs/sources/shared/visualizations/panel-options.md +++ b/docs/sources/shared/visualizations/panel-options.md @@ -4,4 +4,4 @@ comments: | This file is used in all visualizations pages --- -In the **Panel options** section of the panel editor pane, you set basic options like the panel title and description. You can also configure repeating panels in this section. To learn more, refer to [Configure panel options](../../configure-panel-options/). +In the **Panel options** section of the panel editor pane, set basic options like panel title and description, as well as panel links. To learn more, refer to [Configure panel options](../../configure-panel-options/). diff --git a/docs/sources/shared/visualizations/standard-options.md b/docs/sources/shared/visualizations/standard-options.md index f4d84606fd5..db489bd09e3 100644 --- a/docs/sources/shared/visualizations/standard-options.md +++ b/docs/sources/shared/visualizations/standard-options.md @@ -6,8 +6,6 @@ comments: | **Standard options** in the panel editor pane let you change how field data is displayed in your visualizations. When you set a standard option, the change is applied to all fields or series. For more granular control over the display of fields, refer to [Configure overrides](../../configure-overrides/). -You can customize the following standard options: - | Option | Description | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Unit | Choose which unit a field should use. | diff --git a/docs/sources/shared/visualizations/thresholds-options-1.md b/docs/sources/shared/visualizations/thresholds-options-1.md index 264e53839ca..3b2fc6b8f6b 100644 --- a/docs/sources/shared/visualizations/thresholds-options-1.md +++ b/docs/sources/shared/visualizations/thresholds-options-1.md @@ -8,7 +8,7 @@ comments: | A threshold is a value or limit you set for a metric that’s reflected visually when it’s met or exceeded. Thresholds are one way you can conditionally style and color your visualizations based on query results. -Set the following options: +For each threshold, set the following options: | Option | Description | | --------------- | ------------------------------------------------------------------------------------ | From b63694d75f0f416b9ee793e7a94bab7703c64fea Mon Sep 17 00:00:00 2001 From: Nathan Marrs Date: Thu, 1 Aug 2024 17:54:36 -0600 Subject: [PATCH 09/25] XYChart: Promote to generally available (#91417) --- .../visualizations/xy-chart/index.md | 4 ---- .../configure-grafana/feature-toggles/index.md | 2 +- pkg/services/featuremgmt/registry.go | 3 ++- pkg/services/featuremgmt/toggles_gen.csv | 2 +- pkg/services/featuremgmt/toggles_gen.json | 12 ++++++++---- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/sources/panels-visualizations/visualizations/xy-chart/index.md b/docs/sources/panels-visualizations/visualizations/xy-chart/index.md index d28cc0f153e..1e3f4d341dd 100644 --- a/docs/sources/panels-visualizations/visualizations/xy-chart/index.md +++ b/docs/sources/panels-visualizations/visualizations/xy-chart/index.md @@ -59,10 +59,6 @@ refs: # XY chart -{{< admonition type="note">}} -To use xy charts, enable the `autoMigrateXYChartPanel` [feature toggle](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles/). -{{< /admonition >}} - XY charts provide a way to visualize arbitrary x and y values in a graph so that you can easily show the relationship between two variables. XY charts are typically used to create scatter plots. You can also use them to create bubble charts where field values determine the size of each bubble: ![An xy chart showing height weight distribution](/media/docs/grafana/panels-visualizations/screenshot-xy-charts-v11.0.png) diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 835f7ac2089..a9c8b02cd6f 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -27,6 +27,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `publicDashboards` | [Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version. | Yes | | `featureHighlights` | Highlight Grafana Enterprise features | | | `correlations` | Correlations page | Yes | +| `autoMigrateXYChartPanel` | Migrate old XYChart panel to new XYChart2 model | Yes | | `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes | | `nestedFolders` | Enable folder nesting | Yes | | `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view | Yes | @@ -80,7 +81,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `autoMigratePiechartPanel` | Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking | | `autoMigrateWorldmapPanel` | Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking | | `autoMigrateStatPanel` | Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking | -| `autoMigrateXYChartPanel` | Migrate old XYChart panel to new XYChart2 model | | `disableAngular` | Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime. | | `grpcServer` | Run the GRPC server | | `accessControlOnCall` | Access control primitives for OnCall | diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index d0d14abbfbe..4af2dd0aca1 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -144,8 +144,9 @@ var ( { Name: "autoMigrateXYChartPanel", Description: "Migrate old XYChart panel to new XYChart2 model", - Stage: FeatureStagePublicPreview, + Stage: FeatureStageGeneralAvailability, FrontendOnly: true, + Expression: "true", // enabled by default Owner: grafanaDatavizSquad, }, { diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 40519b3e7c3..f67a4ed4923 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -16,7 +16,7 @@ autoMigrateTablePanel,preview,@grafana/dataviz-squad,false,false,true autoMigratePiechartPanel,preview,@grafana/dataviz-squad,false,false,true autoMigrateWorldmapPanel,preview,@grafana/dataviz-squad,false,false,true autoMigrateStatPanel,preview,@grafana/dataviz-squad,false,false,true -autoMigrateXYChartPanel,preview,@grafana/dataviz-squad,false,false,true +autoMigrateXYChartPanel,GA,@grafana/dataviz-squad,false,false,true disableAngular,preview,@grafana/dataviz-squad,false,false,true canvasPanelNesting,experimental,@grafana/dataviz-squad,false,false,true disableSecretsCompatibility,experimental,@grafana/hosted-grafana-team,false,true,false diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 677764ca6d0..b9318868a78 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -429,14 +429,18 @@ { "metadata": { "name": "autoMigrateXYChartPanel", - "resourceVersion": "1718727528075", - "creationTimestamp": "2024-03-22T15:44:37Z" + "resourceVersion": "1722537244598", + "creationTimestamp": "2024-03-22T15:44:37Z", + "annotations": { + "grafana.app/updatedTimestamp": "2024-08-01 18:34:04.598082 +0000 UTC" + } }, "spec": { "description": "Migrate old XYChart panel to new XYChart2 model", - "stage": "preview", + "stage": "GA", "codeowner": "@grafana/dataviz-squad", - "frontend": true + "frontend": true, + "expression": "true" } }, { From 391284bb3322f391100f6d68ab4b2f06395c2873 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 2 Aug 2024 10:27:10 +0300 Subject: [PATCH 10/25] Storage: Improve (some) error handling (#91373) --- pkg/storage/unified/resource/errors.go | 1 - pkg/storage/unified/resource/server.go | 66 +++++++++++-------- pkg/storage/unified/resource/validation.go | 9 ++- .../unified/resource/validation_test.go | 28 ++++---- 4 files changed, 57 insertions(+), 47 deletions(-) diff --git a/pkg/storage/unified/resource/errors.go b/pkg/storage/unified/resource/errors.go index 088e6683630..7f1d3a3ca56 100644 --- a/pkg/storage/unified/resource/errors.go +++ b/pkg/storage/unified/resource/errors.go @@ -12,7 +12,6 @@ import ( // Package-level errors. var ( ErrOptimisticLockingFailed = errors.New("optimistic locking failed") - ErrUserNotFoundInContext = errors.New("user not found in context") ErrNotImplementedYet = errors.New("not implemented yet") ) diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index c301e37288d..a1190e061fb 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -211,19 +211,15 @@ func (s *server) Stop(ctx context.Context) error { } // Old value indicates an update -- otherwise a create -func (s *server) newEvent(ctx context.Context, key *ResourceKey, value, oldValue []byte) (*WriteEvent, error) { - user, err := identity.GetRequester(ctx) - if err != nil { - return nil, ErrUserNotFoundInContext - } +func (s *server) newEvent(ctx context.Context, user identity.Requester, key *ResourceKey, value, oldValue []byte) (*WriteEvent, *ErrorResult) { tmp := &unstructured.Unstructured{} - err = tmp.UnmarshalJSON(value) + err := tmp.UnmarshalJSON(value) if err != nil { - return nil, err + return nil, AsErrorResult(err) } obj, err := utils.MetaAccessor(tmp) if err != nil { - return nil, err + return nil, AsErrorResult(err) } event := &WriteEvent{ @@ -239,27 +235,27 @@ func (s *server) newEvent(ctx context.Context, key *ResourceKey, value, oldValue temp := &unstructured.Unstructured{} err = temp.UnmarshalJSON(oldValue) if err != nil { - return nil, err + return nil, AsErrorResult(err) } event.ObjectOld, err = utils.MetaAccessor(temp) if err != nil { - return nil, err + return nil, AsErrorResult(err) } } if key.Namespace != obj.GetNamespace() { - return nil, apierrors.NewBadRequest("key/namespace do not match") + return nil, NewBadRequestError("key/namespace do not match") } gvk := obj.GetGroupVersionKind() if gvk.Kind == "" { - return nil, apierrors.NewBadRequest("expecting resources with a kind in the body") + return nil, NewBadRequestError("expecting resources with a kind in the body") } if gvk.Version == "" { - return nil, apierrors.NewBadRequest("expecting resources with an apiVersion") + return nil, NewBadRequestError("expecting resources with an apiVersion") } if gvk.Group != "" && gvk.Group != key.Group { - return nil, apierrors.NewBadRequest( + return nil, NewBadRequestError( fmt.Sprintf("group in key does not match group in the body (%s != %s)", key.Group, gvk.Group), ) } @@ -267,15 +263,14 @@ func (s *server) newEvent(ctx context.Context, key *ResourceKey, value, oldValue // This needs to be a create function if key.Name == "" { if obj.GetName() == "" { - return nil, apierrors.NewBadRequest("missing name") + return nil, NewBadRequestError("missing name") } key.Name = obj.GetName() } else if key.Name != obj.GetName() { - return nil, apierrors.NewBadRequest( + return nil, NewBadRequestError( fmt.Sprintf("key/name do not match (key: %s, name: %s)", key.Name, obj.GetName())) } - err = validateName(obj.GetName()) - if err != nil { + if err := validateName(obj.GetName()); err != nil { return nil, err } @@ -283,17 +278,17 @@ func (s *server) newEvent(ctx context.Context, key *ResourceKey, value, oldValue if folder != "" { err = s.access.CanWriteFolder(ctx, user, folder) if err != nil { - return nil, err + return nil, AsErrorResult(err) } } origin, err := obj.GetOriginInfo() if err != nil { - return nil, apierrors.NewBadRequest("invalid origin info") + return nil, NewBadRequestError("invalid origin info") } if origin != nil { err = s.access.CanWriteOrigin(ctx, user, origin.Name) if err != nil { - return nil, err + return nil, AsErrorResult(err) } } return event, nil @@ -308,6 +303,15 @@ func (s *server) Create(ctx context.Context, req *CreateRequest) (*CreateRespons } rsp := &CreateResponse{} + user, err := identity.GetRequester(ctx) + if err != nil || user == nil { + rsp.Error = &ErrorResult{ + Message: "no user found in context", + Code: http.StatusUnauthorized, + } + return rsp, nil + } + found := s.backend.ReadResource(ctx, &ReadRequest{Key: req.Key}) if found != nil && len(found.Value) > 0 { rsp.Error = &ErrorResult{ @@ -317,9 +321,9 @@ func (s *server) Create(ctx context.Context, req *CreateRequest) (*CreateRespons return rsp, nil } - event, err := s.newEvent(ctx, req.Key, req.Value, nil) - if err != nil { - rsp.Error = AsErrorResult(err) + event, e := s.newEvent(ctx, user, req.Key, req.Value, nil) + if e != nil { + rsp.Error = e return rsp, nil } @@ -339,6 +343,14 @@ func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateRespons } rsp := &UpdateResponse{} + user, err := identity.GetRequester(ctx) + if err != nil || user == nil { + rsp.Error = &ErrorResult{ + Message: "no user found in context", + Code: http.StatusUnauthorized, + } + return rsp, nil + } if req.ResourceVersion < 0 { rsp.Error = AsErrorResult(apierrors.NewBadRequest("update must include the previous version")) return rsp, nil @@ -359,9 +371,9 @@ func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateRespons return nil, ErrOptimisticLockingFailed } - event, err := s.newEvent(ctx, req.Key, req.Value, latest.Value) - if err != nil { - rsp.Error = AsErrorResult(err) + event, e := s.newEvent(ctx, user, req.Key, req.Value, latest.Value) + if e != nil { + rsp.Error = e return rsp, err } diff --git a/pkg/storage/unified/resource/validation.go b/pkg/storage/unified/resource/validation.go index 5ce5362717a..dce25a1b8fe 100644 --- a/pkg/storage/unified/resource/validation.go +++ b/pkg/storage/unified/resource/validation.go @@ -1,22 +1,21 @@ package resource import ( - "fmt" "regexp" ) var validNameCharPattern = `a-zA-Z0-9\-\_\.` var validNamePattern = regexp.MustCompile(`^[` + validNameCharPattern + `]*$`).MatchString -func validateName(name string) error { +func validateName(name string) *ErrorResult { if len(name) == 0 { - return fmt.Errorf("name is too short") + return NewBadRequestError("name is too short") } if len(name) > 64 { - return fmt.Errorf("name is too long") + return NewBadRequestError("name is too long") } if !validNamePattern(name) { - return fmt.Errorf("name includes invalid characters") + return NewBadRequestError("name includes invalid characters") } // In standard k8s, it must not start with a number // however that would force us to update many many many existing resources diff --git a/pkg/storage/unified/resource/validation_test.go b/pkg/storage/unified/resource/validation_test.go index 9b4907d8630..b9f9a51d7b5 100644 --- a/pkg/storage/unified/resource/validation_test.go +++ b/pkg/storage/unified/resource/validation_test.go @@ -7,24 +7,24 @@ import ( ) func TestNameValidation(t *testing.T) { - require.Error(t, validateName("")) // too short - require.Error(t, validateName( // too long (max 64) + require.NotNil(t, validateName("")) // too short + require.NotNil(t, validateName( // too long (max 64) "0123456789012345678901234567890123456789012345678901234567890123456789", )) // OK - require.NoError(t, validateName("a")) - require.NoError(t, validateName("hello-world")) - require.NoError(t, validateName("hello.world")) - require.NoError(t, validateName("hello_world")) + require.Nil(t, validateName("a")) + require.Nil(t, validateName("hello-world")) + require.Nil(t, validateName("hello.world")) + require.Nil(t, validateName("hello_world")) // Bad characters - require.Error(t, validateName("hello world")) - require.Error(t, validateName("hello!")) - require.Error(t, validateName("hello~")) - require.Error(t, validateName("hello ")) - require.Error(t, validateName("hello*")) - require.Error(t, validateName("hello+")) - require.Error(t, validateName("hello=")) - require.Error(t, validateName("hello%")) + require.NotNil(t, validateName("hello world")) + require.NotNil(t, validateName("hello!")) + require.NotNil(t, validateName("hello~")) + require.NotNil(t, validateName("hello ")) + require.NotNil(t, validateName("hello*")) + require.NotNil(t, validateName("hello+")) + require.NotNil(t, validateName("hello=")) + require.NotNil(t, validateName("hello%")) } From 8988e04044ae613e293de377a615c7bd4bf38c94 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Fri, 2 Aug 2024 09:32:06 +0200 Subject: [PATCH 11/25] RBAC: Add permission registry (#91247) * RBAC: Permission registry * Populate permission registry * Wire * conflic_user_cmd * Update pkg/services/accesscontrol/permreg/permreg_test.go Co-authored-by: Ieva * PR feedback Co-authored-by: Ieva * Remove ToDo, tackle in subsequent PR --------- Co-authored-by: Ieva --- pkg/api/folder_bench_test.go | 3 +- .../commands/conflict_user_command.go | 3 +- pkg/server/wire.go | 2 + pkg/services/accesscontrol/acimpl/service.go | 19 +- .../accesscontrol/acimpl/service_test.go | 3 + pkg/services/accesscontrol/permreg/permreg.go | 183 +++++++++++++ .../accesscontrol/permreg/permreg_test.go | 246 ++++++++++++++++++ .../accesscontrol/permreg/test/testreg.go | 22 ++ .../extsvcaccounts/service_test.go | 2 + 9 files changed, 478 insertions(+), 5 deletions(-) create mode 100644 pkg/services/accesscontrol/permreg/permreg.go create mode 100644 pkg/services/accesscontrol/permreg/permreg_test.go create mode 100644 pkg/services/accesscontrol/permreg/test/testreg.go diff --git a/pkg/api/folder_bench_test.go b/pkg/api/folder_bench_test.go index c9f0f300601..02f729ff2b2 100644 --- a/pkg/api/folder_bench_test.go +++ b/pkg/api/folder_bench_test.go @@ -25,6 +25,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database" "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/authz/zanzana" "github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" @@ -463,7 +464,7 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog actionSets := resourcepermissions.NewActionSetService(features) acSvc := acimpl.ProvideOSSService( sc.cfg, acdb.ProvideService(sc.db), actionSets, localcache.ProvideService(), - features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db.DB(), + features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db.DB(), permreg.ProvidePermissionRegistry(), ) folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( diff --git a/pkg/cmd/grafana-cli/commands/conflict_user_command.go b/pkg/cmd/grafana-cli/commands/conflict_user_command.go index 9ad5fe87611..9d7a534232b 100644 --- a/pkg/cmd/grafana-cli/commands/conflict_user_command.go +++ b/pkg/cmd/grafana-cli/commands/conflict_user_command.go @@ -22,6 +22,7 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" "github.com/grafana/grafana/pkg/services/authz/zanzana" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/quota/quotaimpl" @@ -90,7 +91,7 @@ func initializeConflictResolver(cmd *utils.ContextCommandLine, f Formatter, ctx if err != nil { return nil, fmt.Errorf("%v: %w", "failed to initialize tracer service", err) } - acService, err := acimpl.ProvideService(cfg, replstore, routing, nil, nil, nil, features, tracer, zanzana.NewNoopClient()) + acService, err := acimpl.ProvideService(cfg, replstore, routing, nil, nil, nil, features, tracer, zanzana.NewNoopClient(), permreg.ProvidePermissionRegistry()) if err != nil { return nil, fmt.Errorf("%v: %w", "failed to get access control", err) } diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 6440aab312e..47aa9aedc38 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -39,6 +39,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/annotations" "github.com/grafana/grafana/pkg/services/annotations/annotationsimpl" @@ -345,6 +346,7 @@ var wireBasicSet = wire.NewSet( resourcepermissions.NewActionSetService, wire.Bind(new(accesscontrol.ActionResolver), new(resourcepermissions.ActionSetService)), wire.Bind(new(pluginaccesscontrol.ActionSetRegistry), new(resourcepermissions.ActionSetService)), + permreg.ProvidePermissionRegistry, acimpl.ProvideAccessControl, navtreeimpl.ProvideService, wire.Bind(new(accesscontrol.AccessControl), new(*acimpl.AccessControl)), diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go index 790fc5773fd..5b7b509b245 100644 --- a/pkg/services/accesscontrol/acimpl/service.go +++ b/pkg/services/accesscontrol/acimpl/service.go @@ -24,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/api" "github.com/grafana/grafana/pkg/services/accesscontrol/database" "github.com/grafana/grafana/pkg/services/accesscontrol/migrator" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" "github.com/grafana/grafana/pkg/services/accesscontrol/pluginutils" "github.com/grafana/grafana/pkg/services/authz/zanzana" "github.com/grafana/grafana/pkg/services/dashboards" @@ -50,9 +51,9 @@ var OSSRolesPrefixes = []string{accesscontrol.ManagedRolePrefix, accesscontrol.E func ProvideService( cfg *setting.Cfg, db db.ReplDB, routeRegister routing.RouteRegister, cache *localcache.CacheService, accessControl accesscontrol.AccessControl, actionResolver accesscontrol.ActionResolver, - features featuremgmt.FeatureToggles, tracer tracing.Tracer, zclient zanzana.Client, + features featuremgmt.FeatureToggles, tracer tracing.Tracer, zclient zanzana.Client, permRegistry permreg.PermissionRegistry, ) (*Service, error) { - service := ProvideOSSService(cfg, database.ProvideService(db), actionResolver, cache, features, tracer, zclient, db.DB()) + service := ProvideOSSService(cfg, database.ProvideService(db), actionResolver, cache, features, tracer, zclient, db.DB(), permRegistry) api.NewAccessControlAPI(routeRegister, accessControl, service, features).RegisterAPIEndpoints() if err := accesscontrol.DeclareFixedRoles(service, cfg); err != nil { @@ -73,7 +74,7 @@ func ProvideService( func ProvideOSSService( cfg *setting.Cfg, store accesscontrol.Store, actionResolver accesscontrol.ActionResolver, cache *localcache.CacheService, features featuremgmt.FeatureToggles, tracer tracing.Tracer, - zclient zanzana.Client, db db.DB, + zclient zanzana.Client, db db.DB, permRegistry permreg.PermissionRegistry, ) *Service { s := &Service{ actionResolver: actionResolver, @@ -85,6 +86,7 @@ func ProvideOSSService( store: store, tracer: tracer, sync: migrator.NewZanzanaSynchroniser(zclient, db), + permRegistry: permRegistry, } return s @@ -102,6 +104,7 @@ type Service struct { store accesscontrol.Store tracer tracing.Tracer sync *migrator.ZanzanaSynchroniser + permRegistry permreg.PermissionRegistry } func (s *Service) GetUsageStats(_ context.Context) map[string]any { @@ -406,6 +409,10 @@ func (s *Service) DeclareFixedRoles(registrations ...accesscontrol.RoleRegistrat return err } + for i := range r.Role.Permissions { + s.permRegistry.RegisterPermission(r.Role.Permissions[i].Action, r.Role.Permissions[i].Scope) + } + s.registrations.Append(r) } @@ -458,6 +465,12 @@ func (s *Service) DeclarePluginRoles(ctx context.Context, ID, name string, regs return err } + for i := range r.Role.Permissions { + // Register plugin actions and their possible scopes for permission validation + s.permRegistry.RegisterPluginScope(r.Role.Permissions[i].Scope) + s.permRegistry.RegisterPermission(r.Role.Permissions[i].Action, r.Role.Permissions[i].Scope) + } + s.log.Debug("Registering plugin role", "role", r.Role.Name) s.registrations.Append(r) } diff --git a/pkg/services/accesscontrol/acimpl/service_test.go b/pkg/services/accesscontrol/acimpl/service_test.go index 19245d113f8..e5df3ada30c 100644 --- a/pkg/services/accesscontrol/acimpl/service_test.go +++ b/pkg/services/accesscontrol/acimpl/service_test.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" "github.com/grafana/grafana/pkg/services/accesscontrol/database" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/licensing" @@ -42,6 +43,7 @@ func setupTestEnv(t testing.TB) *Service { roles: accesscontrol.BuildBasicRoleDefinitions(), tracer: tracing.InitializeTracerForTest(), store: database.ProvideService(db.InitTestReplDB(t)), + permRegistry: permreg.ProvidePermissionRegistry(), } require.NoError(t, ac.RegisterFixedRoles(context.Background())) return ac @@ -71,6 +73,7 @@ func TestUsageMetrics(t *testing.T) { tracing.InitializeTracerForTest(), nil, nil, + permreg.ProvidePermissionRegistry(), ) assert.Equal(t, tt.expectedValue, s.GetUsageStats(context.Background())["stats.oss.accesscontrol.enabled.count"]) }) diff --git a/pkg/services/accesscontrol/permreg/permreg.go b/pkg/services/accesscontrol/permreg/permreg.go new file mode 100644 index 00000000000..ca1b8ba1fd1 --- /dev/null +++ b/pkg/services/accesscontrol/permreg/permreg.go @@ -0,0 +1,183 @@ +package permreg + +import ( + "strings" + + "github.com/grafana/grafana/pkg/apimachinery/errutil" + "github.com/grafana/grafana/pkg/infra/log" +) + +var ( + // ErrInvalidScope is returned when the scope is not valid for the action + ErrInvalidScopeTplt = "invalid scope: {{.Public.Scope}}, for action: {{.Public.Action}}, expected prefixes are {{.Public.ValidScopesFormat}}" + ErrBaseInvalidScope = errutil.BadRequest("permreg.invalid-scope").MustTemplate(ErrInvalidScopeTplt, errutil.WithPublic(ErrInvalidScopeTplt)) + + ErrUnknownActionTplt = "unknown action: {{.Public.Action}}, was not found in the list of valid actions" + ErrBaseUnknownAction = errutil.BadRequest("permreg.unknown-action").MustTemplate(ErrUnknownActionTplt, errutil.WithPublic(ErrUnknownActionTplt)) +) + +func ErrInvalidScope(scope string, action string, validScopePrefixes PrefixSet) error { + if len(validScopePrefixes) == 0 { + return ErrBaseInvalidScope.Build(errutil.TemplateData{Public: map[string]any{"Scope": scope, "Action": action, "ValidScopesFormat": "[none]"}}) + } + formats := generateValidScopeFormats(validScopePrefixes) + return ErrBaseInvalidScope.Build(errutil.TemplateData{Public: map[string]any{"Scope": scope, "Action": action, "ValidScopesFormat": formats}}) +} + +func ErrUnknownAction(action string) error { + return ErrBaseUnknownAction.Build(errutil.TemplateData{Public: map[string]any{"Action": action}}) +} + +func generateValidScopeFormats(acceptedScopePrefixes PrefixSet) []string { + if len(acceptedScopePrefixes) == 0 { + return []string{} + } + acceptedPrefixesList := make([]string, 0, 10) + acceptedPrefixesList = append(acceptedPrefixesList, "*") + for prefix := range acceptedScopePrefixes { + parts := strings.Split(prefix, ":") + // If the prefix has an attribute part add the intermediate format kind:* + if len(parts) > 2 { + acceptedPrefixesList = append(acceptedPrefixesList, parts[0]+":*") + } + // Add the most specific format kind:attribute:* + acceptedPrefixesList = append(acceptedPrefixesList, prefix+"*") + } + return acceptedPrefixesList +} + +type PermissionRegistry interface { + RegisterPluginScope(scope string) + RegisterPermission(action, scope string) + IsPermissionValid(action, scope string) error + GetScopePrefixes(action string) (PrefixSet, bool) +} + +type PrefixSet map[string]bool + +var _ PermissionRegistry = &permissionRegistry{} + +type permissionRegistry struct { + actionScopePrefixes map[string]PrefixSet // TODO use thread safe map + kindScopePrefix map[string]string + logger log.Logger +} + +func ProvidePermissionRegistry() PermissionRegistry { + return newPermissionRegistry() +} + +func newPermissionRegistry() *permissionRegistry { + // defaultKindScopes maps the most specific accepted scope prefix for a given kind (folders, dashboards, etc) + defaultKindScopes := map[string]string{ + "teams": "teams:id:", + "users": "users:id:", + "datasources": "datasources:uid:", + "dashboards": "dashboards:uid:", + "folders": "folders:uid:", + "annotations": "annotations:type:", + "apikeys": "apikeys:id:", + "orgs": "orgs:id:", + "plugins": "plugins:id:", + "provisioners": "provisioners:", + "reports": "reports:id:", + "permissions": "permissions:type:", + "serviceaccounts": "serviceaccounts:id:", + "settings": "settings:", + "global.users": "global.users:id:", + "roles": "roles:uid:", + "services": "services:", + } + return &permissionRegistry{ + actionScopePrefixes: make(map[string]PrefixSet, 200), + kindScopePrefix: defaultKindScopes, + logger: log.New("accesscontrol.permreg"), + } +} + +func (pr *permissionRegistry) RegisterPluginScope(scope string) { + if scope == "" { + return + } + + scopeParts := strings.Split(scope, ":") + // If the scope is already registered, return + if _, found := pr.kindScopePrefix[scopeParts[0]]; found { + return + } + + // If the scope contains an attribute part, register the kind and attribute + if len(scopeParts) > 2 { + kind, attr := scopeParts[0], scopeParts[1] + pr.kindScopePrefix[kind] = kind + ":" + attr + ":" + pr.logger.Debug("registered scope prefix", "kind", kind, "scope_prefix", kind+":"+attr+":") + return + } + + pr.logger.Debug("registered scope prefix", "kind", scopeParts[0], "scope_prefix", scopeParts[0]+":") + pr.kindScopePrefix[scopeParts[0]] = scopeParts[0] + ":" +} + +func (pr *permissionRegistry) RegisterPermission(action, scope string) { + if _, ok := pr.actionScopePrefixes[action]; !ok { + pr.actionScopePrefixes[action] = PrefixSet{} + } + + if scope == "" { + // scopeless action + return + } + + kind := strings.Split(scope, ":")[0] + scopePrefix, ok := pr.kindScopePrefix[kind] + if !ok { + pr.logger.Warn("unknown scope prefix", "scope", scope) + return + } + + // Add a new entry in case the scope is not empty + pr.actionScopePrefixes[action][scopePrefix] = true +} + +func (pr *permissionRegistry) IsPermissionValid(action, scope string) error { + validScopePrefixes, ok := pr.actionScopePrefixes[action] + if !ok { + return ErrUnknownAction(action) + } + + if ok && len(validScopePrefixes) == 0 { + // Expecting action without any scope + if scope != "" { + return ErrInvalidScope(scope, action, nil) + } + return nil + } + + if !isScopeValid(scope, validScopePrefixes) { + return ErrInvalidScope(scope, action, validScopePrefixes) + } + return nil +} + +func isScopeValid(scope string, validScopePrefixes PrefixSet) bool { + // Super wildcard scope + if scope == "*" { + return true + } + for scopePrefix := range validScopePrefixes { + // Correct scope prefix + if strings.HasPrefix(scope, scopePrefix) { + return true + } + // Scope is wildcard of the correct prefix + if strings.HasSuffix(scope, ":*") && strings.HasPrefix(scopePrefix, scope[:len(scope)-2]) { + return true + } + } + return false +} + +func (pr *permissionRegistry) GetScopePrefixes(action string) (PrefixSet, bool) { + set, ok := pr.actionScopePrefixes[action] + return set, ok +} diff --git a/pkg/services/accesscontrol/permreg/permreg_test.go b/pkg/services/accesscontrol/permreg/permreg_test.go new file mode 100644 index 00000000000..4d78e0ef9a6 --- /dev/null +++ b/pkg/services/accesscontrol/permreg/permreg_test.go @@ -0,0 +1,246 @@ +package permreg + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_permissionRegistry_RegisterPluginScope(t *testing.T) { + tests := []struct { + scope string + wantKind string + wantScope string + }{ + { + scope: "folders:uid:AABBCC", + wantKind: "folders", + wantScope: "folders:uid:", + }, + { + scope: "plugins:id:test-app", + wantKind: "plugins", + wantScope: "plugins:id:", + }, + { + scope: "resource:uid:res", + wantKind: "resource", + wantScope: "resource:uid:", + }, + { + scope: "resource:*", + wantKind: "resource", + wantScope: "resource:", + }, + } + for _, tt := range tests { + t.Run(tt.scope, func(t *testing.T) { + pr := newPermissionRegistry() + pr.RegisterPluginScope(tt.scope) + got, ok := pr.kindScopePrefix[tt.wantKind] + require.True(t, ok) + require.Equal(t, tt.wantScope, got) + }) + } +} + +func Test_permissionRegistry_RegisterPermission(t *testing.T) { + tests := []struct { + name string + action string + scope string + wantKind string + wantPrefixSet PrefixSet + wantSkip bool + }{ + { + name: "register folders read", + action: "folders:read", + scope: "folders:*", + wantKind: "folders", + wantPrefixSet: PrefixSet{"folders:uid:": true}, + }, + { + name: "register app plugin settings read", + action: "test-app.settings:read", + wantKind: "settings", + wantPrefixSet: PrefixSet{}, + }, + { + name: "register an action on an unknown kind", + action: "unknown:action", + scope: "unknown:uid:*", + wantPrefixSet: PrefixSet{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pr := newPermissionRegistry() + pr.RegisterPermission(tt.action, tt.scope) + got, ok := pr.actionScopePrefixes[tt.action] + require.True(t, ok) + for k, v := range got { + require.Equal(t, v, tt.wantPrefixSet[k]) + } + }) + } +} + +func Test_permissionRegistry_IsPermissionValid(t *testing.T) { + pr := newPermissionRegistry() + pr.RegisterPermission("folders:read", "folders:uid:") + pr.RegisterPermission("test-app.settings:read", "") + + tests := []struct { + name string + action string + scope string + wantErr bool + }{ + { + name: "valid folders read", + action: "folders:read", + scope: "folders:uid:AABBCC", + wantErr: false, + }, + { + name: "valid folders read with wildcard", + action: "folders:read", + scope: "folders:uid:*", + wantErr: false, + }, + { + name: "valid folders read with kind level wildcard", + action: "folders:read", + scope: "folders:*", + wantErr: false, + }, + { + name: "valid folders read with super wildcard", + action: "folders:read", + scope: "*", + wantErr: false, + }, + { + name: "invalid folders read with wrong kind", + action: "folders:read", + scope: "unknown:uid:AABBCC", + wantErr: true, + }, + { + name: "invalid folders read with wrong attribute", + action: "folders:read", + scope: "folders:id:3", + wantErr: true, + }, + { + name: "valid app plugin settings read", + action: "test-app.settings:read", + scope: "", + wantErr: false, + }, + { + name: "app plugin settings read with a scope", + action: "test-app.settings:read", + scope: "folders:uid:*", + wantErr: true, + }, + { + name: "unknown action", + action: "unknown:write", + scope: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := pr.IsPermissionValid(tt.action, tt.scope) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func Test_permissionRegistry_GetScopePrefixes(t *testing.T) { + pr := newPermissionRegistry() + pr.RegisterPermission("folders:read", "folders:uid:") + pr.RegisterPermission("test-app.settings:read", "") + + tests := []struct { + name string + action string + want PrefixSet + shouldExist bool + }{ + { + name: "get folders read scope prefixes", + action: "folders:read", + want: PrefixSet{"folders:uid:": true}, + shouldExist: true, + }, + { + name: "get app plugin settings read scope prefixes", + action: "test-app.settings:read", + want: PrefixSet{}, + shouldExist: true, + }, + { + name: "get unknown action scope prefixes", + action: "unknown:write", + want: PrefixSet{}, + shouldExist: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := pr.GetScopePrefixes(tt.action) + if !tt.shouldExist { + require.False(t, got1) + return + } + require.True(t, got1) + require.Len(t, tt.want, len(got)) + for k, v := range got { + require.Equal(t, v, tt.want[k]) + } + }) + } +} + +func Test_generateValidScopeFormats(t *testing.T) { + tests := []struct { + name string + prefixSet PrefixSet + want []string + }{ + { + name: "empty prefix set", + prefixSet: PrefixSet{}, + want: []string{}, + }, + { + name: "short prefix", + prefixSet: PrefixSet{"folders:": true}, + want: []string{"*", "folders:*"}, + }, + { + name: "single prefix", + prefixSet: PrefixSet{"folders:uid:": true}, + want: []string{"*", "folders:*", "folders:uid:*"}, + }, + { + name: "multiple prefixes", + prefixSet: PrefixSet{"folders:uid:": true, "dashboards:uid:": true}, + want: []string{"*", "folders:*", "folders:uid:*", "dashboards:*", "dashboards:uid:*"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := generateValidScopeFormats(tt.prefixSet) + require.ElementsMatch(t, tt.want, got) + }) + } +} diff --git a/pkg/services/accesscontrol/permreg/test/testreg.go b/pkg/services/accesscontrol/permreg/test/testreg.go new file mode 100644 index 00000000000..7b5210dd2e6 --- /dev/null +++ b/pkg/services/accesscontrol/permreg/test/testreg.go @@ -0,0 +1,22 @@ +package test + +import "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" + +func ProvidePermissionRegistry() permreg.PermissionRegistry { + permReg := permreg.ProvidePermissionRegistry() + // Test core permissions + permReg.RegisterPermission("datasources:read", "datasources:uid:") + permReg.RegisterPermission("dashboards:read", "dashboards:uid:") + permReg.RegisterPermission("dashboards:read", "folders:uid:") + permReg.RegisterPermission("folders:read", "folders:uid:") + // Test plugins permissions + permReg.RegisterPermission("plugins.app:access", "plugins:id:") + // App + permReg.RegisterPermission("test-app:read", "") + permReg.RegisterPermission("test-app.settings:read", "") + permReg.RegisterPermission("test-app.projects:read", "") + // App 1 + permReg.RegisterPermission("test-app1.catalog:read", "") + permReg.RegisterPermission("test-app1.announcements:read", "") + return permReg +} diff --git a/pkg/services/serviceaccounts/extsvcaccounts/service_test.go b/pkg/services/serviceaccounts/extsvcaccounts/service_test.go index 130fbc98fcc..e8f9650ca7c 100644 --- a/pkg/services/serviceaccounts/extsvcaccounts/service_test.go +++ b/pkg/services/serviceaccounts/extsvcaccounts/service_test.go @@ -15,6 +15,7 @@ import ( ac "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/extsvcauth" @@ -48,6 +49,7 @@ func setupTestEnv(t *testing.T) *TestEnv { acSvc: acimpl.ProvideOSSService( cfg, env.AcStore, &resourcepermissions.FakeActionSetSvc{}, localcache.New(0, 0), fmgt, tracing.InitializeTracerForTest(), nil, nil, + permreg.ProvidePermissionRegistry(), ), features: fmgt, logger: logger, From d03aad8b2c3e4fda54dff0667a18ba4058611d13 Mon Sep 17 00:00:00 2001 From: minuchi <112928337+minuchi@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:51:27 +0900 Subject: [PATCH 12/25] Docs: Update subtitle in Loki datasource documentation to fix link issue (#91454) --- docs/sources/datasources/loki/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/datasources/loki/_index.md b/docs/sources/datasources/loki/_index.md index e8eda35660f..1dc0024779d 100644 --- a/docs/sources/datasources/loki/_index.md +++ b/docs/sources/datasources/loki/_index.md @@ -76,7 +76,7 @@ Once you've added the Loki data source, you can [configure it](#configure-the-da To troubleshoot configuration and other issues, check the log file located at `/var/log/grafana/grafana.log` on Unix systems, or in `/data/log` on other platforms and manual installations. {{% /admonition %}} -## Provision the Loki data source +## Provision the data source You can define and configure the data source in YAML files as part of Grafana's provisioning system. For more information about provisioning, and for available configuration options, refer to [Provisioning Grafana](ref:provisioning-data-sources). From 7d06a17038923c47afbbd7b084b02d3100aa02db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 09:16:10 +0100 Subject: [PATCH 13/25] Update dependency @grafana/faro-web-tracing to v1.9.0 (#91406) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 44ff43a1731..e32756168a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3357,7 +3357,7 @@ __metadata: languageName: node linkType: hard -"@grafana/faro-web-sdk@npm:1.9.0, @grafana/faro-web-sdk@npm:^1.3.6, @grafana/faro-web-sdk@npm:^1.8.2": +"@grafana/faro-web-sdk@npm:1.9.0, @grafana/faro-web-sdk@npm:^1.3.6, @grafana/faro-web-sdk@npm:^1.9.0": version: 1.9.0 resolution: "@grafana/faro-web-sdk@npm:1.9.0" dependencies: @@ -3369,10 +3369,10 @@ __metadata: linkType: hard "@grafana/faro-web-tracing@npm:^1.8.2": - version: 1.8.2 - resolution: "@grafana/faro-web-tracing@npm:1.8.2" + version: 1.9.0 + resolution: "@grafana/faro-web-tracing@npm:1.9.0" dependencies: - "@grafana/faro-web-sdk": "npm:^1.8.2" + "@grafana/faro-web-sdk": "npm:^1.9.0" "@opentelemetry/api": "npm:^1.9.0" "@opentelemetry/context-zone": "npm:1.21.0" "@opentelemetry/core": "npm:^1.25.0" @@ -3384,7 +3384,7 @@ __metadata: "@opentelemetry/resources": "npm:^1.25.0" "@opentelemetry/sdk-trace-web": "npm:^1.25.0" "@opentelemetry/semantic-conventions": "npm:^1.25.0" - checksum: 10/6dd5d07b887de71f37176d01c68602677ebfb9fd9dbc4d6aa2456ec95fd9ceda7556ca71571f4cbaa59b126baebb8424cf34f977c6f3c10cbb3afab1692ebfc4 + checksum: 10/53aa9105c5bff80025c4d0aca0f4be90338b03143615a11207237a543bbfe14e9573d3617b52ef777dd32826bcdf80ba9dd96db0742caf9d30b0764c6d5781ca languageName: node linkType: hard From 25b65d96c7d222a6cca99e6480f10b111c1df5ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 08:19:49 +0000 Subject: [PATCH 14/25] Update dependency browserslist to v4.23.3 --- yarn.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index e32756168a5..56cb0edd70d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11889,16 +11889,16 @@ __metadata: linkType: hard "browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.2, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1": - version: 4.23.2 - resolution: "browserslist@npm:4.23.2" + version: 4.23.3 + resolution: "browserslist@npm:4.23.3" dependencies: - caniuse-lite: "npm:^1.0.30001640" - electron-to-chromium: "npm:^1.4.820" - node-releases: "npm:^2.0.14" + caniuse-lite: "npm:^1.0.30001646" + electron-to-chromium: "npm:^1.5.4" + node-releases: "npm:^2.0.18" update-browserslist-db: "npm:^1.1.0" bin: browserslist: cli.js - checksum: 10/326a98b1c39bcc9a99b197f15790dc28e122b1aead3257c837421899377ac96239123f26868698085b3d9be916d72540602738e1f857e86a387e810af3fda6e5 + checksum: 10/e266d18c6c6c5becf9a1a7aa264477677b9796387972e8fce34854bb33dc1666194dc28389780e5dc6566e68a95e87ece2ce222e1c4ca93c2b75b61dfebd5f1c languageName: node linkType: hard @@ -12141,10 +12141,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001640": - version: 1.0.30001642 - resolution: "caniuse-lite@npm:1.0.30001642" - checksum: 10/8d80ea82be453ae0fdfea8766d82740a4945c1b99189650f29bfc458d4e235d7e99027a8f8bc5a4228d8c4457ba896315284b0703f300353ad5f09d8e693de10 +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001646": + version: 1.0.30001646 + resolution: "caniuse-lite@npm:1.0.30001646" + checksum: 10/6c66a5677b58988c2ee86905b05705b00be552a3e4f768bd0d9a10098cc4ec471de5b204e2c2ab534f34b5f216c059321a9e5cb1395928cf29ded0a4aae4535f languageName: node linkType: hard @@ -14907,10 +14907,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.820": - version: 1.4.829 - resolution: "electron-to-chromium@npm:1.4.829" - checksum: 10/43279561337582ff47bb3486439efbc7c1f2192455c76ebc7374754fca61334380025af9e5da7646b4d8c007d9dc6c25d8f6059dffb2207dc39d2f79287a296a +"electron-to-chromium@npm:^1.5.4": + version: 1.5.4 + resolution: "electron-to-chromium@npm:1.5.4" + checksum: 10/ce64db25c399d33830e74e58bbc5ab7c06948669e204b6508e98c278ddaead1da1cbb356d15b55eb659f89d4d7bcf00944f08f96e886f1d3d065ba11744c5633 languageName: node linkType: hard @@ -22901,10 +22901,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.14": - version: 2.0.14 - resolution: "node-releases@npm:2.0.14" - checksum: 10/0f7607ec7db5ef1dc616899a5f24ae90c869b6a54c2d4f36ff6d84a282ab9343c7ff3ca3670fe4669171bb1e8a9b3e286e1ef1c131f09a83d70554f855d54f24 +"node-releases@npm:^2.0.18": + version: 2.0.18 + resolution: "node-releases@npm:2.0.18" + checksum: 10/241e5fa9556f1c12bafb83c6c3e94f8cf3d8f2f8f904906ecef6e10bcaa1d59aa61212d4651bec70052015fc54bd3fdcdbe7fc0f638a17e6685aa586c076ec4e languageName: node linkType: hard From ce8f5b5e1a191ced7d9e1cb3502bc98ca97524e5 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Fri, 2 Aug 2024 11:29:51 +0200 Subject: [PATCH 15/25] Navigation: Fix `` when `bodyScrolling` is enabled (#91335) fix portal when bodyScrolling is enabled --- .../src/components/Portal/Portal.tsx | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/grafana-ui/src/components/Portal/Portal.tsx b/packages/grafana-ui/src/components/Portal/Portal.tsx index 867ee890821..1dd8e29b374 100644 --- a/packages/grafana-ui/src/components/Portal/Portal.tsx +++ b/packages/grafana-ui/src/components/Portal/Portal.tsx @@ -1,8 +1,11 @@ +import { css, cx } from '@emotion/css'; import { PropsWithChildren, useLayoutEffect, useRef } from 'react'; import * as React from 'react'; import ReactDOM from 'react-dom'; -import { useTheme2 } from '../../themes'; +import { GrafanaTheme2 } from '@grafana/data'; + +import { useStyles2, useTheme2 } from '../../themes'; interface Props { className?: string; @@ -47,9 +50,27 @@ export function getPortalContainer() { /** @internal */ export function PortalContainer() { - return
; + const styles = useStyles2(getStyles); + const isBodyScrolling = window.grafanaBootData?.settings.featureToggles.bodyScrolling; + return ( +
+ ); } +const getStyles = (theme: GrafanaTheme2) => ({ + grafanaPortalContainer: css({ + position: 'fixed', + top: 0, + width: '100%', + zIndex: theme.zIndex.portal, + }), +}); + export const RefForwardingPortal = React.forwardRef((props, ref) => { return ; }); From 1d96cd8ed9f2b8f35cfddc987622a3ae5dafcc21 Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Fri, 2 Aug 2024 10:30:39 +0100 Subject: [PATCH 16/25] Alerting: Use cloud notifier types for metadata on Cloud AMs (#91054) --- .../contact-points/ContactPoint.tsx | 2 +- .../contact-points/useContactPoints.tsx | 3 ++- .../components/contact-points/utils.ts | 19 ++++++-------- .../alerting/unified/utils/receivers.ts | 25 +++++++++++-------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx index 1d671c25c21..f8b353a1059 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx @@ -39,7 +39,7 @@ export const ContactPoint = ({ const styles = useStyles2(getStyles); // TODO probably not the best way to figure out if we want to show either only the summary or full metadata for the receivers? - const showFullMetadata = receivers.some((receiver) => Boolean(receiver[RECEIVER_STATUS_KEY])); + const showFullMetadata = receivers.some((receiver) => Boolean(receiver[RECEIVER_META_KEY])); return (
diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx b/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx index f5c9dbd14a1..c661ead47f0 100644 --- a/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx +++ b/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx @@ -11,6 +11,7 @@ import { ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver, generatedReceiversApi, } from 'app/features/alerting/unified/openapi/receiversApi.gen'; +import { cloudNotifierTypes } from 'app/features/alerting/unified/utils/cloud-alertmanager-notifier-types'; import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource'; import { getNamespace, shouldUseK8sApi } from 'app/features/alerting/unified/utils/k8s/utils'; @@ -175,7 +176,7 @@ export function useContactPointsWithStatus() { contactPoints: result.data ? enhanceContactPointsWithMetadata( fetchContactPointsStatus.data, - fetchReceiverMetadata.data, + isGrafanaAlertmanager ? fetchReceiverMetadata.data : cloudNotifierTypes, onCallMetadata, result.data.alertmanager_config.receivers ?? [], result.data diff --git a/public/app/features/alerting/unified/components/contact-points/utils.ts b/public/app/features/alerting/unified/components/contact-points/utils.ts index 7f9d460b606..1fe8c162a20 100644 --- a/public/app/features/alerting/unified/components/contact-points/utils.ts +++ b/public/app/features/alerting/unified/components/contact-points/utils.ts @@ -34,13 +34,14 @@ export function getReceiverDescription(receiver: ReceiverConfigWithMetadata): Re if (!receiver.settings) { return undefined; } + const { settings } = receiver; switch (receiver.type) { case 'email': { - const hasEmailAddresses = 'addresses' in receiver.settings; // when dealing with alertmanager email_configs we don't normalize the settings - return hasEmailAddresses ? summarizeEmailAddresses(receiver.settings['addresses']) : undefined; + const addresses = settings.addresses || settings.to; // when dealing with alertmanager email_configs we don't normalize the settings + return addresses ? summarizeEmailAddresses(addresses) : undefined; } case 'slack': { - const recipient: string | undefined = receiver.settings['recipient']; + const recipient = settings.recipient || settings.channel; if (!recipient) { return; } @@ -50,12 +51,10 @@ export function getReceiverDescription(receiver: ReceiverConfigWithMetadata): Re return `#${channelName}`; } case 'kafka': { - const topicName: string | undefined = receiver.settings['kafkaTopic']; - return topicName; + return settings.kafkaTopic; } case 'webhook': { - const url: string | undefined = receiver.settings['url']; - return url; + return settings.url; } case ReceiverTypes.OnCall: { return receiver[RECEIVER_PLUGIN_META_KEY]?.description; @@ -123,11 +122,7 @@ export function enhanceContactPointsWithMetadata( const usedContactPoints = getUsedContactPoints(fullyInheritedTree); const usedContactPointsByName = groupBy(usedContactPoints, 'receiver'); - const contactPointsList = alertmanagerConfiguration - ? (alertmanagerConfiguration?.alertmanager_config.receivers ?? []) - : (contactPoints ?? []); - - const enhanced = contactPointsList.map((contactPoint) => { + const enhanced = contactPoints.map((contactPoint) => { const receivers = extractReceivers(contactPoint); const statusForReceiver = status.find((status) => status.name === contactPoint.name); diff --git a/public/app/features/alerting/unified/utils/receivers.ts b/public/app/features/alerting/unified/utils/receivers.ts index d4147cae3d1..9d9c88c960a 100644 --- a/public/app/features/alerting/unified/utils/receivers.ts +++ b/public/app/features/alerting/unified/utils/receivers.ts @@ -1,4 +1,4 @@ -import { isEmpty, times } from 'lodash'; +import { isEmpty } from 'lodash'; import { GrafanaManagedReceiverConfig, Receiver } from 'app/plugins/datasource/alertmanager/types'; @@ -12,10 +12,14 @@ import { GrafanaManagedReceiverConfig, Receiver } from 'app/plugins/datasource/a * We don't normalize the configuration settings and those are blank for vanilla Alertmanager receivers. * * Example input: - * { name: 'my receiver', email_configs: [{ from: "foo@bar.com" }] } + * ``` + * { name: 'my receiver', email_configs: [{ from: "foo@bar.com" }] } + * ``` * * Example output: - * { name: 'my receiver', grafana_managed_receiver_configs: [{ type: 'email', settings: {} }] } + * ``` + * { name: 'my receiver', grafana_managed_receiver_configs: [{ type: 'email', settings: {} }] } + * ``` */ export function extractReceivers(receiver: Receiver): GrafanaManagedReceiverConfig[] { if ('grafana_managed_receiver_configs' in receiver) { @@ -27,13 +31,14 @@ export function extractReceivers(receiver: Receiver): GrafanaManagedReceiverConf .filter(([_, value]) => Array.isArray(value) && !isEmpty(value)) .reduce((acc: GrafanaManagedReceiverConfig[], [key, value]) => { const type = key.replace('_configs', ''); - - const configs = times(value.length, () => ({ - name: receiver.name, - type: type, - settings: [], // we don't normalize the configuration values - disableResolveMessage: false, - })); + const configs = value.map((settings: unknown) => { + return { + name: receiver.name, + type, + settings, + disableResolveMessage: false, + }; + }); return acc.concat(configs); }, []); From a940bb87be19dbfe590d714df10c0f6a0566bc2f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 08:40:41 +0000 Subject: [PATCH 17/25] Update dependency msw to v2.3.5 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index cd1844afef5..4cb4868ea7c 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ "knip": "^5.10.0", "lerna": "8.1.7", "mini-css-extract-plugin": "2.9.0", - "msw": "2.3.4", + "msw": "2.3.5", "mutationobserver-shim": "0.3.7", "ngtemplate-loader": "2.1.0", "node-notifier": "10.0.1", diff --git a/yarn.lock b/yarn.lock index 56cb0edd70d..b94d7b18c69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17699,7 +17699,7 @@ __metadata: moment-timezone: "npm:0.5.45" monaco-editor: "npm:0.34.1" moveable: "npm:0.53.0" - msw: "npm:2.3.4" + msw: "npm:2.3.5" mutationobserver-shim: "npm:0.3.7" nanoid: "npm:^5.0.4" ngtemplate-loader: "npm:2.1.0" @@ -22601,9 +22601,9 @@ __metadata: languageName: node linkType: hard -"msw@npm:2.3.4": - version: 2.3.4 - resolution: "msw@npm:2.3.4" +"msw@npm:2.3.5": + version: 2.3.5 + resolution: "msw@npm:2.3.5" dependencies: "@bundled-es-modules/cookie": "npm:^2.0.0" "@bundled-es-modules/statuses": "npm:^1.0.1" @@ -22629,7 +22629,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10/d35f13d2256d336fb808381839b166986d6f9279dd35f32b6fbd9ca3420fb31bbac0fe1d2c0b0d66d2b67489bdb8e09491ac3a797f04114dd550906eb33fb9d6 + checksum: 10/c7c14f517bf4011de4d8758212f84b355433ac8087840f94a605690a1f41ea8f4a4b6e07161f734f823b2563ba0a8ea168036f59a6ccdfc895817db6eed64418 languageName: node linkType: hard From e2435f92f1472e547b7c3129bcba5d75c0064f77 Mon Sep 17 00:00:00 2001 From: Claudiu Dragalina-Paraipan Date: Fri, 2 Aug 2024 12:36:02 +0300 Subject: [PATCH 18/25] [authn]: add GetIDClaims() to Requester (#91387) * authn: add GetIDClaims() to Requester Co-Authored-By: Gabriel MABILLE * authn: update StaticRequester Co-Authored-By: Gabriel MABILLE * update auth/idtest/mock Co-Authored-By: Gabriel MABILLE * Fix test Co-authored-by: Claudiu Dragalina-Paraipan --------- Co-authored-by: Gabriel MABILLE Co-authored-by: gamab --- pkg/apimachinery/go.mod | 9 +++- pkg/apimachinery/go.sum | 8 ++++ pkg/apimachinery/identity/requester.go | 3 ++ pkg/apimachinery/identity/static.go | 17 +++++-- pkg/services/auth/id.go | 2 +- pkg/services/auth/idimpl/service.go | 57 ++++++++++++++++-------- pkg/services/auth/idimpl/service_test.go | 22 ++++++++- pkg/services/auth/idtest/mock.go | 8 ++-- pkg/services/authn/identity.go | 8 +++- pkg/services/user/identity.go | 10 ++++- 10 files changed, 112 insertions(+), 32 deletions(-) diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index 086f70b7ece..89344ca3d26 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -3,6 +3,7 @@ module github.com/grafana/grafana/pkg/apimachinery go 1.21.10 require ( + github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 github.com/stretchr/testify v1.9.0 k8s.io/apimachinery v0.29.3 k8s.io/apiserver v0.29.2 @@ -12,6 +13,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect @@ -25,10 +27,15 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index 93d7d4e25d0..9a61a32f216 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -1,5 +1,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= @@ -9,6 +10,7 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 h1:EiaupmOnt6XF/LPxvagjTofWmByzYaf5VyMIF+w/71M= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -16,12 +18,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= diff --git a/pkg/apimachinery/identity/requester.go b/pkg/apimachinery/identity/requester.go index da9609b69a0..9da9d99286e 100644 --- a/pkg/apimachinery/identity/requester.go +++ b/pkg/apimachinery/identity/requester.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + authnlib "github.com/grafana/authlib/authn" "k8s.io/apiserver/pkg/authentication/user" ) @@ -77,6 +78,8 @@ type Requester interface { // GetIDToken returns a signed token representing the identity that can be forwarded to plugins and external services. // Will only be set when featuremgmt.FlagIdForwarding is enabled. GetIDToken() string + // GetIDClaims returns the claims of the ID token. + GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims] } // IntIdentifier converts a string identifier to an int64. diff --git a/pkg/apimachinery/identity/static.go b/pkg/apimachinery/identity/static.go index af4f5fa0565..2fe79aaee8a 100644 --- a/pkg/apimachinery/identity/static.go +++ b/pkg/apimachinery/identity/static.go @@ -1,6 +1,10 @@ package identity -import "fmt" +import ( + "fmt" + + authnlib "github.com/grafana/authlib/authn" +) var _ Requester = &StaticRequester{} @@ -25,9 +29,10 @@ type StaticRequester struct { AllowedKubernetesNamespace string IsGrafanaAdmin bool // Permissions grouped by orgID and actions - Permissions map[int64]map[string][]string - IDToken string - CacheKey string + Permissions map[int64]map[string][]string + IDToken string + IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] + CacheKey string } // GetRawIdentifier implements Requester. @@ -208,3 +213,7 @@ func (u *StaticRequester) GetDisplayName() string { func (u *StaticRequester) GetIDToken() string { return u.IDToken } + +func (u *StaticRequester) GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims] { + return u.IDTokenClaims +} diff --git a/pkg/services/auth/id.go b/pkg/services/auth/id.go index 63fa61c05c9..18096eee393 100644 --- a/pkg/services/auth/id.go +++ b/pkg/services/auth/id.go @@ -10,7 +10,7 @@ import ( type IDService interface { // SignIdentity signs a id token for provided identity that can be forwarded to plugins and external services - SignIdentity(ctx context.Context, identity identity.Requester) (string, error) + SignIdentity(ctx context.Context, id identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) // RemoveIDToken removes any locally stored id tokens for key RemoveIDToken(ctx context.Context, identity identity.Requester) error diff --git a/pkg/services/auth/idimpl/service.go b/pkg/services/auth/idimpl/service.go index d5694c35cfb..f48bf1c700b 100644 --- a/pkg/services/auth/idimpl/service.go +++ b/pkg/services/auth/idimpl/service.go @@ -58,21 +58,30 @@ type Service struct { nsMapper request.NamespaceMapper } -func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (string, error) { +func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) { defer func(t time.Time) { s.metrics.tokenSigningDurationHistogram.Observe(time.Since(t).Seconds()) }(time.Now()) cacheKey := prefixCacheKey(id.GetCacheKey()) - result, err, _ := s.si.Do(cacheKey, func() (interface{}, error) { + type resultType struct { + token string + idClaims *auth.IDClaims + } + result, err, _ := s.si.Do(cacheKey, func() (any, error) { namespace, identifier := id.GetTypedID() cachedToken, err := s.cache.Get(ctx, cacheKey) if err == nil { s.metrics.tokenSigningFromCacheCounter.Inc() s.logger.FromContext(ctx).Debug("Cached token found", "namespace", namespace, "id", identifier) - return string(cachedToken), nil + + tokenClaims, err := s.extractTokenClaims(string(cachedToken)) + if err != nil { + return resultType{}, err + } + return resultType{token: string(cachedToken), idClaims: tokenClaims}, nil } s.metrics.tokenSigningCounter.Inc() @@ -104,21 +113,12 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri token, err := s.signer.SignIDToken(ctx, claims) if err != nil { s.metrics.failedTokenSigningCounter.Inc() - return "", err + return resultType{}, nil } - parsed, err := jwt.ParseSigned(token) + extracted, err := s.extractTokenClaims(token) if err != nil { - s.metrics.failedTokenSigningCounter.Inc() - return "", err - } - - extracted := auth.IDClaims{} - // We don't need to verify the signature here, we are only interested in checking - // when the token expires. - if err := parsed.UnsafeClaimsWithoutVerification(&extracted); err != nil { - s.metrics.failedTokenSigningCounter.Inc() - return "", err + return resultType{}, err } expires := time.Until(extracted.Expiry.Time()) @@ -126,14 +126,14 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri s.logger.FromContext(ctx).Error("Failed to add id token to cache", "error", err) } - return token, nil + return resultType{token: token, idClaims: claims}, nil }) if err != nil { - return "", err + return "", nil, err } - return result.(string), nil + return result.(resultType).token, result.(resultType).idClaims, nil } func (s *Service) RemoveIDToken(ctx context.Context, id identity.Requester) error { @@ -142,7 +142,7 @@ func (s *Service) RemoveIDToken(ctx context.Context, id identity.Requester) erro func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error { // FIXME(kalleep): we should probably lazy load this - token, err := s.SignIdentity(ctx, identity) + token, claims, err := s.SignIdentity(ctx, identity) if err != nil { if shouldLogErr(err) { namespace, id := identity.GetTypedID() @@ -153,9 +153,28 @@ func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.R } identity.IDToken = token + identity.IDTokenClaims = claims return nil } +func (s *Service) extractTokenClaims(token string) (*authnlib.Claims[authnlib.IDTokenClaims], error) { + parsed, err := jwt.ParseSigned(token) + if err != nil { + s.metrics.failedTokenSigningCounter.Inc() + return nil, err + } + + extracted := authnlib.Claims[authnlib.IDTokenClaims]{} + // We don't need to verify the signature here, we are only interested in checking + // when the token expires. + if err := parsed.UnsafeClaimsWithoutVerification(&extracted); err != nil { + s.metrics.failedTokenSigningCounter.Inc() + return nil, err + } + + return &extracted, nil +} + func getAudience(orgID int64) jwt.Audience { return jwt.Audience{fmt.Sprintf("org:%d", orgID)} } diff --git a/pkg/services/auth/idimpl/service_test.go b/pkg/services/auth/idimpl/service_test.go index 6b619b5075f..3cb8b36a7b9 100644 --- a/pkg/services/auth/idimpl/service_test.go +++ b/pkg/services/auth/idimpl/service_test.go @@ -70,7 +70,7 @@ func TestService_SignIdentity(t *testing.T) { featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding), &authntest.FakeService{}, nil, ) - token, err := s.SignIdentity(context.Background(), &authn.Identity{ID: identity.MustParseTypedID("user:1")}) + token, _, err := s.SignIdentity(context.Background(), &authn.Identity{ID: identity.MustParseTypedID("user:1")}) require.NoError(t, err) require.NotEmpty(t, token) }) @@ -81,7 +81,7 @@ func TestService_SignIdentity(t *testing.T) { featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding), &authntest.FakeService{}, nil, ) - token, err := s.SignIdentity(context.Background(), &authn.Identity{ + token, _, err := s.SignIdentity(context.Background(), &authn.Identity{ ID: identity.MustParseTypedID("user:1"), AuthenticatedBy: login.AzureADAuthModule, Login: "U1", @@ -97,4 +97,22 @@ func TestService_SignIdentity(t *testing.T) { assert.Equal(t, "U1", claims.Rest.Username) assert.Equal(t, "user:edpu3nnt61se8e", claims.Rest.UID) }) + + t.Run("should sign identity with authenticated by if user is externally authenticated", func(t *testing.T) { + s := ProvideService( + setting.NewCfg(), signer, remotecache.NewFakeCacheStorage(), + featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding), + &authntest.FakeService{}, nil, + ) + _, gotClaims, err := s.SignIdentity(context.Background(), &authn.Identity{ + ID: identity.MustParseTypedID("user:1"), + AuthenticatedBy: login.AzureADAuthModule, + Login: "U1", + UID: identity.NewTypedIDString(identity.TypeUser, "edpu3nnt61se8e")}) + require.NoError(t, err) + + assert.Equal(t, login.AzureADAuthModule, gotClaims.Rest.AuthenticatedBy) + assert.Equal(t, "U1", gotClaims.Rest.Username) + assert.Equal(t, "user:edpu3nnt61se8e", gotClaims.Rest.UID) + }) } diff --git a/pkg/services/auth/idtest/mock.go b/pkg/services/auth/idtest/mock.go index 2613d6a7690..cd506836d13 100644 --- a/pkg/services/auth/idtest/mock.go +++ b/pkg/services/auth/idtest/mock.go @@ -3,6 +3,8 @@ package idtest import ( "context" + authnlib "github.com/grafana/authlib/authn" + "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/auth" ) @@ -10,15 +12,15 @@ import ( var _ auth.IDService = (*MockService)(nil) type MockService struct { - SignIdentityFn func(ctx context.Context, identity identity.Requester) (string, error) + SignIdentityFn func(ctx context.Context, identity identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) RemoveIDTokenFn func(ctx context.Context, identity identity.Requester) error } -func (m *MockService) SignIdentity(ctx context.Context, identity identity.Requester) (string, error) { +func (m *MockService) SignIdentity(ctx context.Context, identity identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) { if m.SignIdentityFn != nil { return m.SignIdentityFn(ctx, identity) } - return "", nil + return "", nil, nil } func (m *MockService) RemoveIDToken(ctx context.Context, identity identity.Requester) error { diff --git a/pkg/services/authn/identity.go b/pkg/services/authn/identity.go index 51bd72e5156..b41c65c3bba 100644 --- a/pkg/services/authn/identity.go +++ b/pkg/services/authn/identity.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/grafana/authlib/authn" "golang.org/x/oauth2" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -69,7 +70,8 @@ type Identity struct { Permissions map[int64]map[string][]string // IDToken is a signed token representing the identity that can be forwarded to plugins and external services. // Will only be set when featuremgmt.FlagIdForwarding is enabled. - IDToken string + IDToken string + IDTokenClaims *authn.Claims[authn.IDTokenClaims] } // GetRawIdentifier implements Requester. @@ -156,6 +158,10 @@ func (i *Identity) GetIDToken() string { return i.IDToken } +func (i *Identity) GetIDClaims() *authn.Claims[authn.IDTokenClaims] { + return i.IDTokenClaims +} + func (i *Identity) GetIsGrafanaAdmin() bool { return i.IsGrafanaAdmin != nil && *i.IsGrafanaAdmin } diff --git a/pkg/services/user/identity.go b/pkg/services/user/identity.go index b5906276ea5..c05293ae4b5 100644 --- a/pkg/services/user/identity.go +++ b/pkg/services/user/identity.go @@ -5,6 +5,8 @@ import ( "strconv" "time" + authnlib "github.com/grafana/authlib/authn" + "github.com/grafana/grafana/pkg/apimachinery/identity" ) @@ -40,9 +42,11 @@ type SignedInUser struct { Teams []int64 // Permissions grouped by orgID and actions Permissions map[int64]map[string][]string `json:"-"` + // IDToken is a signed token representing the identity that can be forwarded to plugins and external services. // Will only be set when featuremgmt.FlagIdForwarding is enabled. - IDToken string `json:"-" xorm:"-"` + IDToken string `json:"-" xorm:"-"` + IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] `json:"-" xorm:"-"` // When other settings are not deterministic, this value is used FallbackType identity.IdentityType @@ -309,3 +313,7 @@ func (u *SignedInUser) GetDisplayName() string { func (u *SignedInUser) GetIDToken() string { return u.IDToken } + +func (u *SignedInUser) GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims] { + return u.IDTokenClaims +} From a89435aeab10a1bf17b3faef712714420d3abdbc Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Fri, 2 Aug 2024 10:46:27 +0100 Subject: [PATCH 19/25] I18n: Require Trans i18nKey prop (#91399) * I18n: Require Trans i18nKey prop * fix lint errors --- .../app/core/internationalization/index.tsx | 10 +++++++++- .../unified/components/Provisioning.tsx | 2 +- .../rule-editor/GrafanaEvaluationBehavior.tsx | 2 +- .../EmptyTransformationsMessage.tsx | 4 ++-- .../trails/ActionTabs/MetricOverviewScene.tsx | 20 +++++++------------ public/locales/en-US/grafana.json | 17 +++++++++------- public/locales/pseudo-LOCALE/grafana.json | 17 +++++++++------- 7 files changed, 40 insertions(+), 32 deletions(-) diff --git a/public/app/core/internationalization/index.tsx b/public/app/core/internationalization/index.tsx index 895d7c58627..2d31fc1d708 100644 --- a/public/app/core/internationalization/index.tsx +++ b/public/app/core/internationalization/index.tsx @@ -1,5 +1,6 @@ import i18n, { InitOptions, TFunction } from 'i18next'; import LanguageDetector, { DetectorOptions } from 'i18next-browser-languagedetector'; +import { ReactElement } from 'react'; import { Trans as I18NextTrans, initReactI18next } from 'react-i18next'; // eslint-disable-line no-restricted-imports import { DEFAULT_LANGUAGE, NAMESPACES, VALID_LANGUAGES } from './constants'; @@ -56,7 +57,14 @@ export function changeLanguage(locale: string) { return i18n.changeLanguage(validLocale); } -export const Trans: typeof I18NextTrans = (props) => { +type I18NextTransType = typeof I18NextTrans; +type I18NextTransProps = Parameters[0]; + +interface TransProps extends I18NextTransProps { + i18nKey: string; +} + +export const Trans = (props: TransProps): ReactElement => { return ; }; diff --git a/public/app/features/alerting/unified/components/Provisioning.tsx b/public/app/features/alerting/unified/components/Provisioning.tsx index dd004ce64bf..33f4d866a9d 100644 --- a/public/app/features/alerting/unified/components/Provisioning.tsx +++ b/public/app/features/alerting/unified/components/Provisioning.tsx @@ -41,7 +41,7 @@ export const ProvisioningBadge = ({ if (tooltip) { const provenanceTooltip = ( - + This resource has been provisioned via {{ provenance }} and cannot be edited through the UI ); diff --git a/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx b/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx index 35ad15987c1..5d091aa083d 100644 --- a/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx @@ -146,7 +146,7 @@ function FolderGroupAndEvaluationInterval({ {getValues('group') && getValues('evaluateEvery') && ( - + All rules in the selected group are evaluated every {{ evaluateEvery }}. {!isNewGroup && ( diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/EmptyTransformationsMessage.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/EmptyTransformationsMessage.tsx index 136e8f17426..d2f6d537559 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/EmptyTransformationsMessage.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/EmptyTransformationsMessage.tsx @@ -10,10 +10,10 @@ export function EmptyTransformationsMessage(props: EmptyTransformationsProps) { - Start transforming data + Start transforming data - + Transformations allow data to be changed in various ways before your visualization is shown.
This includes joining data together, renaming fields, making calculations, formatting data for display, and diff --git a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx index 7e1b9c141ce..b7894ca6d86 100644 --- a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx +++ b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx @@ -78,45 +78,39 @@ export class MetricOverviewScene extends SceneObjectBase - Description + Description
{metadata?.help ? (
{metadata?.help}
) : ( - No description available + No description available )}
- Type + Type {metadata?.type ? (
{metadata?.type}
) : ( - Unknown + Unknown )}
- Unit + Unit - {metadata?.unit ? ( -
{metadata?.unit}
- ) : ( - - {unit} - - )} + {metadata?.unit ?
{metadata?.unit}
: {unit}}
- Labels + Labels {labelOptions.length === 0 && 'Unable to fetch labels.'} {labelOptions.map((l) => ( diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 10df4dd789a..d1f35a9be67 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1,6 +1,5 @@ { "_comment": "The code is the source of truth for English phrases. They should be updated in the components directly, and additional plurals specified in this file.", - "{unit}": "{unit}", "access-control": { "add-permission": { "role-label": "Role", @@ -669,7 +668,6 @@ "message": "No data sources found" } }, - "Description": "Description", "explore": { "add-to-dashboard": "Add to dashboard", "add-to-library-modal": { @@ -940,7 +938,6 @@ "refresh": "Refresh" } }, - "Labels": "Labels", "library-panel": { "add-modal": { "cancel": "Cancel", @@ -1541,7 +1538,6 @@ }, "title": "Latest from the blog" }, - "No description available": "No description available", "notifications": { "empty-state": { "description": "Notifications you have received will appear here", @@ -2233,15 +2229,22 @@ "select-search-input": "Type to search (country, city, abbreviation)" } }, + "trails": { + "metric-overview": { + "description-label": "Description", + "labels-label": "Labels", + "no-description": "No description available", + "type-label": "Type", + "unit-label": "Unit", + "unknown-type": "Unknown" + } + }, "transformations": { "empty": { "add-transformation-body": "Transformations allow data to be changed in various ways before your visualization is shown.<1>This includes joining data together, renaming fields, making calculations, formatting data for display, and more.", "add-transformation-header": "Start transforming data" } }, - "Type": "Type", - "Unit": "Unit", - "Unknown": "Unknown", "user-orgs": { "current-org-button": "Current", "name-column": "Name", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index c067310672a..0ca6f638913 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -1,6 +1,5 @@ { "_comment": "Ŧĥę čőđę įş ŧĥę şőūřčę őƒ ŧřūŧĥ ƒőř Ēʼnģľįşĥ pĥřäşęş. Ŧĥęy şĥőūľđ þę ūpđäŧęđ įʼn ŧĥę čőmpőʼnęʼnŧş đįřęčŧľy, äʼnđ äđđįŧįőʼnäľ pľūřäľş şpęčįƒįęđ įʼn ŧĥįş ƒįľę.", - "{unit}": "{ūʼnįŧ}", "access-control": { "add-permission": { "role-label": "Ŗőľę", @@ -669,7 +668,6 @@ "message": "Ńő đäŧä şőūřčęş ƒőūʼnđ" } }, - "Description": "Đęşčřįpŧįőʼn", "explore": { "add-to-dashboard": "Åđđ ŧő đäşĥþőäřđ", "add-to-library-modal": { @@ -940,7 +938,6 @@ "refresh": "Ŗęƒřęşĥ" } }, - "Labels": "Ŀäþęľş", "library-panel": { "add-modal": { "cancel": "Cäʼnčęľ", @@ -1541,7 +1538,6 @@ }, "title": "Ŀäŧęşŧ ƒřőm ŧĥę þľőģ" }, - "No description available": "Ńő đęşčřįpŧįőʼn äväįľäþľę", "notifications": { "empty-state": { "description": "Ńőŧįƒįčäŧįőʼnş yőū ĥävę řęčęįvęđ ŵįľľ äppęäř ĥęřę", @@ -2233,15 +2229,22 @@ "select-search-input": "Ŧypę ŧő şęäřčĥ (čőūʼnŧřy, čįŧy, äþþřęvįäŧįőʼn)" } }, + "trails": { + "metric-overview": { + "description-label": "Đęşčřįpŧįőʼn", + "labels-label": "Ŀäþęľş", + "no-description": "Ńő đęşčřįpŧįőʼn äväįľäþľę", + "type-label": "Ŧypę", + "unit-label": "Ůʼnįŧ", + "unknown-type": "Ůʼnĸʼnőŵʼn" + } + }, "transformations": { "empty": { "add-transformation-body": "Ŧřäʼnşƒőřmäŧįőʼnş äľľőŵ đäŧä ŧő þę čĥäʼnģęđ įʼn väřįőūş ŵäyş þęƒőřę yőūř vįşūäľįžäŧįőʼn įş şĥőŵʼn.<1>Ŧĥįş įʼnčľūđęş ĵőįʼnįʼnģ đäŧä ŧőģęŧĥęř, řęʼnämįʼnģ ƒįęľđş, mäĸįʼnģ čäľčūľäŧįőʼnş, ƒőřmäŧŧįʼnģ đäŧä ƒőř đįşpľäy, äʼnđ mőřę.", "add-transformation-header": "Ŝŧäřŧ ŧřäʼnşƒőřmįʼnģ đäŧä" } }, - "Type": "Ŧypę", - "Unit": "Ůʼnįŧ", - "Unknown": "Ůʼnĸʼnőŵʼn", "user-orgs": { "current-org-button": "Cūřřęʼnŧ", "name-column": "Ńämę", From e55b438f14df1751c58a760dde8d86b4eaa8a6f5 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Fri, 2 Aug 2024 10:46:41 +0100 Subject: [PATCH 20/25] E2C: Reduce UI poll interval and make configurable (#91386) * E2C: Reduce UI poll interval and make configurable * fix default --- conf/defaults.ini | 2 ++ conf/sample.ini | 2 ++ packages/grafana-runtime/src/config.ts | 1 + pkg/api/dtos/frontend_settings.go | 5 +++-- pkg/api/frontendsettings.go | 1 + pkg/setting/setting_cloud_migration.go | 2 ++ public/app/features/migrate-to-cloud/onprem/Page.tsx | 5 ++--- 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 70053cf6526..33cc2bdc9cd 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1969,4 +1969,6 @@ domain = grafana.net snapshot_folder = "" # Link to form to give feedback on the feature feedback_url = https://docs.google.com/forms/d/e/1FAIpQLSeEE33vhbSpR8A8S1A1ocZ1ByVRRwiRl1GZr2FSrEer_tSa8w/viewform?usp=sf_link +# How frequently should the frontend UI poll for changes while resources are migrating +frontend_poll_interval = 2s diff --git a/conf/sample.ini b/conf/sample.ini index a23a17e1b14..780a2031fbc 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1900,3 +1900,5 @@ timeout = 30s ;snapshot_folder = "" # Link to form to give feedback on the feature ;feedback_url = "" +# How frequently should the frontend UI poll for changes while resources are migrating +;frontend_poll_interval = 2s diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 868a3d8a8fb..b2bc145eacf 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -182,6 +182,7 @@ export class GrafanaBootConfig implements GrafanaConfig { localFileSystemAvailable: boolean | undefined; cloudMigrationIsTarget: boolean | undefined; cloudMigrationFeedbackURL = ''; + cloudMigrationPollIntervalMs = 2000; reportingStaticContext?: Record; exploreDefaultTimeOffset = '1h'; diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go index a07a5e4358c..92ce60699b6 100644 --- a/pkg/api/dtos/frontend_settings.go +++ b/pkg/api/dtos/frontend_settings.go @@ -257,8 +257,9 @@ type FrontendSettingsDTO struct { PublicDashboardAccessToken string `json:"publicDashboardAccessToken"` PublicDashboardsEnabled bool `json:"publicDashboardsEnabled"` - CloudMigrationIsTarget bool `json:"cloudMigrationIsTarget"` - CloudMigrationFeedbackURL string `json:"cloudMigrationFeedbackURL"` + CloudMigrationIsTarget bool `json:"cloudMigrationIsTarget"` + CloudMigrationFeedbackURL string `json:"cloudMigrationFeedbackURL"` + CloudMigrationPollIntervalMs int `json:"cloudMigrationPollIntervalMs"` DateFormats setting.DateFormats `json:"dateFormats,omitempty"` diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 90b3e5317e1..148f916c4ac 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -224,6 +224,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro PublicDashboardsEnabled: hs.Cfg.PublicDashboardsEnabled, CloudMigrationIsTarget: isCloudMigrationTarget, CloudMigrationFeedbackURL: hs.Cfg.CloudMigration.FeedbackURL, + CloudMigrationPollIntervalMs: int(hs.Cfg.CloudMigration.FrontendPollInterval.Milliseconds()), SharedWithMeFolderUID: folder.SharedWithMeFolderUID, RootFolderUID: accesscontrol.GeneralFolderUID, LocalFileSystemAvailable: hs.Cfg.LocalFileSystemAvailable, diff --git a/pkg/setting/setting_cloud_migration.go b/pkg/setting/setting_cloud_migration.go index 1efc88089dd..e05eb3b7f42 100644 --- a/pkg/setting/setting_cloud_migration.go +++ b/pkg/setting/setting_cloud_migration.go @@ -24,6 +24,7 @@ type CloudMigrationSettings struct { DeleteTokenTimeout time.Duration TokenExpiresAfter time.Duration FeedbackURL string + FrontendPollInterval time.Duration IsDeveloperMode bool } @@ -49,6 +50,7 @@ func (cfg *Cfg) readCloudMigrationSettings() { cfg.CloudMigration.TokenExpiresAfter = cloudMigration.Key("token_expires_after").MustDuration(7 * 24 * time.Hour) cfg.CloudMigration.IsDeveloperMode = cloudMigration.Key("developer_mode").MustBool(false) cfg.CloudMigration.FeedbackURL = cloudMigration.Key("feedback_url").MustString("") + cfg.CloudMigration.FrontendPollInterval = cloudMigration.Key("frontend_poll_interval").MustDuration(2 * time.Second) if cfg.CloudMigration.SnapshotFolder == "" { cfg.CloudMigration.SnapshotFolder = filepath.Join(cfg.DataPath, "cloud_migration") diff --git a/public/app/features/migrate-to-cloud/onprem/Page.tsx b/public/app/features/migrate-to-cloud/onprem/Page.tsx index 4da9ea67aa7..213a9ef6449 100644 --- a/public/app/features/migrate-to-cloud/onprem/Page.tsx +++ b/public/app/features/migrate-to-cloud/onprem/Page.tsx @@ -1,6 +1,7 @@ import { skipToken } from '@reduxjs/toolkit/query/react'; import { useCallback, useEffect, useState } from 'react'; +import { config } from '@grafana/runtime'; import { AlertVariant, Box, Stack, Text } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; @@ -62,8 +63,6 @@ const SNAPSHOT_REBUILD_STATUSES: Array = ['PENDING_UPLOAD const SNAPSHOT_BUILDING_STATUSES: Array = ['INITIALIZING', 'CREATING']; const SNAPSHOT_UPLOADING_STATUSES: Array = ['UPLOADING', 'PENDING_PROCESSING', 'PROCESSING']; -const STATUS_POLL_INTERVAL = 5 * 1000; - const PAGE_SIZE = 50; function useGetLatestSnapshot(sessionUid?: string, page = 1) { @@ -78,7 +77,7 @@ function useGetLatestSnapshot(sessionUid?: string, page = 1) { : skipToken; const snapshotResult = useGetSnapshotQuery(getSnapshotQueryArgs, { - pollingInterval: shouldPoll ? STATUS_POLL_INTERVAL : 0, + pollingInterval: shouldPoll ? config.cloudMigrationPollIntervalMs : 0, skipPollingIfUnfocused: true, }); From 670132ead8b8e50ab3b83c9f31730df8a8bdf547 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 09:35:44 +0000 Subject: [PATCH 21/25] Update dependency @types/node to v20.14.14 --- package.json | 2 +- packages/grafana-data/package.json | 2 +- packages/grafana-e2e-selectors/package.json | 2 +- packages/grafana-flamegraph/package.json | 2 +- packages/grafana-icons/package.json | 2 +- .../grafana-o11y-ds-frontend/package.json | 2 +- packages/grafana-prometheus/package.json | 2 +- packages/grafana-sql/package.json | 2 +- packages/grafana-ui/package.json | 2 +- .../datasource/azuremonitor/package.json | 2 +- .../datasource/cloud-monitoring/package.json | 2 +- .../package.json | 2 +- .../grafana-pyroscope-datasource/package.json | 2 +- .../grafana-testdata-datasource/package.json | 2 +- .../plugins/datasource/jaeger/package.json | 2 +- .../app/plugins/datasource/mysql/package.json | 2 +- .../app/plugins/datasource/parca/package.json | 2 +- .../app/plugins/datasource/tempo/package.json | 2 +- .../plugins/datasource/zipkin/package.json | 2 +- yarn.lock | 46 +++++++++---------- 20 files changed, 42 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 4cb4868ea7c..d90d1eb1f8b 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "@types/lodash": "4.17.7", "@types/logfmt": "^1.2.3", "@types/lucene": "^2", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/node-forge": "^1", "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4", "@types/pluralize": "^0.0.33", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 377150b3e9c..b9e56523c4a 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -66,7 +66,7 @@ "@types/dompurify": "^3.0.0", "@types/history": "4.7.11", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/papaparse": "5.3.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index 0192643eee7..f289eb45131 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@rollup/plugin-node-resolve": "15.2.3", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "esbuild": "0.20.2", "rimraf": "5.0.7", "rollup": "2.79.1", diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 4bdae15871c..c6bb7ee4bf4 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -68,7 +68,7 @@ "@types/d3": "^7", "@types/jest": "^29.5.4", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-virtualized-auto-sizer": "1.0.4", "@types/tinycolor2": "1.4.6", diff --git a/packages/grafana-icons/package.json b/packages/grafana-icons/package.json index 2386ed8e776..a7ea505fbb0 100644 --- a/packages/grafana-icons/package.json +++ b/packages/grafana-icons/package.json @@ -45,7 +45,7 @@ "@svgr/plugin-prettier": "^8.1.0", "@svgr/plugin-svgo": "^8.1.0", "@types/babel__core": "^7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "esbuild": "0.20.2", diff --git a/packages/grafana-o11y-ds-frontend/package.json b/packages/grafana-o11y-ds-frontend/package.json index c28714bbca2..e9a02d3644e 100644 --- a/packages/grafana-o11y-ds-frontend/package.json +++ b/packages/grafana-o11y-ds-frontend/package.json @@ -36,7 +36,7 @@ "@testing-library/react": "15.0.2", "@testing-library/user-event": "14.5.2", "@types/jest": "^29.5.4", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/systemjs": "6.13.5", "@types/testing-library__jest-dom": "5.14.9", diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 35273842128..3f8a68aead2 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -92,7 +92,7 @@ "@types/jest": "29.5.12", "@types/jquery": "3.5.30", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/pluralize": "^0.0.33", "@types/prismjs": "1.26.4", "@types/react": "18.3.3", diff --git a/packages/grafana-sql/package.json b/packages/grafana-sql/package.json index 8d3c20db50e..5b0b81cf7a9 100644 --- a/packages/grafana-sql/package.json +++ b/packages/grafana-sql/package.json @@ -42,7 +42,7 @@ "@testing-library/user-event": "14.5.2", "@types/jest": "^29.5.4", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/react-virtualized-auto-sizer": "1.0.4", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 98ed90d7b60..691455a85b7 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -145,7 +145,7 @@ "@types/is-hotkey": "0.1.10", "@types/jest": "29.5.12", "@types/mock-raf": "1.0.6", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/prismjs": "1.26.4", "@types/react": "18.3.3", "@types/react-color": "3.0.12", diff --git a/public/app/plugins/datasource/azuremonitor/package.json b/public/app/plugins/datasource/azuremonitor/package.json index d9aeb16e710..401db90f6c7 100644 --- a/public/app/plugins/datasource/azuremonitor/package.json +++ b/public/app/plugins/datasource/azuremonitor/package.json @@ -32,7 +32,7 @@ "@testing-library/user-event": "14.5.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/prismjs": "1.26.4", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", diff --git a/public/app/plugins/datasource/cloud-monitoring/package.json b/public/app/plugins/datasource/cloud-monitoring/package.json index 6d6882ce46a..6dcdcb8bf2b 100644 --- a/public/app/plugins/datasource/cloud-monitoring/package.json +++ b/public/app/plugins/datasource/cloud-monitoring/package.json @@ -34,7 +34,7 @@ "@types/debounce-promise": "3.1.9", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/prismjs": "1.26.4", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json index b81bcb2cc38..db09f516cda 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json @@ -22,7 +22,7 @@ "@testing-library/user-event": "14.5.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/testing-library__jest-dom": "5.14.9", "ts-node": "10.9.2", diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json index 81d6c1c6cb2..b5302ae2e7c 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json @@ -27,7 +27,7 @@ "@testing-library/user-event": "14.5.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/prismjs": "1.26.4", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/package.json b/public/app/plugins/datasource/grafana-testdata-datasource/package.json index 891802c7759..42574cb5b54 100644 --- a/public/app/plugins/datasource/grafana-testdata-datasource/package.json +++ b/public/app/plugins/datasource/grafana-testdata-datasource/package.json @@ -30,7 +30,7 @@ "@types/d3-random": "^3.0.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/testing-library__jest-dom": "5.14.9", diff --git a/public/app/plugins/datasource/jaeger/package.json b/public/app/plugins/datasource/jaeger/package.json index e4e36419fd4..cf177421244 100644 --- a/public/app/plugins/datasource/jaeger/package.json +++ b/public/app/plugins/datasource/jaeger/package.json @@ -31,7 +31,7 @@ "@types/jest": "29.5.12", "@types/lodash": "4.17.7", "@types/logfmt": "^1.2.3", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/react-window": "1.8.8", diff --git a/public/app/plugins/datasource/mysql/package.json b/public/app/plugins/datasource/mysql/package.json index 423d72f818d..28c38167682 100644 --- a/public/app/plugins/datasource/mysql/package.json +++ b/public/app/plugins/datasource/mysql/package.json @@ -22,7 +22,7 @@ "@testing-library/user-event": "14.5.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/testing-library__jest-dom": "5.14.9", "ts-node": "10.9.2", diff --git a/public/app/plugins/datasource/parca/package.json b/public/app/plugins/datasource/parca/package.json index f5e5714b309..7666e598c06 100644 --- a/public/app/plugins/datasource/parca/package.json +++ b/public/app/plugins/datasource/parca/package.json @@ -23,7 +23,7 @@ "@testing-library/react": "15.0.2", "@testing-library/user-event": "14.5.2", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "ts-node": "10.9.2", diff --git a/public/app/plugins/datasource/tempo/package.json b/public/app/plugins/datasource/tempo/package.json index 0616776098d..db9a594992e 100644 --- a/public/app/plugins/datasource/tempo/package.json +++ b/public/app/plugins/datasource/tempo/package.json @@ -46,7 +46,7 @@ "@testing-library/user-event": "14.5.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/prismjs": "1.26.4", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", diff --git a/public/app/plugins/datasource/zipkin/package.json b/public/app/plugins/datasource/zipkin/package.json index c8c21ca6d8f..144a401ba39 100644 --- a/public/app/plugins/datasource/zipkin/package.json +++ b/public/app/plugins/datasource/zipkin/package.json @@ -25,7 +25,7 @@ "@testing-library/react": "15.0.2", "@types/jest": "29.5.12", "@types/lodash": "4.17.7", - "@types/node": "20.14.13", + "@types/node": "20.14.14", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "ts-node": "10.9.2", diff --git a/yarn.lock b/yarn.lock index b94d7b18c69..0e6a5b347bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2766,7 +2766,7 @@ __metadata: "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/prismjs": "npm:1.26.4" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" @@ -2808,7 +2808,7 @@ __metadata: "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/testing-library__jest-dom": "npm:5.14.9" lodash: "npm:4.17.21" @@ -2839,7 +2839,7 @@ __metadata: "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/prismjs": "npm:1.26.4" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" @@ -2882,7 +2882,7 @@ __metadata: "@types/d3-random": "npm:^3.0.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" "@types/testing-library__jest-dom": "npm:5.14.9" @@ -2924,7 +2924,7 @@ __metadata: "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" "@types/logfmt": "npm:^1.2.3" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" "@types/react-window": "npm:1.8.8" @@ -2963,7 +2963,7 @@ __metadata: "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/testing-library__jest-dom": "npm:5.14.9" lodash: "npm:4.17.21" @@ -2992,7 +2992,7 @@ __metadata: "@testing-library/react": "npm:15.0.2" "@testing-library/user-event": "npm:14.5.2" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" lodash: "npm:4.17.21" @@ -3029,7 +3029,7 @@ __metadata: "@types/debounce-promise": "npm:3.1.9" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/prismjs": "npm:1.26.4" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" @@ -3085,7 +3085,7 @@ __metadata: "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/prismjs": "npm:1.26.4" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" @@ -3134,7 +3134,7 @@ __metadata: "@testing-library/react": "npm:15.0.2" "@types/jest": "npm:29.5.12" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" lodash: "npm:4.17.21" @@ -3192,7 +3192,7 @@ __metadata: "@types/dompurify": "npm:^3.0.0" "@types/history": "npm:4.7.11" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/papaparse": "npm:5.3.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" @@ -3239,7 +3239,7 @@ __metadata: dependencies: "@grafana/tsconfig": "npm:^1.3.0-rc1" "@rollup/plugin-node-resolve": "npm:15.2.3" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" esbuild: "npm:0.20.2" rimraf: "npm:5.0.7" rollup: "npm:2.79.1" @@ -3408,7 +3408,7 @@ __metadata: "@types/d3": "npm:^7" "@types/jest": "npm:^29.5.4" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-virtualized-auto-sizer": "npm:1.0.4" "@types/tinycolor2": "npm:1.4.6" @@ -3492,7 +3492,7 @@ __metadata: "@testing-library/react": "npm:15.0.2" "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:^29.5.4" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/systemjs": "npm:6.13.5" "@types/testing-library__jest-dom": "npm:5.14.9" @@ -3580,7 +3580,7 @@ __metadata: "@types/jest": "npm:29.5.12" "@types/jquery": "npm:3.5.30" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/pluralize": "npm:^0.0.33" "@types/prismjs": "npm:1.26.4" "@types/react": "npm:18.3.3" @@ -3712,7 +3712,7 @@ __metadata: "@svgr/plugin-prettier": "npm:^8.1.0" "@svgr/plugin-svgo": "npm:^8.1.0" "@types/babel__core": "npm:^7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" esbuild: "npm:0.20.2" @@ -3790,7 +3790,7 @@ __metadata: "@testing-library/user-event": "npm:14.5.2" "@types/jest": "npm:^29.5.4" "@types/lodash": "npm:4.17.7" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/react": "npm:18.3.3" "@types/react-dom": "npm:18.2.25" "@types/react-virtualized-auto-sizer": "npm:1.0.4" @@ -3877,7 +3877,7 @@ __metadata: "@types/jquery": "npm:3.5.30" "@types/lodash": "npm:4.17.7" "@types/mock-raf": "npm:1.0.6" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/prismjs": "npm:1.26.4" "@types/react": "npm:18.3.3" "@types/react-color": "npm:3.0.12" @@ -9211,12 +9211,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:20.14.13, @types/node@npm:>=13.7.0, @types/node@npm:^20.11.16": - version: 20.14.13 - resolution: "@types/node@npm:20.14.13" +"@types/node@npm:*, @types/node@npm:20.14.14, @types/node@npm:>=13.7.0, @types/node@npm:^20.11.16": + version: 20.14.14 + resolution: "@types/node@npm:20.14.14" dependencies: undici-types: "npm:~5.26.4" - checksum: 10/749160b6bd9866e6169cb1a222e75aaf81da3868af1fda1e1e66d33c7e182be381f98a42b7d231fddf470f6389f2052ee842e776b3fdc677df798b933617866d + checksum: 10/035bc347e3de04888d537801e23eb4b4f99522975ca002dbfef978edd853710031b7cd43bf022670d6aba4ed5d4ac75ea1b5ff77ff8f80998bffd943b7bcef48 languageName: node linkType: hard @@ -17565,7 +17565,7 @@ __metadata: "@types/lodash": "npm:4.17.7" "@types/logfmt": "npm:^1.2.3" "@types/lucene": "npm:^2" - "@types/node": "npm:20.14.13" + "@types/node": "npm:20.14.14" "@types/node-forge": "npm:^1" "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4" "@types/pluralize": "npm:^0.0.33" From 4fd014eae9092d80b7232177dbb7f7b5f0d8acee Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Fri, 2 Aug 2024 11:39:30 +0100 Subject: [PATCH 22/25] Chore: Update i18next parser to 9.0.1 (#91478) --- package.json | 2 +- yarn.lock | 38 ++++++++++---------------------------- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index d90d1eb1f8b..fa8c6c23bc5 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "html-loader": "5.1.0", "html-webpack-plugin": "5.6.0", "http-server": "14.1.1", - "i18next-parser": "8.13.0", + "i18next-parser": "9.0.1", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", "jest-date-mock": "1.0.10", diff --git a/yarn.lock b/yarn.lock index 0e6a5b347bd..6579ea0608a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12764,10 +12764,10 @@ __metadata: languageName: node linkType: hard -"commander@npm:~11.1.0": - version: 11.1.0 - resolution: "commander@npm:11.1.0" - checksum: 10/66bd2d8a0547f6cb1d34022efb25f348e433b0e04ad76a65279b1b09da108f59a4d3001ca539c60a7a46ea38bcf399fc17d91adad76a8cf43845d8dcbaf5cda1 +"commander@npm:~12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10/cdaeb672d979816853a4eed7f1310a9319e8b976172485c2a6b437ed0db0a389a44cfb222bfbde772781efa9f215bdd1b936f80d6b249485b465c6cb906e1f93 languageName: node linkType: hard @@ -14186,13 +14186,6 @@ __metadata: languageName: node linkType: hard -"de-indent@npm:^1.0.2": - version: 1.0.2 - resolution: "de-indent@npm:1.0.2" - checksum: 10/30bf43744dca005f9252dbb34ed95dcb3c30dfe52bfed84973b89c29eccff04e27769f222a34c61a93354acf47457785e9032e6184be390ed1d324fb9ab3f427 - languageName: node - linkType: hard - "debounce-promise@npm:3.1.2": version: 3.1.2 resolution: "debounce-promise@npm:3.1.2" @@ -17664,7 +17657,7 @@ __metadata: http-server: "npm:14.1.1" i18next: "npm:^23.0.0" i18next-browser-languagedetector: "npm:^7.0.2" - i18next-parser: "npm:8.13.0" + i18next-parser: "npm:9.0.1" immer: "npm:10.1.1" immutable: "npm:4.3.7" jest: "npm:29.7.0" @@ -18494,15 +18487,15 @@ __metadata: languageName: node linkType: hard -"i18next-parser@npm:8.13.0": - version: 8.13.0 - resolution: "i18next-parser@npm:8.13.0" +"i18next-parser@npm:9.0.1": + version: 9.0.1 + resolution: "i18next-parser@npm:9.0.1" dependencies: "@babel/runtime": "npm:^7.23.2" broccoli-plugin: "npm:^4.0.7" cheerio: "npm:^1.0.0-rc.2" colors: "npm:1.4.0" - commander: "npm:~11.1.0" + commander: "npm:~12.1.0" eol: "npm:^0.9.1" esbuild: "npm:^0.20.1" fs-extra: "npm:^11.1.0" @@ -18515,10 +18508,9 @@ __metadata: typescript: "npm:^5.0.4" vinyl: "npm:~3.0.0" vinyl-fs: "npm:^4.0.0" - vue-template-compiler: "npm:^2.6.11" bin: i18next: bin/cli.js - checksum: 10/dcdb34f0418df7c969162f4c58b1f751e0245769a5748d127dcefd3d0986b45622f6b41d598920cf6fbda6972564fc48c8a5e934dc334ed46651294f98835bda + checksum: 10/d6f13c6cdc98f853b5cc433fb0853a996e9a88f83e9fe26974b4b6649a01713ec09f567869c57f21e57a7efcb731d50f296373f9647deef7a73d0d76fda63388 languageName: node linkType: hard @@ -30526,16 +30518,6 @@ __metadata: languageName: node linkType: hard -"vue-template-compiler@npm:^2.6.11": - version: 2.7.10 - resolution: "vue-template-compiler@npm:2.7.10" - dependencies: - de-indent: "npm:^1.0.2" - he: "npm:^1.2.0" - checksum: 10/9990ea1ae1f46648e366dcca6f4f164748f131ebf0588378c7427318eac02e8993780b9e0b03a510236287b02028759cc75e5bd655cb8f3cd86b4ea705f97122 - languageName: node - linkType: hard - "w3c-xmlserializer@npm:^3.0.0": version: 3.0.0 resolution: "w3c-xmlserializer@npm:3.0.0" From 4de68e37740f3d22449bcd538278dc53fafc1e76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 12:44:01 +0200 Subject: [PATCH 23/25] Update dependency eslint-plugin-jsdoc to v48.11.0 (#91473) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- packages/grafana-prometheus/package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index fa8c6c23bc5..ae06847fb0b 100644 --- a/package.json +++ b/package.json @@ -172,7 +172,7 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jest": "28.6.0", - "eslint-plugin-jsdoc": "48.10.2", + "eslint-plugin-jsdoc": "48.11.0", "eslint-plugin-jsx-a11y": "6.9.0", "eslint-plugin-lodash": "7.4.0", "eslint-plugin-no-barrel-files": "^1.1.0", diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 3f8a68aead2..d05b669c953 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -111,7 +111,7 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jest": "28.6.0", - "eslint-plugin-jsdoc": "48.10.2", + "eslint-plugin-jsdoc": "48.11.0", "eslint-plugin-jsx-a11y": "6.9.0", "eslint-plugin-lodash": "7.4.0", "eslint-plugin-react": "7.35.0", diff --git a/yarn.lock b/yarn.lock index 6579ea0608a..c5dc42b76e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3602,7 +3602,7 @@ __metadata: eslint-config-prettier: "npm:9.1.0" eslint-plugin-import: "npm:^2.26.0" eslint-plugin-jest: "npm:28.6.0" - eslint-plugin-jsdoc: "npm:48.10.2" + eslint-plugin-jsdoc: "npm:48.11.0" eslint-plugin-jsx-a11y: "npm:6.9.0" eslint-plugin-lodash: "npm:7.4.0" eslint-plugin-react: "npm:7.35.0" @@ -15634,9 +15634,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jsdoc@npm:48.10.2": - version: 48.10.2 - resolution: "eslint-plugin-jsdoc@npm:48.10.2" +"eslint-plugin-jsdoc@npm:48.11.0": + version: 48.11.0 + resolution: "eslint-plugin-jsdoc@npm:48.11.0" dependencies: "@es-joy/jsdoccomment": "npm:~0.46.0" are-docs-informative: "npm:^0.0.2" @@ -15651,7 +15651,7 @@ __metadata: synckit: "npm:^0.9.1" peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - checksum: 10/0685131bfdf3a58efbcb37bfccd36ee6251755b2d961d944f5df89e855fdb0264ed107c097b79489a2a2fe34fe80082c3b31f348be6167e68ded6615f768037d + checksum: 10/3bc2533656e9ccfdadbcd71a6f7c1ec125b1965c6e399a43c40408b51b4f8c26e44031f077c947b15d68b9cd317e7e8be1e2b222a46fb3c24a25377a2643796b languageName: node linkType: hard @@ -17636,7 +17636,7 @@ __metadata: eslint-config-prettier: "npm:9.1.0" eslint-plugin-import: "npm:^2.26.0" eslint-plugin-jest: "npm:28.6.0" - eslint-plugin-jsdoc: "npm:48.10.2" + eslint-plugin-jsdoc: "npm:48.11.0" eslint-plugin-jsx-a11y: "npm:6.9.0" eslint-plugin-lodash: "npm:7.4.0" eslint-plugin-no-barrel-files: "npm:^1.1.0" From 44ed331239d8ba1299ecd5108b702d5e08ed0529 Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:47:03 +0100 Subject: [PATCH 24/25] I18n: Download translations from Crowdin (#91474) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/de-DE/grafana.json | 17 ++++++++++------- public/locales/es-ES/grafana.json | 17 ++++++++++------- public/locales/fr-FR/grafana.json | 17 ++++++++++------- public/locales/pt-BR/grafana.json | 17 ++++++++++------- public/locales/zh-Hans/grafana.json | 17 ++++++++++------- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 9d8447d1c3c..da660dd7fa8 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -1,6 +1,5 @@ { "_comment": "", - "{unit}": "", "access-control": { "add-permission": { "role-label": "Rolle", @@ -669,7 +668,6 @@ "message": "Keine Datenquellen gefunden" } }, - "Description": "", "explore": { "add-to-dashboard": "Zum Dashboard hinzufügen", "add-to-library-modal": { @@ -940,7 +938,6 @@ "refresh": "Aktualisieren" } }, - "Labels": "", "library-panel": { "add-modal": { "cancel": "Abbrechen", @@ -1541,7 +1538,6 @@ }, "title": "Das Neueste aus dem Blog" }, - "No description available": "", "notifications": { "empty-state": { "description": "", @@ -2233,15 +2229,22 @@ "select-search-input": "Suchbegriff eingeben (Land, Stadt, Abkürzung)" } }, + "trails": { + "metric-overview": { + "description-label": "", + "labels-label": "", + "no-description": "", + "type-label": "", + "unit-label": "", + "unknown-type": "" + } + }, "transformations": { "empty": { "add-transformation-body": "Mithilfe von Transformationen können Daten auf verschiedene Arten geändert werden, bevor Ihre Visualisierung angezeigt wird.<1>Dies beinhaltet die Verknüpfung von Daten, das Umbenennen von Feldern, die Erstellung von Berechnungen, das Formatieren von Daten für die Anzeige und mehr.", "add-transformation-header": "Daten transformieren beginnen" } }, - "Type": "", - "Unit": "", - "Unknown": "", "user-orgs": { "current-org-button": "Aktuell", "name-column": "Name", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index d65385c3aff..2226877e29b 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -1,6 +1,5 @@ { "_comment": "", - "{unit}": "", "access-control": { "add-permission": { "role-label": "Rol", @@ -669,7 +668,6 @@ "message": "" } }, - "Description": "", "explore": { "add-to-dashboard": "Añadir al tablero", "add-to-library-modal": { @@ -940,7 +938,6 @@ "refresh": "Actualizar" } }, - "Labels": "", "library-panel": { "add-modal": { "cancel": "Cancelar", @@ -1541,7 +1538,6 @@ }, "title": "Últimas entradas del blog" }, - "No description available": "", "notifications": { "empty-state": { "description": "", @@ -2233,15 +2229,22 @@ "select-search-input": "Escribir para buscar (país, ciudad, abreviatura)" } }, + "trails": { + "metric-overview": { + "description-label": "", + "labels-label": "", + "no-description": "", + "type-label": "", + "unit-label": "", + "unknown-type": "" + } + }, "transformations": { "empty": { "add-transformation-body": "Las transformaciones permiten cambiar los datos de varias maneras antes de que se muestre su visualización.<1>Aquí se incluyen acciones como unir datos, renombrar campos, hacer cálculos, dar formato a los datos para mostrarlos, etc.", "add-transformation-header": "Empezar a transformar datos" } }, - "Type": "", - "Unit": "", - "Unknown": "", "user-orgs": { "current-org-button": "Actual", "name-column": "Nombre", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 88c03b2bcca..eb55de35e52 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -1,6 +1,5 @@ { "_comment": "", - "{unit}": "", "access-control": { "add-permission": { "role-label": "Rôle", @@ -669,7 +668,6 @@ "message": "" } }, - "Description": "", "explore": { "add-to-dashboard": "Ajouter au tableau de bord", "add-to-library-modal": { @@ -940,7 +938,6 @@ "refresh": "Actualiser" } }, - "Labels": "", "library-panel": { "add-modal": { "cancel": "Annuler", @@ -1541,7 +1538,6 @@ }, "title": "Dernières nouvelles sur le blog" }, - "No description available": "", "notifications": { "empty-state": { "description": "", @@ -2233,15 +2229,22 @@ "select-search-input": "Tapez pour rechercher (pays, ville, abréviation)" } }, + "trails": { + "metric-overview": { + "description-label": "", + "labels-label": "", + "no-description": "", + "type-label": "", + "unit-label": "", + "unknown-type": "" + } + }, "transformations": { "empty": { "add-transformation-body": "Les transformations permettent de modifier les données de différentes manières avant l'affichage de votre visualisation.<1>Vous pouvez notamment rassembler des données, renommer des champs, faire des calculs, formater les données à afficher, etc.", "add-transformation-header": "Commencer la transformation des données" } }, - "Type": "", - "Unit": "", - "Unknown": "", "user-orgs": { "current-org-button": "Actuel", "name-column": "Nom", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 945ecd589bc..01d608d7383 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -1,6 +1,5 @@ { "_comment": "", - "{unit}": "", "access-control": { "add-permission": { "role-label": "Função", @@ -669,7 +668,6 @@ "message": "" } }, - "Description": "", "explore": { "add-to-dashboard": "Adicionar ao painel de controle", "add-to-library-modal": { @@ -940,7 +938,6 @@ "refresh": "Atualizar" } }, - "Labels": "", "library-panel": { "add-modal": { "cancel": "Cancelar", @@ -1541,7 +1538,6 @@ }, "title": "Mais recentes do blog" }, - "No description available": "", "notifications": { "empty-state": { "description": "", @@ -2233,15 +2229,22 @@ "select-search-input": "Tipo de busca (país, cidade, abreviação)" } }, + "trails": { + "metric-overview": { + "description-label": "", + "labels-label": "", + "no-description": "", + "type-label": "", + "unit-label": "", + "unknown-type": "" + } + }, "transformations": { "empty": { "add-transformation-body": "As transformações permitem que os dados sejam alterados de várias maneiras antes de sua visualização ser mostrada. <1>Isso inclui juntar dados, renomear campos, fazer cálculos, formatar dados para exibição e muito mais.", "add-transformation-header": "Começar a transformar dados" } }, - "Type": "", - "Unit": "", - "Unknown": "", "user-orgs": { "current-org-button": "Atual", "name-column": "Nome", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index 94502a89b6d..630a7a615ed 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -1,6 +1,5 @@ { "_comment": "", - "{unit}": "", "access-control": { "add-permission": { "role-label": "角色", @@ -663,7 +662,6 @@ "message": "" } }, - "Description": "", "explore": { "add-to-dashboard": "添加到仪表板", "add-to-library-modal": { @@ -934,7 +932,6 @@ "refresh": "刷新" } }, - "Labels": "", "library-panel": { "add-modal": { "cancel": "取消", @@ -1534,7 +1531,6 @@ }, "title": "最新博客" }, - "No description available": "", "notifications": { "empty-state": { "description": "", @@ -2224,15 +2220,22 @@ "select-search-input": "输入以搜索(国家、城市、缩写)" } }, + "trails": { + "metric-overview": { + "description-label": "", + "labels-label": "", + "no-description": "", + "type-label": "", + "unit-label": "", + "unknown-type": "" + } + }, "transformations": { "empty": { "add-transformation-body": "转换允许在显示可视化之前以各种方式更改数据。 <1>这包括合并数据、重命名字段、进行计算、格式化数据以便显示等等。", "add-transformation-header": "开始转换数据" } }, - "Type": "", - "Unit": "", - "Unknown": "", "user-orgs": { "current-org-button": "当前", "name-column": "姓名", From 0145b0fe7044b16efc73bb9f67dd92efc5ae1656 Mon Sep 17 00:00:00 2001 From: Alexa V <239999+axelavargas@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:30:47 +0200 Subject: [PATCH 25/25] Dashboard: Chore - Remove rudderstack events from panel menu and toolbar (#91376) * Chore: Remove rudderstack events from panel interactions, and toolbar in dashboards - dashboards_panelheader_menu - dashboards_panelheader_description_displayed - dashboards_toolbar_actions_clicked * Add back some toolbar events: sharing, settings, favourites, and add * restore ShareButton --- .../scene/NavToolbarActions.tsx | 5 -- .../scene/PanelMenuBehavior.tsx | 22 ------ .../transformSaveModelToScene.ts | 3 - .../dashboard-scene/utils/interactions.ts | 68 ++++--------------- .../dashboard/components/DashNav/DashNav.tsx | 12 +--- .../dashboard/dashgrid/PanelChromeAngular.tsx | 1 - .../dashboard/dashgrid/PanelStateWrapper.tsx | 1 - .../dashboard/utils/getPanelChromeProps.tsx | 5 -- .../features/dashboard/utils/getPanelMenu.ts | 14 ---- 9 files changed, 16 insertions(+), 115 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index c40f3e08697..0d44671f1e6 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -496,7 +496,6 @@ export function ToolbarActions({ dashboard }: Props) { return (