From 66aec9ec1fa4dbf7b3ead71d1b5a390a194e3675 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Mon, 3 Feb 2025 17:21:34 +0100 Subject: [PATCH 01/12] Docs: Fix typos in Cloud Migration Assistant standalone guide (#99959) * Docs: fix typo important admonition in Cloud Migration Assistant page * Docs: fix typo in feature toggle name in Cloud Migration Assistant page * Docs: fix bullet point
  • missing in Cloud Migration Assistant page --- docs/sources/administration/migration-guide/_index.md | 8 ++++---- .../migration-guide/cloud-migration-assistant.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sources/administration/migration-guide/_index.md b/docs/sources/administration/migration-guide/_index.md index 9fb1faa2ddf..deb7de8c533 100644 --- a/docs/sources/administration/migration-guide/_index.md +++ b/docs/sources/administration/migration-guide/_index.md @@ -15,9 +15,9 @@ title: Migrate from Grafana OSS/Enterprise to Grafana Cloud When you decide to migrate from your self-managed Grafana instance to Grafana Cloud, you can benefit from the convenience of a managed observability platform, additional cloud-only features, and robust security. There are a couple of key approaches to help you transition to Grafana Cloud. -| Migration type | Tools used | Availability | Migratable resources | -| :------------- | :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Manual | | Generally available in all versions of Grafana OSS/Enterprise | The entire Grafana instance | -| Automated | The Grafana Cloud Migration Assistant | Available in public preview from Grafana v11.2 using the `OnPremToCloudMigration` feature toggle. This toggle is enabled by default in Grafana v11.5 and later. | | +| Migration type | Tools used | Availability | Migratable resources | +| :------------- | :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Manual | | Generally available in all versions of Grafana OSS/Enterprise | The entire Grafana instance | +| Automated | The Grafana Cloud Migration Assistant | Available in public preview from Grafana v11.2 using the `onPremToCloudMigrations` feature toggle. This toggle is enabled by default in Grafana v11.5 and later. | | Our detailed [migration guide](https://www.grafana.com/docs/grafana-cloud/account-management/migration-guide/manually-migrate-to-grafana-cloud/) explains the key steps and scripts to manually migrate your resources to Grafana Cloud, covering a comprehensive set of resources in your Grafana instance. Alternatively, the [Grafana Cloud Migration Assistant](https://www.grafana.com/docs/grafana-cloud/account-management/migration-guide/cloud-migration-assistant/), available in public preview in Grafana v11.2 and later, automates the migration process across a broad range of Grafana resources. You can use the migration assistant to migrate a large proportion of your Grafana resources and then, if needed, leverage the migration guide to migrate the rest. diff --git a/docs/sources/administration/migration-guide/cloud-migration-assistant.md b/docs/sources/administration/migration-guide/cloud-migration-assistant.md index 4ee56b63c8b..87a5a47cb5a 100644 --- a/docs/sources/administration/migration-guide/cloud-migration-assistant.md +++ b/docs/sources/administration/migration-guide/cloud-migration-assistant.md @@ -58,7 +58,7 @@ In Grafana Enterprise, the server administrator has access to the migration assi ### Grant access in Grafana Enterprise -{{< admonition type="impotrtant">}} +{{< admonition type="important">}} You must [configure RBAC](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/configure-rbac/) before you can grant other administrators access to the Grafana Migration Assistant. {{< /admonition >}} From 8a259ecafaf6c0141285c76a853f105bb95d1cce Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Mon, 3 Feb 2025 17:43:28 +0000 Subject: [PATCH 02/12] Graphite: Ensure frames have `refId` set (#99911) Ensure frames have refIDs - Sanitise the refID - Alias the query target with the refID - Set the appropriate refID on the created frames - Update tests --- .../datasource/graphite/datasource.test.ts | 35 ++++++++++++------- .../plugins/datasource/graphite/datasource.ts | 20 +++++++++-- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/public/app/plugins/datasource/graphite/datasource.test.ts b/public/app/plugins/datasource/graphite/datasource.test.ts index 849271524eb..a9268dbaed9 100644 --- a/public/app/plugins/datasource/graphite/datasource.test.ts +++ b/public/app/plugins/datasource/graphite/datasource.test.ts @@ -55,6 +55,10 @@ describe('graphiteDatasource', () => { describe('convertResponseToDataFrames', () => { it('should transform regular result', () => { + const refIDMap = { + refIDA: 'A', + refIDB: 'B', + }; const result = ctx.ds.convertResponseToDataFrames( createFetchResponse({ meta: { @@ -65,7 +69,7 @@ describe('graphiteDatasource', () => { }, series: [ { - target: 'seriesA', + target: 'seriesA refIDA', datapoints: [ [100, 200], [101, 201], @@ -85,7 +89,7 @@ describe('graphiteDatasource', () => { ], }, { - target: 'seriesB', + target: 'seriesB refIDB', meta: [ { 'aggnum-norm': 1, @@ -105,7 +109,8 @@ describe('graphiteDatasource', () => { ], }, ], - }) + }), + refIDMap ); expect(result.data.length).toBe(2); @@ -113,24 +118,28 @@ describe('graphiteDatasource', () => { expect(getFrameDisplayName(result.data[1])).toBe('seriesB'); expect(result.data[0].length).toBe(2); expect(result.data[0].meta.notices.length).toBe(1); + expect(result.data[0].refId).toBe('A'); expect(result.data[0].meta.notices[0].text).toBe('Data is rolled up, aggregated over 2h using Average function'); expect(result.data[1].meta.notices).toBeUndefined(); + expect(result.data[1].refId).toBe('B'); }); }); describe('When querying graphite with one target using query editor target spec', () => { - const query = { - panelId: 3, - dashboardId: 5, - range: { from: dateTime('2022-04-01T00:00:00'), to: dateTime('2022-07-01T00:00:00') }, - targets: [{ target: 'prod1.count' }, { target: 'prod2.count' }], - maxDataPoints: 500, - }; - let response: unknown; let requestOptions: BackendSrvRequest; beforeEach(() => { + const query = { + panelId: 3, + dashboardId: 5, + range: { from: dateTime('2022-04-01T00:00:00'), to: dateTime('2022-07-01T00:00:00') }, + targets: [ + { target: 'prod1.count', refId: 'A' }, + { target: 'prod2.count', refId: 'B' }, + ], + maxDataPoints: 500, + }; fetchMock.mockImplementation((options) => { requestOptions = options; return of( @@ -164,8 +173,8 @@ describe('graphiteDatasource', () => { it('should query correctly', () => { const params = requestOptions.data.split('&'); - expect(params).toContain('target=prod1.count'); - expect(params).toContain('target=prod2.count'); + expect(params).toContain(`target=${encodeURIComponent(`aliasSub(prod1.count, "(^.*$)", "\\1 A")`)}`); + expect(params).toContain(`target=${encodeURIComponent(`aliasSub(prod2.count, "(^.*$)", "\\1 B")`)}`); expect(params).toContain('from=1648789200'); expect(params).toContain('until=1656655200'); }); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index e3ab7e93dc7..23979d1d546 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -209,6 +209,17 @@ export class GraphiteDatasource return merge(...streams); } + // Use this object to map the original refID of the query to our sanitised one + const refIds: { [key: string]: string } = {}; + for (const target of options.targets) { + // Sanitise the refID otherwise the Graphite query will fail + const formattedRefId = target.refId.replaceAll(' ', '_'); + refIds[formattedRefId] = target.refId; + // Use aliasSub to include the refID in the response series name. This allows us to set the refID on the frame. + const updatedTarget = `aliasSub(${target.target}, "(^.*$)", "\\1 ${formattedRefId}")`; + target.target = updatedTarget; + } + // handle the queries here const graphOptions = { from: this.translateTime(options.range.from, false, options.timezone), @@ -243,7 +254,7 @@ export class GraphiteDatasource httpOptions.requestId = this.name + '.panelId.' + options.panelId; } - return this.doGraphiteRequest(httpOptions).pipe(map(this.convertResponseToDataFrames)); + return this.doGraphiteRequest(httpOptions).pipe(map((result) => this.convertResponseToDataFrames(result, refIds))); } addTracingHeaders( @@ -267,7 +278,7 @@ export class GraphiteDatasource } } - convertResponseToDataFrames = (result: FetchResponse): DataQueryResponse => { + convertResponseToDataFrames = (result: FetchResponse, refIdMap: { [key: string]: string }): DataQueryResponse => { const data: DataFrame[] = []; if (!result || !result.data) { return { data }; @@ -282,6 +293,9 @@ export class GraphiteDatasource for (let i = 0; i < series.length; i++) { const s = series[i]; + // Retrieve the original refID of the query + const [target, refId] = s.target.split(' '); + s.target = target; // Disables Grafana own series naming s.title = s.target; @@ -291,6 +305,8 @@ export class GraphiteDatasource } const frame = toDataFrame(s); + // Set the refID value on the frame + frame.refId = refIdMap[refId]; // Metrictank metadata if (s.meta) { From ac41c193508ac514b85ebafa15442df583fa0992 Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Mon, 3 Feb 2025 13:26:18 -0500 Subject: [PATCH 03/12] Alerting: Rule version history API (#99041) * implement store method to read rule versions * implement request handler * declare a new endpoint * fix fake to return correct response * add tests * add integration tests * rename history to versions * apply diff from swagger CI step Signed-off-by: Yuri Tseretyan --------- Signed-off-by: Yuri Tseretyan --- pkg/services/ngalert/api/api_ruler.go | 25 ++++ pkg/services/ngalert/api/api_ruler_test.go | 103 +++++++++++++++++ pkg/services/ngalert/api/authorization.go | 3 +- .../ngalert/api/authorization_test.go | 2 +- pkg/services/ngalert/api/forking_ruler.go | 4 + .../ngalert/api/generated_base_api_ruler.go | 18 +++ pkg/services/ngalert/api/persist.go | 2 +- pkg/services/ngalert/api/tooling/api.json | 60 +++++++++- .../api/tooling/definitions/cortex-ruler.go | 17 ++- pkg/services/ngalert/api/tooling/post.json | 95 +++++++++++++++- pkg/services/ngalert/api/tooling/spec.json | 95 +++++++++++++++- pkg/services/ngalert/models/testing.go | 13 +++ pkg/services/ngalert/store/alert_rule.go | 30 +++++ pkg/services/ngalert/store/compat.go | 31 +++++ pkg/services/ngalert/store/compat_test.go | 17 +++ pkg/services/ngalert/tests/fakes/rules.go | 29 ++++- pkg/tests/api/alerting/api_ruler_test.go | 107 ++++++++++++++++++ pkg/tests/api/alerting/testing.go | 23 ++++ public/api-merged.json | 57 +++++++++- public/openapi3.json | 57 +++++++++- 20 files changed, 772 insertions(+), 16 deletions(-) diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index f0cc114d77a..215048e448c 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "slices" + "sort" "strings" "time" @@ -330,6 +331,30 @@ func (srv RulerSrv) RouteGetRuleByUID(c *contextmodel.ReqContext, ruleUID string return response.JSON(http.StatusOK, result) } +func (srv RulerSrv) RouteGetRuleVersionsByUID(c *contextmodel.ReqContext, ruleUID string) response.Response { + ctx := c.Req.Context() + // make sure the user has access to the current version of the rule. Also, check if it exists + _, err := srv.getAuthorizedRuleByUid(ctx, c, ruleUID) + if err != nil { + if errors.Is(err, ngmodels.ErrAlertRuleNotFound) { + return response.Empty(http.StatusNotFound) + } + return response.ErrOrFallback(http.StatusInternalServerError, "failed to get rule by UID", err) + } + + rules, err := srv.store.GetAlertRuleVersions(ctx, ngmodels.AlertRuleKey{OrgID: c.OrgID, UID: ruleUID}) + if err != nil { + return response.ErrOrFallback(http.StatusInternalServerError, "failed to get rule history", err) + } + sort.Slice(rules, func(i, j int) bool { return rules[i].ID > rules[j].ID }) + result := make(apimodels.GettableRuleVersions, 0, len(rules)) + for _, rule := range rules { + // do not provide provenance status because we do not have historical changes for it + result = append(result, toGettableExtendedRuleNode(*rule, map[string]ngmodels.Provenance{}, srv.resolveUserIdToNameFn(ctx))) + } + return response.JSON(http.StatusOK, result) +} + func (srv RulerSrv) RoutePostNameRulesConfig(c *contextmodel.ReqContext, ruleGroupConfig apimodels.PostableRuleGroupConfig, namespaceUID string) response.Response { namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), namespaceUID, c.SignedInUser.GetOrgID(), c.SignedInUser) if err != nil { diff --git a/pkg/services/ngalert/api/api_ruler_test.go b/pkg/services/ngalert/api/api_ruler_test.go index eec2d83729a..dce8f9a801c 100644 --- a/pkg/services/ngalert/api/api_ruler_test.go +++ b/pkg/services/ngalert/api/api_ruler_test.go @@ -448,6 +448,109 @@ func TestRouteGetRuleByUID(t *testing.T) { }) } +func TestRouteGetRuleHistoryByUID(t *testing.T) { + orgID := rand.Int63() + f := randFolder() + groupKey := models.GenerateGroupKey(orgID) + groupKey.NamespaceUID = f.UID + gen := models.RuleGen.With(models.RuleGen.WithGroupKey(groupKey), models.RuleGen.WithUniqueID()) + + t.Run("rule history is successfully fetched with the correct UID", func(t *testing.T) { + ruleStore := fakes.NewRuleStore(t) + ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f) + + rule := gen.GenerateRef() + history := gen.With(gen.WithUID(rule.UID)).GenerateManyRef(3) + // simulate order of the history + rule.ID = 100 + for i, alertRule := range history { + alertRule.ID = rule.ID - int64(i) - 1 + } + + ruleStore.PutRule(context.Background(), rule) + ruleStore.History[rule.GetKey()] = append(ruleStore.History[rule.GetKey()], history...) + + perms := createPermissionsForRules([]*models.AlertRule{rule}, orgID) + req := createRequestContextWithPerms(orgID, perms, nil) + + svc := createService(ruleStore) + response := svc.RouteGetRuleVersionsByUID(req, rule.UID) + + require.Equal(t, http.StatusOK, response.Status()) + var result apimodels.GettableRuleVersions + require.NoError(t, json.Unmarshal(response.Body(), &result)) + require.NotNil(t, result) + + require.Len(t, result, len(history)+1) // history + current version + + t.Run("should be in correct order", func(t *testing.T) { + expectedHistory := append([]*models.AlertRule{rule}, history...) + for i, rul := range expectedHistory { + assert.Equal(t, rul.UID, result[i].GrafanaManagedAlert.UID) + } + }) + }) + + t.Run("NotFound when rule does not exist", func(t *testing.T) { + ruleStore := fakes.NewRuleStore(t) + ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f) + ruleKey := models.AlertRuleKey{ + OrgID: orgID, + UID: "test", + } + history := gen.With(gen.WithKey(ruleKey)).GenerateManyRef(3) + ruleStore.History[ruleKey] = append(ruleStore.History[ruleKey], history...) // even if history is full of records + + perms := createPermissionsForRules(history, orgID) + req := createRequestContextWithPerms(orgID, perms, nil) + response := createService(ruleStore).RouteGetRuleVersionsByUID(req, ruleKey.UID) + + require.Equal(t, http.StatusNotFound, response.Status()) + }) + + t.Run("Empty result when rule history is empty", func(t *testing.T) { + ruleStore := fakes.NewRuleStore(t) + ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f) + ruleKey := models.AlertRuleKey{ + OrgID: orgID, + UID: "test", + } + rule := gen.With(gen.WithKey(ruleKey)).GenerateRef() + ruleStore.PutRule(context.Background(), rule) + ruleStore.History[ruleKey] = nil + + perms := createPermissionsForRules([]*models.AlertRule{rule}, orgID) + req := createRequestContextWithPerms(orgID, perms, nil) + response := createService(ruleStore).RouteGetRuleVersionsByUID(req, ruleKey.UID) + + require.Equal(t, http.StatusOK, response.Status()) + + var result apimodels.GettableRuleVersions + require.NoError(t, json.Unmarshal(response.Body(), &result)) + require.Empty(t, result) + }) + + t.Run("Unauthorized if user does not have access to the current rule", func(t *testing.T) { + ruleStore := fakes.NewRuleStore(t) + anotherFolder := randFolder() + ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f, anotherFolder) + ruleKey := models.AlertRuleKey{ + OrgID: orgID, + UID: "test", + } + rule := gen.With(gen.WithKey(ruleKey), gen.WithNamespaceUID(anotherFolder.UID)).GenerateRef() + ruleStore.PutRule(context.Background(), rule) + history := gen.With(gen.WithKey(ruleKey)).GenerateManyRef(3) + ruleStore.History[ruleKey] = history + + perms := createPermissionsForRules(history, orgID) // grant permissions to all records in history but not the rule itself + req := createRequestContextWithPerms(orgID, perms, nil) + response := createService(ruleStore).RouteGetRuleVersionsByUID(req, ruleKey.UID) + + require.Equal(t, http.StatusForbidden, response.Status()) + }) +} + func TestRouteGetRulesConfig(t *testing.T) { gen := models.RuleGen t.Run("fine-grained access is enabled", func(t *testing.T) { diff --git a/pkg/services/ngalert/api/authorization.go b/pkg/services/ngalert/api/authorization.go index 083ae995637..ac5aacfd93d 100644 --- a/pkg/services/ngalert/api/authorization.go +++ b/pkg/services/ngalert/api/authorization.go @@ -41,7 +41,8 @@ func (api *API) authorize(method, path string) web.Handler { case http.MethodGet + "/api/ruler/grafana/api/v1/rules", http.MethodGet + "/api/ruler/grafana/api/v1/export/rules": eval = ac.EvalPermission(ac.ActionAlertingRuleRead) - case http.MethodGet + "/api/ruler/grafana/api/v1/rule/{RuleUID}": + case http.MethodGet + "/api/ruler/grafana/api/v1/rule/{RuleUID}", + http.MethodGet + "/api/ruler/grafana/api/v1/rule/{RuleUID}/versions": eval = ac.EvalAll( ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(dashboards.ActionFoldersRead), diff --git a/pkg/services/ngalert/api/authorization_test.go b/pkg/services/ngalert/api/authorization_test.go index 76eaae47437..1f83e8750cf 100644 --- a/pkg/services/ngalert/api/authorization_test.go +++ b/pkg/services/ngalert/api/authorization_test.go @@ -41,7 +41,7 @@ func TestAuthorize(t *testing.T) { } paths[p] = methods } - require.Len(t, paths, 59) + require.Len(t, paths, 60) ac := acmock.New() api := &API{AccessControl: ac, FeatureManager: featuremgmt.WithFeatures()} diff --git a/pkg/services/ngalert/api/forking_ruler.go b/pkg/services/ngalert/api/forking_ruler.go index e1b9f2e570c..16029af2414 100644 --- a/pkg/services/ngalert/api/forking_ruler.go +++ b/pkg/services/ngalert/api/forking_ruler.go @@ -124,3 +124,7 @@ func (f *RulerApiHandler) getService(ctx *contextmodel.ReqContext) (*LotexRuler, } return f.LotexRuler, nil } + +func (f *RulerApiHandler) handleRouteGetRuleVersionsByUID(ctx *contextmodel.ReqContext, ruleUID string) response.Response { + return f.GrafanaRuler.RouteGetRuleVersionsByUID(ctx, ruleUID) +} diff --git a/pkg/services/ngalert/api/generated_base_api_ruler.go b/pkg/services/ngalert/api/generated_base_api_ruler.go index 0e06ea76f9a..d7d4c56428a 100644 --- a/pkg/services/ngalert/api/generated_base_api_ruler.go +++ b/pkg/services/ngalert/api/generated_base_api_ruler.go @@ -29,6 +29,7 @@ type RulerApi interface { RouteGetNamespaceGrafanaRulesConfig(*contextmodel.ReqContext) response.Response RouteGetNamespaceRulesConfig(*contextmodel.ReqContext) response.Response RouteGetRuleByUID(*contextmodel.ReqContext) response.Response + RouteGetRuleVersionsByUID(*contextmodel.ReqContext) response.Response RouteGetRulegGroupConfig(*contextmodel.ReqContext) response.Response RouteGetRulesConfig(*contextmodel.ReqContext) response.Response RouteGetRulesForExport(*contextmodel.ReqContext) response.Response @@ -86,6 +87,11 @@ func (f *RulerApiHandler) RouteGetRuleByUID(ctx *contextmodel.ReqContext) respon ruleUIDParam := web.Params(ctx.Req)[":RuleUID"] return f.handleRouteGetRuleByUID(ctx, ruleUIDParam) } +func (f *RulerApiHandler) RouteGetRuleVersionsByUID(ctx *contextmodel.ReqContext) response.Response { + // Parse Path Parameters + ruleUIDParam := web.Params(ctx.Req)[":RuleUID"] + return f.handleRouteGetRuleVersionsByUID(ctx, ruleUIDParam) +} func (f *RulerApiHandler) RouteGetRulegGroupConfig(ctx *contextmodel.ReqContext) response.Response { // Parse Path Parameters datasourceUIDParam := web.Params(ctx.Req)[":DatasourceUID"] @@ -243,6 +249,18 @@ func (api *API) RegisterRulerApiEndpoints(srv RulerApi, m *metrics.API) { m, ), ) + group.Get( + toMacaronPath("/api/ruler/grafana/api/v1/rule/{RuleUID}/versions"), + requestmeta.SetOwner(requestmeta.TeamAlerting), + requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), + api.authorize(http.MethodGet, "/api/ruler/grafana/api/v1/rule/{RuleUID}/versions"), + metrics.Instrument( + http.MethodGet, + "/api/ruler/grafana/api/v1/rule/{RuleUID}/versions", + api.Hooks.Wrap(srv.RouteGetRuleVersionsByUID), + m, + ), + ) group.Get( toMacaronPath("/api/ruler/{DatasourceUID}/api/v1/rules/{Namespace}/{Groupname}"), requestmeta.SetOwner(requestmeta.TeamAlerting), diff --git a/pkg/services/ngalert/api/persist.go b/pkg/services/ngalert/api/persist.go index b05abd5f6df..952db94f1f5 100644 --- a/pkg/services/ngalert/api/persist.go +++ b/pkg/services/ngalert/api/persist.go @@ -28,6 +28,6 @@ type RuleStore interface { // IncreaseVersionForAllRulesInNamespaces Increases version for all rules that have specified namespace uids IncreaseVersionForAllRulesInNamespaces(ctx context.Context, orgID int64, namespaceUIDs []string) ([]ngmodels.AlertRuleKeyWithVersion, error) - + GetAlertRuleVersions(ctx context.Context, key ngmodels.AlertRuleKey) ([]*ngmodels.AlertRule, error) accesscontrol.RuleUIDToNamespaceStore } diff --git a/pkg/services/ngalert/api/tooling/api.json b/pkg/services/ngalert/api/tooling/api.json index b28835c9327..24d1d9d81c4 100644 --- a/pkg/services/ngalert/api/tooling/api.json +++ b/pkg/services/ngalert/api/tooling/api.json @@ -459,6 +459,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -498,10 +501,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type", @@ -1166,6 +1174,14 @@ "CounterResetHint": { "$ref": "#/definitions/CounterResetHint" }, + "CustomValues": { + "description": "Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.\nThis slice is interned, to be treated as immutable and copied by reference.\nThese numbers should be strictly increasing. This field is only used when the\nschema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans\nand NegativeBuckets fields are not used in that case.", + "items": { + "format": "double", + "type": "number" + }, + "type": "array" + }, "PositiveBuckets": { "description": "Observation counts in buckets. Each represents an absolute count and\nmust be zero or positive.", "items": { @@ -1182,7 +1198,7 @@ "type": "array" }, "Schema": { - "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8. They are all for\nbase-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets. Or\nin other words, each bucket boundary is the previous boundary times\n2^(2^-n).", + "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8 for exponential buckets.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in\neach case, and then each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times\n2^(2^-n). Another valid schema number is -53 for custom buckets, defined by\nthe CustomValues field.", "format": "int32", "type": "integer" }, @@ -1628,6 +1644,9 @@ "format": "date-time", "type": "string" }, + "updated_by": { + "$ref": "#/definitions/UserInfo" + }, "version": { "format": "int64", "type": "integer" @@ -1712,6 +1731,12 @@ }, "type": "object" }, + "GettableRuleVersions": { + "items": { + "$ref": "#/definitions/GettableExtendedRuleNode" + }, + "type": "array" + }, "GettableStatus": { "properties": { "cluster": { @@ -3572,6 +3597,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -3593,10 +3621,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type" @@ -3636,6 +3669,9 @@ "file": { "type": "string" }, + "folderUid": { + "type": "string" + }, "interval": { "format": "double", "type": "number" @@ -3665,6 +3701,7 @@ "required": [ "name", "file", + "folderUid", "rules", "interval" ], @@ -3767,6 +3804,10 @@ "Sample": { "description": "Sample is a single sample belonging to a metric. It represents either a float\nsample or a histogram sample. If H is nil, it is a float sample. Otherwise,\nit is a histogram sample.", "properties": { + "DropName": { + "description": "DropName is used to indicate whether the __name__ label should be dropped\nas part of the query evaluation.", + "type": "boolean" + }, "F": { "format": "double", "type": "number" @@ -4382,7 +4423,6 @@ "type": "object" }, "URL": { - "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nThe Host field contains the host and port subcomponents of the URL.\nWhen the port is present, it is separated from the host with a colon.\nWhen the host is an IPv6 address, it must be enclosed in square brackets:\n\"[fe80::1]:80\". The [net.JoinHostPort] function combines a host and port\ninto a string suitable for the Host field, adding square brackets to\nthe host when necessary.\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use the [URL.EscapedPath] method, which preserves\nthe original encoding of Path.\n\nThe RawPath field is an optional field which is only set when the default\nencoding of Path is different from the escaped path. See the EscapedPath method\nfor more details.\n\nURL's String method uses the EscapedPath method to obtain the path.", "properties": { "ForceQuery": { "type": "boolean" @@ -4418,7 +4458,7 @@ "$ref": "#/definitions/Userinfo" } }, - "title": "A URL represents a parsed URL (technically, a URI reference).", + "title": "URL is a custom URL type that allows validation at configuration load time.", "type": "object" }, "UpdateRuleGroupResponse": { @@ -4447,6 +4487,18 @@ }, "type": "object" }, + "UserInfo": { + "properties": { + "name": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "title": "UserInfo represents user-related information, including a unique identifier and a name.", + "type": "object" + }, "Userinfo": { "description": "The Userinfo type is an immutable encapsulation of username and\npassword details for a [URL]. An existing Userinfo value is guaranteed\nto have a username set (potentially empty, as allowed by RFC 2396),\nand optionally a password.", "type": "object" @@ -4648,6 +4700,7 @@ "type": "object" }, "alertGroups": { + "description": "AlertGroups alert groups", "items": { "$ref": "#/definitions/alertGroup", "type": "object" @@ -4934,6 +4987,7 @@ "type": "object" }, "gettableSilences": { + "description": "GettableSilences gettable silences", "items": { "$ref": "#/definitions/gettableSilence", "type": "object" diff --git a/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go b/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go index 502e167f10c..d97b99512bf 100644 --- a/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go +++ b/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go @@ -20,6 +20,18 @@ import ( // 403: ForbiddenError // 404: description: Not found. +// swagger:route Get /ruler/grafana/api/v1/rule/{RuleUID}/versions ruler RouteGetRuleVersionsByUID +// +// Get rule versions by UID +// +// Produces: +// - application/json +// +// Responses: +// 202: GettableRuleVersions +// 403: ForbiddenError +// 404: description: Not found. + // swagger:route Get /ruler/grafana/api/v1/rules ruler RouteGetGrafanaRulesConfig // // List rule groups @@ -218,7 +230,7 @@ type PathGetRulesParams struct { PanelID int64 } -// swagger:parameters RouteGetRuleByUID +// swagger:parameters RouteGetRuleByUID RouteGetRuleVersionsByUID type PathGetRuleByUIDParams struct { // in: path RuleUID string @@ -290,6 +302,9 @@ func (c *PostableRuleGroupConfig) validate() error { return nil } +// swagger:model +type GettableRuleVersions []GettableExtendedRuleNode + // swagger:model type GettableRuleGroupConfig struct { Name string `yaml:"name" json:"name"` diff --git a/pkg/services/ngalert/api/tooling/post.json b/pkg/services/ngalert/api/tooling/post.json index b0c7b79ac8a..0e02ed6f68a 100644 --- a/pkg/services/ngalert/api/tooling/post.json +++ b/pkg/services/ngalert/api/tooling/post.json @@ -459,6 +459,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -498,10 +501,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type", @@ -1166,6 +1174,14 @@ "CounterResetHint": { "$ref": "#/definitions/CounterResetHint" }, + "CustomValues": { + "description": "Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.\nThis slice is interned, to be treated as immutable and copied by reference.\nThese numbers should be strictly increasing. This field is only used when the\nschema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans\nand NegativeBuckets fields are not used in that case.", + "items": { + "format": "double", + "type": "number" + }, + "type": "array" + }, "PositiveBuckets": { "description": "Observation counts in buckets. Each represents an absolute count and\nmust be zero or positive.", "items": { @@ -1182,7 +1198,7 @@ "type": "array" }, "Schema": { - "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8. They are all for\nbase-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets. Or\nin other words, each bucket boundary is the previous boundary times\n2^(2^-n).", + "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8 for exponential buckets.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in\neach case, and then each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times\n2^(2^-n). Another valid schema number is -53 for custom buckets, defined by\nthe CustomValues field.", "format": "int32", "type": "integer" }, @@ -1628,6 +1644,9 @@ "format": "date-time", "type": "string" }, + "updated_by": { + "$ref": "#/definitions/UserInfo" + }, "version": { "format": "int64", "type": "integer" @@ -1712,6 +1731,12 @@ }, "type": "object" }, + "GettableRuleVersions": { + "items": { + "$ref": "#/definitions/GettableExtendedRuleNode" + }, + "type": "array" + }, "GettableStatus": { "properties": { "cluster": { @@ -3572,6 +3597,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -3593,10 +3621,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type" @@ -3636,6 +3669,9 @@ "file": { "type": "string" }, + "folderUid": { + "type": "string" + }, "interval": { "format": "double", "type": "number" @@ -3665,6 +3701,7 @@ "required": [ "name", "file", + "folderUid", "rules", "interval" ], @@ -3767,6 +3804,10 @@ "Sample": { "description": "Sample is a single sample belonging to a metric. It represents either a float\nsample or a histogram sample. If H is nil, it is a float sample. Otherwise,\nit is a histogram sample.", "properties": { + "DropName": { + "description": "DropName is used to indicate whether the __name__ label should be dropped\nas part of the query evaluation.", + "type": "boolean" + }, "F": { "format": "double", "type": "number" @@ -4446,6 +4487,18 @@ }, "type": "object" }, + "UserInfo": { + "properties": { + "name": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "title": "UserInfo represents user-related information, including a unique identifier and a name.", + "type": "object" + }, "Userinfo": { "description": "The Userinfo type is an immutable encapsulation of username and\npassword details for a [URL]. An existing Userinfo value is guaranteed\nto have a username set (potentially empty, as allowed by RFC 2396),\nand optionally a password.", "type": "object" @@ -4647,6 +4700,7 @@ "type": "object" }, "alertGroups": { + "description": "AlertGroups alert groups", "items": { "$ref": "#/definitions/alertGroup", "type": "object" @@ -4808,6 +4862,7 @@ "type": "object" }, "gettableAlerts": { + "description": "GettableAlerts gettable alerts", "items": { "$ref": "#/definitions/gettableAlert", "type": "object" @@ -4932,6 +4987,7 @@ "type": "object" }, "gettableSilences": { + "description": "GettableSilences gettable silences", "items": { "$ref": "#/definitions/gettableSilence", "type": "object" @@ -6515,6 +6571,43 @@ ] } }, + "/ruler/grafana/api/v1/rule/{RuleUID}/versions": { + "get": { + "description": "Get rule versions by UID", + "operationId": "RouteGetRuleVersionsByUID", + "parameters": [ + { + "in": "path", + "name": "RuleUID", + "required": true, + "type": "string" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "202": { + "description": "GettableRuleVersions", + "schema": { + "$ref": "#/definitions/GettableRuleVersions" + } + }, + "403": { + "description": "ForbiddenError", + "schema": { + "$ref": "#/definitions/ForbiddenError" + } + }, + "404": { + "description": " Not found." + } + }, + "tags": [ + "ruler" + ] + } + }, "/ruler/grafana/api/v1/rules": { "get": { "description": "List rule groups", diff --git a/pkg/services/ngalert/api/tooling/spec.json b/pkg/services/ngalert/api/tooling/spec.json index c041526254d..960fb35a5fe 100644 --- a/pkg/services/ngalert/api/tooling/spec.json +++ b/pkg/services/ngalert/api/tooling/spec.json @@ -1343,6 +1343,43 @@ } } }, + "/ruler/grafana/api/v1/rule/{RuleUID}/versions": { + "get": { + "description": "Get rule versions by UID", + "produces": [ + "application/json" + ], + "tags": [ + "ruler" + ], + "operationId": "RouteGetRuleVersionsByUID", + "parameters": [ + { + "type": "string", + "name": "RuleUID", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "GettableRuleVersions", + "schema": { + "$ref": "#/definitions/GettableRuleVersions" + } + }, + "403": { + "description": "ForbiddenError", + "schema": { + "$ref": "#/definitions/ForbiddenError" + } + }, + "404": { + "description": " Not found." + } + } + } + }, "/ruler/grafana/api/v1/rules": { "get": { "description": "List rule groups", @@ -4088,7 +4125,9 @@ "description": "adapted from cortex", "type": "object", "required": [ + "uid", "name", + "folderUid", "query", "health", "type", @@ -4118,6 +4157,9 @@ "type": "number", "format": "double" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -4157,6 +4199,9 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } } }, @@ -4818,6 +4863,14 @@ "CounterResetHint": { "$ref": "#/definitions/CounterResetHint" }, + "CustomValues": { + "description": "Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.\nThis slice is interned, to be treated as immutable and copied by reference.\nThese numbers should be strictly increasing. This field is only used when the\nschema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans\nand NegativeBuckets fields are not used in that case.", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, "PositiveBuckets": { "description": "Observation counts in buckets. Each represents an absolute count and\nmust be zero or positive.", "type": "array", @@ -4834,7 +4887,7 @@ } }, "Schema": { - "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8. They are all for\nbase-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets. Or\nin other words, each bucket boundary is the previous boundary times\n2^(2^-n).", + "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8 for exponential buckets.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in\neach case, and then each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times\n2^(2^-n). Another valid schema number is -53 for custom buckets, defined by\nthe CustomValues field.", "type": "integer", "format": "int32" }, @@ -5279,6 +5332,9 @@ "type": "string", "format": "date-time" }, + "updated_by": { + "$ref": "#/definitions/UserInfo" + }, "version": { "type": "integer", "format": "int64" @@ -5362,6 +5418,12 @@ } } }, + "GettableRuleVersions": { + "type": "array", + "items": { + "$ref": "#/definitions/GettableExtendedRuleNode" + } + }, "GettableStatus": { "type": "object", "required": [ @@ -7220,7 +7282,9 @@ "description": "adapted from cortex", "type": "object", "required": [ + "uid", "name", + "folderUid", "query", "health", "type" @@ -7230,6 +7294,9 @@ "type": "number", "format": "double" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -7251,6 +7318,9 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } } }, @@ -7283,6 +7353,7 @@ "required": [ "name", "file", + "folderUid", "rules", "interval" ], @@ -7294,6 +7365,9 @@ "file": { "type": "string" }, + "folderUid": { + "type": "string" + }, "interval": { "type": "number", "format": "double" @@ -7419,6 +7493,10 @@ "description": "Sample is a single sample belonging to a metric. It represents either a float\nsample or a histogram sample. If H is nil, it is a float sample. Otherwise,\nit is a histogram sample.", "type": "object", "properties": { + "DropName": { + "description": "DropName is used to indicate whether the __name__ label should be dropped\nas part of the query evaluation.", + "type": "boolean" + }, "F": { "type": "number", "format": "double" @@ -8097,6 +8175,18 @@ } } }, + "UserInfo": { + "type": "object", + "title": "UserInfo represents user-related information, including a unique identifier and a name.", + "properties": { + "name": { + "type": "string" + }, + "uid": { + "type": "string" + } + } + }, "Userinfo": { "description": "The Userinfo type is an immutable encapsulation of username and\npassword details for a [URL]. An existing Userinfo value is guaranteed\nto have a username set (potentially empty, as allowed by RFC 2396),\nand optionally a password.", "type": "object" @@ -8298,6 +8388,7 @@ } }, "alertGroups": { + "description": "AlertGroups alert groups", "type": "array", "items": { "type": "object", @@ -8459,6 +8550,7 @@ } }, "gettableAlerts": { + "description": "GettableAlerts gettable alerts", "type": "array", "items": { "type": "object", @@ -8583,6 +8675,7 @@ } }, "gettableSilences": { + "description": "GettableSilences gettable silences", "type": "array", "items": { "type": "object", diff --git a/pkg/services/ngalert/models/testing.go b/pkg/services/ngalert/models/testing.go index e5b988cf084..515b8eed07c 100644 --- a/pkg/services/ngalert/models/testing.go +++ b/pkg/services/ngalert/models/testing.go @@ -550,6 +550,19 @@ func (a *AlertRuleMutators) WithUpdatedBy(uid *UserUID) AlertRuleMutator { } } +func (a *AlertRuleMutators) WithUID(uid string) AlertRuleMutator { + return func(r *AlertRule) { + r.UID = uid + } +} + +func (a *AlertRuleMutators) WithKey(key AlertRuleKey) AlertRuleMutator { + return func(r *AlertRule) { + r.UID = key.UID + r.OrgID = key.OrgID + } +} + func (g *AlertRuleGenerator) GenerateLabels(min, max int, prefix string) data.Labels { count := max if min > max { diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index b248d8e4fee..adcfe8450fa 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -118,6 +118,36 @@ func (st DBstore) GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAler return result, err } +func (st DBstore) GetAlertRuleVersions(ctx context.Context, key ngmodels.AlertRuleKey) ([]*ngmodels.AlertRule, error) { + alertRules := make([]*ngmodels.AlertRule, 0) + err := st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { + rows, err := sess.Table(new(alertRuleVersion)).Where("rule_org_id = ? AND rule_uid = ?", key.OrgID, key.UID).Desc("id").Rows(new(alertRuleVersion)) + if err != nil { + return err + } + // Deserialize each rule separately in case any of them contain invalid JSON. + for rows.Next() { + rule := new(alertRuleVersion) + err = rows.Scan(rule) + if err != nil { + st.Logger.Error("Invalid rule version found in DB store, ignoring it", "func", "GetAlertRuleVersions", "error", err) + continue + } + converted, err := alertRuleToModelsAlertRule(alertRuleVersionToAlertRule(*rule), st.Logger) + if err != nil { + st.Logger.Error("Invalid rule found in DB store, cannot convert, ignoring it", "func", "GetAlertRuleVersions", "error", err, "version_id", rule.ID) + continue + } + alertRules = append(alertRules, &converted) + } + return nil + }) + if err != nil { + return nil, err + } + return alertRules, nil +} + // GetRuleByID retrieves models.AlertRule by ID. // It returns models.ErrAlertRuleNotFound if no alert rule is found for the provided ID. func (st DBstore) GetRuleByID(ctx context.Context, query ngmodels.GetAlertRuleByIDQuery) (result *ngmodels.AlertRule, err error) { diff --git a/pkg/services/ngalert/store/compat.go b/pkg/services/ngalert/store/compat.go index ed36782114b..cdb85d526c6 100644 --- a/pkg/services/ngalert/store/compat.go +++ b/pkg/services/ngalert/store/compat.go @@ -204,3 +204,34 @@ func alertRuleToAlertRuleVersion(rule alertRule) alertRuleVersion { Metadata: rule.Metadata, } } + +func alertRuleVersionToAlertRule(version alertRuleVersion) alertRule { + return alertRule{ + ID: version.ID, + OrgID: version.RuleOrgID, + Title: version.Title, + Condition: version.Condition, + Data: version.Data, + Updated: version.Created, + UpdatedBy: version.CreatedBy, + IntervalSeconds: version.IntervalSeconds, + Version: version.Version, + UID: version.RuleUID, + NamespaceUID: version.RuleNamespaceUID, + // Versions do not store Dashboard\Panel as separate column. + // However, these fields are part of annotations and information in these fields is redundant + DashboardUID: nil, + PanelID: nil, + RuleGroup: version.RuleGroup, + RuleGroupIndex: version.RuleGroupIndex, + Record: version.Record, + NoDataState: version.NoDataState, + ExecErrState: version.ExecErrState, + For: version.For, + Annotations: version.Annotations, + Labels: version.Labels, + IsPaused: version.IsPaused, + NotificationSettings: version.NotificationSettings, + Metadata: version.Metadata, + } +} diff --git a/pkg/services/ngalert/store/compat_test.go b/pkg/services/ngalert/store/compat_test.go index 6bae3726e30..ecc9898589f 100644 --- a/pkg/services/ngalert/store/compat_test.go +++ b/pkg/services/ngalert/store/compat_test.go @@ -43,3 +43,20 @@ func TestAlertRuleToModelsAlertRule(t *testing.T) { require.Equal(t, ngmodels.ErrorErrState, converted.ExecErrState) }) } + +func TestAlertRuleVersionToAlertRule(t *testing.T) { + g := ngmodels.RuleGen + + t.Run("make sure no data is lost between conversions", func(t *testing.T) { + for _, rule := range g.GenerateMany(100) { + // ignore fields + rule.DashboardUID = nil + rule.PanelID = nil + + r, err := alertRuleFromModelsAlertRule(rule) + require.NoError(t, err) + r2 := alertRuleVersionToAlertRule(alertRuleToAlertRuleVersion(r)) + require.Equal(t, r, r2) + } + }) +} diff --git a/pkg/services/ngalert/tests/fakes/rules.go b/pkg/services/ngalert/tests/fakes/rules.go index 3784ee6ed9b..5cdabe62857 100644 --- a/pkg/services/ngalert/tests/fakes/rules.go +++ b/pkg/services/ngalert/tests/fakes/rules.go @@ -22,6 +22,7 @@ type RuleStore struct { mtx sync.Mutex // OrgID -> RuleGroup -> Namespace -> Rules Rules map[int64][]*models.AlertRule + History map[models.AlertRuleKey][]*models.AlertRule Hook func(cmd any) error // use Hook if you need to intercept some query and return an error RecordedOps []any Folders map[int64][]*folder.Folder @@ -40,6 +41,7 @@ func NewRuleStore(t *testing.T) *RuleStore { return nil }, Folders: map[int64][]*folder.Folder{}, + History: map[models.AlertRuleKey][]*models.AlertRule{}, } } @@ -50,6 +52,8 @@ func (f *RuleStore) PutRule(_ context.Context, rules ...*models.AlertRule) { mainloop: for _, r := range rules { rgs := f.Rules[r.OrgID] + cp := models.CopyRule(r) + f.History[r.GetKey()] = append(f.History[r.GetKey()], cp) for idx, rulePtr := range rgs { if rulePtr.UID == r.UID { rgs[idx] = r @@ -131,11 +135,7 @@ func (f *RuleStore) GetAlertRuleByUID(_ context.Context, q *models.GetAlertRuleB if err := f.Hook(*q); err != nil { return nil, err } - rules, ok := f.Rules[q.OrgID] - if !ok { - return nil, nil - } - + rules := f.Rules[q.OrgID] for _, rule := range rules { if rule.UID == q.UID { return rule, nil @@ -372,3 +372,22 @@ func (f *RuleStore) GetNamespacesByRuleUID(ctx context.Context, orgID int64, uid return namespacesMap, nil } + +func (f *RuleStore) GetAlertRuleVersions(_ context.Context, key models.AlertRuleKey) ([]*models.AlertRule, error) { + f.mtx.Lock() + defer f.mtx.Unlock() + + q := GenericRecordedQuery{ + Name: "GetAlertRuleVersions", + Params: []any{key}, + } + defer func() { + f.RecordedOps = append(f.RecordedOps, q) + }() + + if err := f.Hook(key); err != nil { + return nil, err + } + + return f.History[key], nil +} diff --git a/pkg/tests/api/alerting/api_ruler_test.go b/pkg/tests/api/alerting/api_ruler_test.go index 568205ef36e..6da7f57c963 100644 --- a/pkg/tests/api/alerting/api_ruler_test.go +++ b/pkg/tests/api/alerting/api_ruler_test.go @@ -241,6 +241,18 @@ func TestIntegrationAlertRulePermissions(t *testing.T) { require.Len(t, export.Groups, 1) require.Equal(t, expected, export.Groups[0]) }) + + t.Run("Get versions of any rule", func(t *testing.T) { + for _, groups := range allRules { // random rule from each folder + group := groups[rand.Intn(len(groups))] + rule := group.Rules[rand.Intn(len(group.Rules))] + versions, status, raw := apiClient.GetRuleVersionsWithStatus(t, rule.GrafanaManagedAlert.UID) + if assert.Equalf(t, http.StatusOK, status, "Expected status 200, got %d: %s", status, raw) { + assert.NotEmpty(t, versions) + assert.Equal(t, rule, versions[0]) // the first version in the collection should always be the current + } + } + }) }) t.Run("when permissions for folder2 removed", func(t *testing.T) { @@ -310,6 +322,12 @@ func TestIntegrationAlertRulePermissions(t *testing.T) { require.Equal(t, http.StatusForbidden, status) }) + t.Run("Versions of rule", func(t *testing.T) { + uid := allRules["folder2"][0].Rules[0].GrafanaManagedAlert.UID + _, status, raw := apiClient.GetRuleVersionsWithStatus(t, uid) + require.Equalf(t, http.StatusForbidden, status, "Expected status 403, got %d: %s", status, raw) + }) + t.Run("when all permissions are revoked", func(t *testing.T) { removeFolderPermission(t, permissionsStore, 1, userID, org.RoleEditor, "folder1") apiClient.ReloadCachedPermissions(t) @@ -4315,6 +4333,95 @@ func TestIntegrationRuleUpdateAllDatabases(t *testing.T) { }) } +func TestIntegrationRuleVersions(t *testing.T) { + testinfra.SQLiteIntegrationTest(t) + + // Setup Grafana and its Database + dir, p := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + DisableLegacyAlerting: true, + EnableUnifiedAlerting: true, + EnableQuota: true, + DisableAnonymous: true, + AppModeProduction: true, + }) + + grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p) + + createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleEditor), + Password: "password", + Login: "grafana", + }) + + apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password") + + // Create the namespace we'll save our alerts to. + apiClient.CreateFolder(t, "folder1", "folder1") + + postGroupRaw, err := testData.ReadFile(path.Join("test-data", "rulegroup-1-post.json")) + require.NoError(t, err) + var group1 apimodels.PostableRuleGroupConfig + require.NoError(t, json.Unmarshal(postGroupRaw, &group1)) + + // Create rule under folder1 + response := apiClient.PostRulesGroup(t, "folder1", &group1) + + require.NotEmptyf(t, response.Created, "Expected created to be set") + uid := response.Created[0] + + ruleV1 := apiClient.GetRuleByUID(t, uid) + + t.Run("should return 1 version right after creation", func(t *testing.T) { + versions, status, raw := apiClient.GetRuleVersionsWithStatus(t, uid) + require.Equalf(t, http.StatusOK, status, "Expected status 200, got %d: %s", status, raw) + require.Lenf(t, versions, 1, "Expected 1 version, got %d", len(versions)) + assert.Equal(t, ruleV1, versions[0]) + }) + + group1Gettable := apiClient.GetRulesGroup(t, "folder1", group1.Name) + group1 = convertGettableRuleGroupToPostable(group1Gettable.GettableRuleGroupConfig) + group1.Rules[0].Annotations[util.GenerateShortUID()] = util.GenerateShortUID() + + _ = apiClient.PostRulesGroup(t, "folder1", &group1) + + ruleV2 := apiClient.GetRuleByUID(t, uid) + + t.Run("should return previous versions after update", func(t *testing.T) { + versions, status, raw := apiClient.GetRuleVersionsWithStatus(t, uid) + require.Equalf(t, http.StatusOK, status, "Expected status 200, got %d: %s", status, raw) + require.Lenf(t, versions, 2, "Expected 2 versions, got %d", len(versions)) + + pathsToIgnore := []string{ + "GrafanaManagedAlert.ID", // In versions ID has different value + } + // compare expected and actual and ignore the dynamic fields + diff := cmp.Diff(apimodels.GettableRuleVersions{ruleV2, ruleV1}, versions, cmp.FilterPath(func(path cmp.Path) bool { + for _, s := range pathsToIgnore { + if strings.Contains(path.String(), s) { + return true + } + } + return false + }, cmp.Ignore())) + assert.Empty(t, diff) + }) + + _ = apiClient.PostRulesGroup(t, "folder1", &group1) // Noop update + + t.Run("should not add new version if rule was not changed", func(t *testing.T) { + versions, status, raw := apiClient.GetRuleVersionsWithStatus(t, uid) + require.Equalf(t, http.StatusOK, status, "Expected status 200, got %d: %s", status, raw) + require.Lenf(t, versions, 2, "Expected 2 versions, got %d", len(versions)) + }) + + apiClient.DeleteRulesGroup(t, "folder1", group1.Name) + + t.Run("should NotFound after rule was deleted", func(t *testing.T) { + _, status, raw := apiClient.GetRuleVersionsWithStatus(t, uid) + require.Equalf(t, http.StatusNotFound, status, "Expected status 404, got %d: %s", status, raw) + }) +} + func newTestingRuleConfig(t *testing.T) apimodels.PostableRuleGroupConfig { interval, err := model.ParseDuration("1m") require.NoError(t, err) diff --git a/pkg/tests/api/alerting/testing.go b/pkg/tests/api/alerting/testing.go index c66281d7af7..4e06ed72945 100644 --- a/pkg/tests/api/alerting/testing.go +++ b/pkg/tests/api/alerting/testing.go @@ -475,6 +475,13 @@ func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group * return m, resp.StatusCode, string(b) } +func (a apiClient) PostRulesGroup(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) apimodels.UpdateRuleGroupResponse { + t.Helper() + m, status, raw := a.PostRulesGroupWithStatus(t, folder, group) + requireStatusCode(t, http.StatusAccepted, status, raw) + return m +} + func (a apiClient) PostRulesExportWithStatus(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig, params *apimodels.ExportQueryParams) (int, string) { t.Helper() buf := bytes.Buffer{} @@ -1048,6 +1055,22 @@ func (a apiClient) GetActiveAlertsWithStatus(t *testing.T) (apimodels.AlertGroup return sendRequest[apimodels.AlertGroups](t, req, http.StatusOK) } +func (a apiClient) GetRuleVersionsWithStatus(t *testing.T, ruleUID string) (apimodels.GettableRuleVersions, int, string) { + t.Helper() + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/ruler/grafana/api/v1/rule/%s/versions", a.url, ruleUID), nil) + require.NoError(t, err) + return sendRequest[apimodels.GettableRuleVersions](t, req, http.StatusOK) +} + +func (a apiClient) GetRuleByUID(t *testing.T, ruleUID string) apimodels.GettableExtendedRuleNode { + t.Helper() + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/ruler/grafana/api/v1/rule/%s", a.url, ruleUID), nil) + require.NoError(t, err) + rule, status, raw := sendRequest[apimodels.GettableExtendedRuleNode](t, req, http.StatusOK) + requireStatusCode(t, http.StatusOK, status, raw) + return rule +} + func sendRequest[T any](t *testing.T, req *http.Request, successStatusCode int) (T, int, string) { t.Helper() client := &http.Client{} diff --git a/public/api-merged.json b/public/api-merged.json index 8a09345e112..e6dd277974a 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -12861,7 +12861,9 @@ "description": "adapted from cortex", "type": "object", "required": [ + "uid", "name", + "folderUid", "query", "health", "type", @@ -12891,6 +12893,9 @@ "type": "number", "format": "double" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -12930,6 +12935,9 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } } }, @@ -15392,6 +15400,14 @@ "CounterResetHint": { "$ref": "#/definitions/CounterResetHint" }, + "CustomValues": { + "description": "Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.\nThis slice is interned, to be treated as immutable and copied by reference.\nThese numbers should be strictly increasing. This field is only used when the\nschema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans\nand NegativeBuckets fields are not used in that case.", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, "PositiveBuckets": { "description": "Observation counts in buckets. Each represents an absolute count and\nmust be zero or positive.", "type": "array", @@ -15408,7 +15424,7 @@ } }, "Schema": { - "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8. They are all for\nbase-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets. Or\nin other words, each bucket boundary is the previous boundary times\n2^(2^-n).", + "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8 for exponential buckets.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in\neach case, and then each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times\n2^(2^-n). Another valid schema number is -53 for custom buckets, defined by\nthe CustomValues field.", "type": "integer", "format": "int32" }, @@ -16048,6 +16064,9 @@ "type": "string", "format": "date-time" }, + "updated_by": { + "$ref": "#/definitions/UserInfo" + }, "version": { "type": "integer", "format": "int64" @@ -16131,6 +16150,12 @@ } } }, + "GettableRuleVersions": { + "type": "array", + "items": { + "$ref": "#/definitions/GettableExtendedRuleNode" + } + }, "GettableStatus": { "type": "object", "required": [ @@ -19810,7 +19835,9 @@ "description": "adapted from cortex", "type": "object", "required": [ + "uid", "name", + "folderUid", "query", "health", "type" @@ -19820,6 +19847,9 @@ "type": "number", "format": "double" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -19841,6 +19871,9 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } } }, @@ -19873,6 +19906,7 @@ "required": [ "name", "file", + "folderUid", "rules", "interval" ], @@ -19884,6 +19918,9 @@ "file": { "type": "string" }, + "folderUid": { + "type": "string" + }, "interval": { "type": "number", "format": "double" @@ -20009,6 +20046,10 @@ "description": "Sample is a single sample belonging to a metric. It represents either a float\nsample or a histogram sample. If H is nil, it is a float sample. Otherwise,\nit is a histogram sample.", "type": "object", "properties": { + "DropName": { + "description": "DropName is used to indicate whether the __name__ label should be dropped\nas part of the query evaluation.", + "type": "boolean" + }, "F": { "type": "number", "format": "double" @@ -22032,6 +22073,18 @@ } } }, + "UserInfo": { + "type": "object", + "title": "UserInfo represents user-related information, including a unique identifier and a name.", + "properties": { + "name": { + "type": "string" + }, + "uid": { + "type": "string" + } + } + }, "UserLookupDTO": { "type": "object", "properties": { @@ -22437,6 +22490,7 @@ } }, "alertGroups": { + "description": "AlertGroups alert groups", "type": "array", "items": { "type": "object", @@ -22766,6 +22820,7 @@ } }, "gettableSilences": { + "description": "GettableSilences gettable silences", "type": "array", "items": { "type": "object", diff --git a/public/openapi3.json b/public/openapi3.json index cd2830849b6..bea198b06a2 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -2956,6 +2956,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -2995,10 +2998,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type", @@ -5465,6 +5473,14 @@ "CounterResetHint": { "$ref": "#/components/schemas/CounterResetHint" }, + "CustomValues": { + "description": "Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.\nThis slice is interned, to be treated as immutable and copied by reference.\nThese numbers should be strictly increasing. This field is only used when the\nschema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans\nand NegativeBuckets fields are not used in that case.", + "items": { + "format": "double", + "type": "number" + }, + "type": "array" + }, "PositiveBuckets": { "description": "Observation counts in buckets. Each represents an absolute count and\nmust be zero or positive.", "items": { @@ -5481,7 +5497,7 @@ "type": "array" }, "Schema": { - "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8. They are all for\nbase-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets. Or\nin other words, each bucket boundary is the previous boundary times\n2^(2^-n).", + "description": "Currently valid schema numbers are -4 \u003c= n \u003c= 8 for exponential buckets.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in\neach case, and then each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times\n2^(2^-n). Another valid schema number is -53 for custom buckets, defined by\nthe CustomValues field.", "format": "int32", "type": "integer" }, @@ -6122,6 +6138,9 @@ "format": "date-time", "type": "string" }, + "updated_by": { + "$ref": "#/components/schemas/UserInfo" + }, "version": { "format": "int64", "type": "integer" @@ -6206,6 +6225,12 @@ }, "type": "object" }, + "GettableRuleVersions": { + "items": { + "$ref": "#/components/schemas/GettableExtendedRuleNode" + }, + "type": "array" + }, "GettableStatus": { "properties": { "cluster": { @@ -9888,6 +9913,9 @@ "format": "double", "type": "number" }, + "folderUid": { + "type": "string" + }, "health": { "type": "string" }, @@ -9909,10 +9937,15 @@ }, "type": { "type": "string" + }, + "uid": { + "type": "string" } }, "required": [ + "uid", "name", + "folderUid", "query", "health", "type" @@ -9952,6 +9985,9 @@ "file": { "type": "string" }, + "folderUid": { + "type": "string" + }, "interval": { "format": "double", "type": "number" @@ -9981,6 +10017,7 @@ "required": [ "name", "file", + "folderUid", "rules", "interval" ], @@ -10083,6 +10120,10 @@ "Sample": { "description": "Sample is a single sample belonging to a metric. It represents either a float\nsample or a histogram sample. If H is nil, it is a float sample. Otherwise,\nit is a histogram sample.", "properties": { + "DropName": { + "description": "DropName is used to indicate whether the __name__ label should be dropped\nas part of the query evaluation.", + "type": "boolean" + }, "F": { "format": "double", "type": "number" @@ -12106,6 +12147,18 @@ }, "type": "object" }, + "UserInfo": { + "properties": { + "name": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "title": "UserInfo represents user-related information, including a unique identifier and a name.", + "type": "object" + }, "UserLookupDTO": { "properties": { "avatarUrl": { @@ -12511,6 +12564,7 @@ "type": "object" }, "alertGroups": { + "description": "AlertGroups alert groups", "items": { "$ref": "#/components/schemas/alertGroup" }, @@ -12838,6 +12892,7 @@ "type": "object" }, "gettableSilences": { + "description": "GettableSilences gettable silences", "items": { "$ref": "#/components/schemas/gettableSilence" }, From 29fa6dfc8de0a758f9f37f000c923f0806c3b629 Mon Sep 17 00:00:00 2001 From: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:46:54 -0500 Subject: [PATCH 04/12] ExploreMetrics: Add toggle to enable routing to externalized Explore Metrics app plugin (#99481) * feat: toggle for explore metrics app plugin * chore: put app navigation settings behind feature toggle * chore: require restart * feat: context-driven applinks toggling * fix: toggle ownership * docs: note restart requirement * refactor: prefer global feature toggle check * chore: undo rm newlines * fix: minimize changes * fix: app id, `chromeless`, sidebar ordering --- .../src/types/featureToggles.gen.ts | 1 + pkg/services/featuremgmt/registry.go | 11 ++++- pkg/services/featuremgmt/toggles_gen.csv | 3 +- pkg/services/featuremgmt/toggles_gen.go | 4 ++ pkg/services/featuremgmt/toggles_gen.json | 24 +++++++-- pkg/services/navtree/navtreeimpl/applinks.go | 10 ++-- pkg/services/navtree/navtreeimpl/navtree.go | 2 +- public/app/features/plugins/routes.tsx | 49 +++++++++++++------ public/app/routes/routes.tsx | 16 ++++-- 9 files changed, 91 insertions(+), 29 deletions(-) diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 24f2244ac83..71fa78c0d67 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -251,6 +251,7 @@ export interface FeatureToggles { queryLibraryDashboards?: boolean; grafanaAdvisor?: boolean; elasticsearchImprovedParsing?: boolean; + exploreMetricsUseExternalAppPlugin?: boolean; datasourceConnectionsTab?: boolean; fetchRulesUsingPost?: boolean; alertingAlertmanagerExtraDedupStage?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 7571ff6a37f..3e737bb1f45 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -912,7 +912,7 @@ var ( Stage: FeatureStageGeneralAvailability, Expression: "true", // enabled by default FrontendOnly: true, - Owner: grafanaDashboardsSquad, + Owner: grafanaObservabilityMetricsSquad, }, { Name: "alertingSimplifiedRouting", @@ -1742,6 +1742,15 @@ var ( Stage: FeatureStageExperimental, Owner: awsDatasourcesSquad, }, + { + Name: "exploreMetricsUseExternalAppPlugin", + Description: "Use the externalized Metrics Drilldown (formerly known as Explore Metrics) app plugin", + Stage: FeatureStageExperimental, + Owner: grafanaObservabilityMetricsSquad, + FrontendOnly: true, + RequiresRestart: true, + HideFromDocs: true, + }, { Name: "datasourceConnectionsTab", Description: "Shows defined connections for a data source in the plugins detail page", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 250833a78a3..3b653f76b4e 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -120,7 +120,7 @@ pdfTables,preview,@grafana/sharing-squad,false,false,false ssoSettingsApi,GA,@grafana/identity-access-team,false,false,false canvasPanelPanZoom,preview,@grafana/dataviz-squad,false,false,true logsInfiniteScrolling,GA,@grafana/observability-logs,false,false,true -exploreMetrics,GA,@grafana/dashboards-squad,false,false,true +exploreMetrics,GA,@grafana/observability-metrics,false,false,true alertingSimplifiedRouting,GA,@grafana/alerting-squad,false,false,false logRowsPopoverMenu,GA,@grafana/observability-logs,false,false,true pluginsSkipHostEnvVars,experimental,@grafana/plugins-platform-backend,false,false,false @@ -232,6 +232,7 @@ ABTestFeatureToggleB,experimental,@grafana/sharing-squad,false,false,false queryLibraryDashboards,experimental,@grafana/grafana-frontend-platform,false,false,false grafanaAdvisor,experimental,@grafana/plugins-platform-backend,false,false,false elasticsearchImprovedParsing,experimental,@grafana/aws-datasources,false,false,false +exploreMetricsUseExternalAppPlugin,experimental,@grafana/observability-metrics,false,true,true datasourceConnectionsTab,experimental,@grafana/plugins-platform-backend,false,false,true fetchRulesUsingPost,experimental,@grafana/alerting-squad,false,false,false alertingAlertmanagerExtraDedupStage,experimental,@grafana/alerting-squad,false,true,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 556275ed908..3a5db419907 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -939,6 +939,10 @@ const ( // Enables less memory intensive Elasticsearch result parsing FlagElasticsearchImprovedParsing = "elasticsearchImprovedParsing" + // FlagExploreMetricsUseExternalAppPlugin + // Use the externalized Metrics Drilldown (formerly known as Explore Metrics) app plugin + FlagExploreMetricsUseExternalAppPlugin = "exploreMetricsUseExternalAppPlugin" + // FlagDatasourceConnectionsTab // Shows defined connections for a data source in the plugins detail page FlagDatasourceConnectionsTab = "datasourceConnectionsTab" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 573ae344f02..e39d9ef150e 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1529,16 +1529,16 @@ { "metadata": { "name": "exploreMetrics", - "resourceVersion": "1720021873452", + "resourceVersion": "1737658563230", "creationTimestamp": "2024-04-09T18:15:18Z", "annotations": { - "grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC" + "grafana.app/updatedTimestamp": "2025-01-23 18:56:03.23086 +0000 UTC" } }, "spec": { "description": "Enables the new Explore Metrics core app", "stage": "GA", - "codeowner": "@grafana/dashboards-squad", + "codeowner": "@grafana/observability-metrics", "frontend": true, "expression": "true" } @@ -1557,6 +1557,24 @@ "hideFromDocs": true } }, + { + "metadata": { + "name": "exploreMetricsUseExternalAppPlugin", + "resourceVersion": "1738596266973", + "creationTimestamp": "2025-01-21T23:24:50Z", + "annotations": { + "grafana.app/updatedTimestamp": "2025-02-03 15:24:26.973231 +0000 UTC" + } + }, + "spec": { + "description": "Use the externalized Metrics Drilldown (formerly known as Explore Metrics) app plugin", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "requiresRestart": true, + "hideFromDocs": true + } + }, { "metadata": { "name": "expressionParser", diff --git a/pkg/services/navtree/navtreeimpl/applinks.go b/pkg/services/navtree/navtreeimpl/applinks.go index 8fc88a5ed80..cf78f67e1d4 100644 --- a/pkg/services/navtree/navtreeimpl/applinks.go +++ b/pkg/services/navtree/navtreeimpl/applinks.go @@ -286,9 +286,9 @@ func (s *ServiceImpl) readNavigationSettings() { "grafana-k8s-app": {SectionID: navtree.NavIDInfrastructure, SortWeight: 1, Text: "Kubernetes"}, "grafana-dbo11y-app": {SectionID: navtree.NavIDInfrastructure, SortWeight: 2, Text: "Databases"}, "grafana-app-observability-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightApplication, Text: "Application", Icon: "graph-bar"}, - "grafana-lokiexplore-app": {SectionID: navtree.NavIDExplore, SortWeight: 1, Text: "Logs"}, - "grafana-exploretraces-app": {SectionID: navtree.NavIDExplore, SortWeight: 2, Text: "Traces"}, - "grafana-pyroscope-app": {SectionID: navtree.NavIDExplore, SortWeight: 3, Text: "Profiles"}, + "grafana-lokiexplore-app": {SectionID: navtree.NavIDExplore, SortWeight: 2, Text: "Logs"}, + "grafana-exploretraces-app": {SectionID: navtree.NavIDExplore, SortWeight: 3, Text: "Traces"}, + "grafana-pyroscope-app": {SectionID: navtree.NavIDExplore, SortWeight: 4, Text: "Profiles"}, "grafana-kowalski-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightFrontend, Text: "Frontend", Icon: "frontend-observability"}, "grafana-synthetic-monitoring-app": {SectionID: navtree.NavIDTestingAndSynthetics, SortWeight: 2, Text: "Synthetics"}, "grafana-irm-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 1, Text: "IRM"}, @@ -309,6 +309,10 @@ func (s *ServiceImpl) readNavigationSettings() { "grafana-csp-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightCloudServiceProviders, Icon: "cloud"}, } + if s.features.IsEnabledGlobally(featuremgmt.FlagExploreMetricsUseExternalAppPlugin) { + s.navigationAppConfig["grafana-metricsdrilldown-app"] = NavigationAppConfig{SectionID: navtree.NavIDExplore, SortWeight: 1, Text: "Metrics"} + } + s.navigationAppPathConfig = map[string]NavigationAppConfig{ "/a/grafana-auth-app": {SectionID: navtree.NavIDCfgAccess, SortWeight: 2}, } diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index c67ee1bbfe8..1f461db67f3 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -562,7 +562,7 @@ func (s *ServiceImpl) buildDataConnectionsNavLink(c *contextmodel.ReqContext) *n func (s *ServiceImpl) buildExploreNavLinks(c *contextmodel.ReqContext) []*navtree.NavLink { exploreChildNavs := []*navtree.NavLink{} - if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagExploreMetrics) { + if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagExploreMetrics) && !s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagExploreMetricsUseExternalAppPlugin) { exploreChildNavs = append(exploreChildNavs, &navtree.NavLink{ Text: "Metrics", SubTitle: "Queryless exploration of your metrics", diff --git a/public/app/features/plugins/routes.tsx b/public/app/features/plugins/routes.tsx index 46cc470549d..1504f6e2e48 100644 --- a/public/app/features/plugins/routes.tsx +++ b/public/app/features/plugins/routes.tsx @@ -4,26 +4,28 @@ import { getRootSectionForNode } from 'app/core/selectors/navModel'; import AppRootPage from 'app/features/plugins/components/AppRootPage'; import { getState } from 'app/store/store'; +const isPluginNavModelItem = (model: NavModelItem): model is PluginNavModelItem => 'pluginId' in model && 'id' in model; +const isStandalonePluginPage = (id: string) => id.startsWith('standalone-plugin-page-/'); + +function getPathForNavItem(navItem: PluginNavModelItem) { + const pluginNavSection = getRootSectionForNode(navItem); + const appPluginUrl = `/a/${navItem.pluginId}`; + const path = isStandalonePluginPage(navItem.id) ? navItem.url || appPluginUrl : appPluginUrl; // Only standalone pages can use core URLs, otherwise we fall back to "/a/:pluginId" + const isSensitive = isStandalonePluginPage(navItem.id) && !navItem.url?.startsWith('/a/'); // Have case-sensitive URLs only for standalone pages that have custom URLs + + return { + path: `${path}/*`, + sensitive: isSensitive, + component: () => , + }; +} + export function getAppPluginRoutes(): RouteDescriptor[] { const state = getState(); const { navIndex } = state; - const isStandalonePluginPage = (id: string) => id.startsWith('standalone-plugin-page-/'); - const isPluginNavModelItem = (model: NavModelItem): model is PluginNavModelItem => - 'pluginId' in model && 'id' in model; const explicitAppPluginRoutes = Object.values(navIndex) .filter(isPluginNavModelItem) - .map((navItem) => { - const pluginNavSection = getRootSectionForNode(navItem); - const appPluginUrl = `/a/${navItem.pluginId}`; - const path = isStandalonePluginPage(navItem.id) ? navItem.url || appPluginUrl : appPluginUrl; // Only standalone pages can use core URLs, otherwise we fall back to "/a/:pluginId" - const isSensitive = isStandalonePluginPage(navItem.id) && !navItem.url?.startsWith('/a/'); // Have case-sensitive URLs only for standalone pages that have custom URLs - - return { - path: `${path}/*`, - sensitive: isSensitive, - component: () => , - }; - }); + .map(getPathForNavItem); return [ ...explicitAppPluginRoutes, @@ -36,6 +38,23 @@ export function getAppPluginRoutes(): RouteDescriptor[] { ]; } +export function getRouteForAppPlugin(pluginId: string): RouteDescriptor { + const state = getState(); + const { navIndex } = state; + const navItem = Object.values(navIndex) + .filter(isPluginNavModelItem) + .find((navItem) => navItem.pluginId === pluginId); + + if (!navItem) { + return { + path: '/a/:pluginId/*', + component: () => , + }; + } + + return getPathForNavItem(navItem); +} + interface PluginNavModelItem extends Omit { pluginId: string; id: string; diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx index 8a59242f820..ddadc2f40b7 100644 --- a/public/app/routes/routes.tsx +++ b/public/app/routes/routes.tsx @@ -15,7 +15,7 @@ import { getRoutes as getDataConnectionsRoutes } from 'app/features/connections/ import { DATASOURCES_ROUTES } from 'app/features/datasources/constants'; import { ConfigureIRM } from 'app/features/gops/configuration-tracker/components/ConfigureIRM'; import { getRoutes as getPluginCatalogRoutes } from 'app/features/plugins/admin/routes'; -import { getAppPluginRoutes } from 'app/features/plugins/routes'; +import { getAppPluginRoutes, getRouteForAppPlugin } from 'app/features/plugins/routes'; import { getProfileRoutes } from 'app/features/profile/routes'; import { AccessControlAction, DashboardRoutes } from 'app/types'; @@ -515,11 +515,17 @@ export function getAppRoutes(): RouteDescriptor[] { }, config.featureToggles.exploreMetrics && { path: '/explore/metrics/*', - chromeless: false, roles: () => contextSrv.evaluatePermission([AccessControlAction.DataSourcesExplore]), - component: SafeDynamicImport( - () => import(/* webpackChunkName: "DataTrailsPage"*/ 'app/features/trails/DataTrailsPage') - ), + ...(config.featureToggles.exploreMetricsUseExternalAppPlugin + ? { + component: getRouteForAppPlugin('grafana-metricsdrilldown-app').component, + } + : { + chromeless: false, + component: SafeDynamicImport( + () => import(/* webpackChunkName: "DataTrailsPage"*/ 'app/features/trails/DataTrailsPage') + ), + }), }, { path: '/bookmarks', From 9677f4b692340c8ba310794c55725a492c2408d0 Mon Sep 17 00:00:00 2001 From: Scott Lepper Date: Mon, 3 Feb 2025 15:55:22 -0500 Subject: [PATCH 05/12] [search] fix folder sync (#99913) [search] fix folder sync --- .../features/search/service/unified.test.ts | 139 +++++++++++++++++- public/app/features/search/service/unified.ts | 45 ++++-- 2 files changed, 174 insertions(+), 10 deletions(-) diff --git a/public/app/features/search/service/unified.test.ts b/public/app/features/search/service/unified.test.ts index 4d50e25193b..e62a126f38f 100644 --- a/public/app/features/search/service/unified.test.ts +++ b/public/app/features/search/service/unified.test.ts @@ -1,6 +1,143 @@ -import { toDashboardResults, SearchHit, SearchAPIResponse } from './unified'; +import { BackendSrv } from '@grafana/runtime'; + +import { GrafanaSearcher, SearchQuery } from './types'; +import { toDashboardResults, SearchHit, SearchAPIResponse, UnifiedSearcher } from './unified'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +const mockResults: SearchAPIResponse = { + hits: [], + totalHits: 0, +}; + +const mockFolders: SearchAPIResponse = { + hits: [], + totalHits: 0, +}; + +const mockFallbackSearcher = { + search: jest.fn(), +} as unknown as GrafanaSearcher; + +const getResponse = (uri: string) => { + if (uri.endsWith('?type=folders')) { + return Promise.resolve(mockFolders); + } + return Promise.resolve(mockResults); +}; + +const mockSearcher = { + search: (uri: string) => getResponse(uri), +}; + +jest.mock('@grafana/runtime', () => { + const originalRuntime = jest.requireActual('@grafana/runtime'); + return { + ...originalRuntime, + getBackendSrv: () => + ({ + get: (uri: string) => mockSearcher.search(uri), + }) as unknown as BackendSrv, + }; +}); describe('Unified Storage Searcher', () => { + it('should perform search with basic query', async () => { + mockFolders.hits = [ + { + name: 'folder1', + title: 'Folder 1', + resource: 'folders', + } as SearchHit, + ]; + mockResults.hits = [ + { + name: 'dashboard1', + title: 'Dashboard 1', + resource: 'dashboards', + folder: 'folder1', + } as SearchHit, + ]; + + const query: SearchQuery = { + query: 'test', + limit: 50, + }; + + const searcher = new UnifiedSearcher(mockFallbackSearcher); + + const response = await searcher.search(query); + + expect(response.view.length).toBe(1); + expect(response.view.get(0).title).toBe('Dashboard 1'); + + const df = response.view.dataFrame; + const locationInfo = df.meta?.custom?.locationInfo; + expect(locationInfo).toBeDefined(); + expect(locationInfo?.folder1.name).toBe('Folder 1'); + }); + + it('should perform search and sync folders with missing folder', async () => { + const mockFolders = { + hits: [ + { + name: 'folder2', + title: 'Folder 2', + resource: 'folders', + } as SearchHit, + ], + totalHits: 1, + }; + + const mockResults = { + hits: [ + { + name: 'db1', + title: 'DB 1', + resource: 'dashboards', + folder: 'folder1', + } as SearchHit, + { + name: 'db2', + title: 'DB 2', + resource: 'dashboards', + folder: 'folder2', + } as SearchHit, + ], + totalHits: 2, + }; + + jest + .spyOn(mockSearcher, 'search') + .mockResolvedValueOnce(mockFolders) + .mockResolvedValueOnce(mockResults) + .mockResolvedValueOnce(mockFolders); + + const consoleWarn = jest.fn(); + jest.spyOn(console, 'warn').mockImplementationOnce(consoleWarn); + + const query: SearchQuery = { + query: 'test', + limit: 50, + }; + + const searcher = new UnifiedSearcher(mockFallbackSearcher); + + const response = await searcher.search(query); + + expect(response.view.length).toBe(1); + expect(response.view.get(0).title).toBe('DB 2'); + + const df = response.view.dataFrame; + const locationInfo = df.meta?.custom?.locationInfo; + expect(locationInfo).toBeDefined(); + expect(locationInfo?.folder2.name).toBe('Folder 2'); + expect(consoleWarn).toHaveBeenCalled(); + expect(mockSearcher.search).toHaveBeenCalledTimes(3); + }); + it('can create dashboard search results and set meta sortBy so column is added for sprinkles sort field', () => { const mockHits: SearchHit[] = [ { diff --git a/public/app/features/search/service/unified.ts b/public/app/features/search/service/unified.ts index 700cf566314..9d942f668d3 100644 --- a/public/app/features/search/service/unified.ts +++ b/public/app/features/search/service/unified.ts @@ -112,7 +112,7 @@ export class UnifiedSearcher implements GrafanaSearcher { async doSearchQuery(query: SearchQuery): Promise { const uri = await this.newRequest(query); - const rsp = await getBackendSrv().get(uri); + const rsp = await this.fetchResponse(uri); const first = toDashboardResults(rsp, query.sort ?? ''); if (first.name === loadingFrameName) { @@ -120,12 +120,6 @@ export class UnifiedSearcher implements GrafanaSearcher { } const meta = first.meta?.custom || ({} as SearchResultMeta); - const locationInfo = await this.locationInfo; - const hasMissing = rsp.hits.some((hit) => !locationInfo[hit.folder]); - if (hasMissing) { - // sync the location info ( folders ) - this.locationInfo = loadLocationInfo(); - } meta.locationInfo = await this.locationInfo; // Set the field name to a better display name @@ -141,14 +135,13 @@ export class UnifiedSearcher implements GrafanaSearcher { let loadMax = 0; let pending: Promise | undefined = undefined; const getNextPage = async () => { - // TODO: implement this correctly while (loadMax > view.dataFrame.length) { const offset = view.dataFrame.length; if (offset >= meta.count) { return; } const nextPageUrl = `${uri}&offset=${offset}`; - const resp = await getBackendSrv().get(nextPageUrl); + const resp = await this.fetchResponse(nextPageUrl); const frame = toDashboardResults(resp, query.sort ?? ''); if (!frame) { console.log('no results', frame); @@ -198,6 +191,40 @@ export class UnifiedSearcher implements GrafanaSearcher { }; } + async fetchResponse(uri: string) { + const rsp = await getBackendSrv().get(uri); + const isFolderCacheStale = await this.isFolderCacheStale(rsp.hits); + if (!isFolderCacheStale) { + return rsp; + } + // sync the location info ( folders ) + this.locationInfo = loadLocationInfo(); + // recheck for missing folders + const hasMissing = await this.isFolderCacheStale(rsp.hits); + if (!hasMissing) { + return rsp; + } + // we still have results here with folders we can't find + // filter the results since we probably don't have access to that folder + const locationInfo = await this.locationInfo; + const hits = rsp.hits.filter((hit) => { + if (hit.folder === undefined || locationInfo[hit.folder] !== undefined) { + return true; + } + console.warn('Dropping search hit with missing folder', hit); + return false; + }); + const totalHits = rsp.totalHits - (rsp.hits.length - hits.length); + return { ...rsp, hits, totalHits }; + } + + async isFolderCacheStale(hits: SearchHit[]): Promise { + const locationInfo = await this.locationInfo; + return hits.some((hit) => { + return hit.folder !== undefined && locationInfo[hit.folder] === undefined; + }); + } + private async newRequest(query: SearchQuery): Promise { query = await replaceCurrentFolderQuery(query); From 5fe6479a10a30d167999e8674ac18c82ee17ccad Mon Sep 17 00:00:00 2001 From: Pepe Cano <825430+ppcano@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:01:18 +0100 Subject: [PATCH 06/12] Alerting docs: update screenshot (modify NoData/Error state) (#99996) --- .../fundamentals/alert-rule-evaluation/state-and-health.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md index cb4f932a5f6..bcfc5b27769 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md @@ -87,7 +87,7 @@ In [Configure no data and error handling](ref:no-data-and-error-handling), you c Note that `No Data` and `Error` states are supported only for Grafana-managed alert rules. -{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} +{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} {{< docs/shared lookup="alerts/table-configure-no-data-and-error.md" source="grafana" version="" >}} From 46af49f907675b8f694a91f8d455af5bdb45b7d8 Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:06:29 -0700 Subject: [PATCH 07/12] Chore: Update alpine build docker image (#99998) baldm0mma/ update alpine build image --- .drone.yml | 82 ++++++++++++++++----------------- scripts/drone/utils/images.star | 2 +- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/.drone.yml b/.drone.yml index f81d8bbbc69..224e42dc60c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,7 +18,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -69,7 +69,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - go install github.com/bazelbuild/buildtools/buildifier@latest @@ -112,7 +112,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -170,7 +170,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -309,7 +309,7 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -417,7 +417,7 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -506,7 +506,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -630,7 +630,7 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - mkdir -p bin @@ -699,7 +699,7 @@ steps: -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.5 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 + --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.5 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt @@ -756,7 +756,7 @@ steps: GF_APP_MODE: development GF_SERVER_HTTP_PORT: "3001" GF_SERVER_ROUTER_LOGGING: "1" - image: alpine:3.20.3 + image: alpine:3.20.5 name: grafana-server - commands: - ./bin/build e2e-tests --port 3001 --suite dashboards-suite @@ -1100,7 +1100,7 @@ steps: name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1266,7 +1266,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1623,7 +1623,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1694,7 +1694,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1752,7 +1752,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -1818,7 +1818,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1898,7 +1898,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -1964,7 +1964,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -2039,7 +2039,7 @@ steps: path: /github-app - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - mkdir -p bin @@ -2107,7 +2107,7 @@ steps: -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.5 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 + --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.5 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt @@ -2168,7 +2168,7 @@ steps: GF_APP_MODE: development GF_SERVER_HTTP_PORT: "3001" GF_SERVER_ROUTER_LOGGING: "1" - image: alpine:3.20.3 + image: alpine:3.20.5 name: grafana-server - commands: - ./bin/build e2e-tests --port 3001 --suite dashboards-suite @@ -2585,7 +2585,7 @@ steps: name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -2830,7 +2830,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -2886,7 +2886,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -2950,7 +2950,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -3028,7 +3028,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -3133,7 +3133,7 @@ steps: name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -3336,7 +3336,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - mkdir -p bin @@ -3468,7 +3468,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - mkdir -p bin @@ -3958,7 +3958,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.20.5 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4033,7 +4033,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.20.5 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4195,7 +4195,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.20.5 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4297,7 +4297,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - yarn install --immutable || yarn install --immutable @@ -4351,7 +4351,7 @@ services: [] steps: - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -4432,7 +4432,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.20.5 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4576,7 +4576,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.20.5 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4704,7 +4704,7 @@ steps: environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 + ALPINE_BASE: alpine:3.20.5 CDN_DESTINATION: from_secret: rgm_cdn_destination DESTINATION: @@ -4843,7 +4843,7 @@ steps: name: grabpl - commands: - echo $DRONE_RUNNER_NAME - image: alpine:3.20.3 + image: alpine:3.20.5 name: identify-runner - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -5267,7 +5267,7 @@ steps: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-deploy:1.3.3 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.20.3 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.20.5 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM ubuntu:22.04 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM byrnedo/alpine-curl:0.1.8 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack @@ -5305,7 +5305,7 @@ steps: - trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-deploy:1.3.3 - - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.20.3 + - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.20.5 - trivy --exit-code 1 --severity HIGH,CRITICAL ubuntu:22.04 - trivy --exit-code 1 --severity HIGH,CRITICAL byrnedo/alpine-curl:0.1.8 - trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack @@ -5569,6 +5569,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: 154c1f072294cef09ba4f579eed30b1e29bc5c263897c862f4574432d321502c +hmac: b7abfae4fe14ca97a65bc5869984d81407f61a66fd06301fb2023aab07238a5b ... diff --git a/scripts/drone/utils/images.star b/scripts/drone/utils/images.star index c0809849811..9e0575f86f3 100644 --- a/scripts/drone/utils/images.star +++ b/scripts/drone/utils/images.star @@ -16,7 +16,7 @@ images = { "node_deb": "node:{}-bookworm".format(nodejs_version[:2]), "cloudsdk": "google/cloud-sdk:431.0.0", "publish": "grafana/grafana-ci-deploy:1.3.3", - "alpine": "alpine:3.20.3", + "alpine": "alpine:3.20.5", "ubuntu": "ubuntu:22.04", "curl": "byrnedo/alpine-curl:0.1.8", "plugins_slack": "plugins/slack", From 88c62cd8287a14768262c01718fe6544a41f2e7b Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:12:05 -0700 Subject: [PATCH 08/12] Chore: Manually update versions and update `grafana-test-datasource` to "workspace:*" (#99999) * baldm0mma/ manually update deps and run yarn * baldm0mma/ update version from workspace to actual version * baldm0mma/ revert some workspaces * baldm0mma/ update workspace * baldm0mma/ update deps --- .../grafana-extensionstest-app/package.json | 2 +- .../grafana-test-datasource/package.json | 4 +- lerna.json | 2 +- package.json | 2 +- packages/grafana-data/package.json | 4 +- packages/grafana-e2e-selectors/package.json | 2 +- packages/grafana-eslint-rules/package.json | 2 +- packages/grafana-flamegraph/package.json | 6 +- packages/grafana-icons/package.json | 2 +- .../grafana-o11y-ds-frontend/package.json | 12 +- packages/grafana-plugin-configs/package.json | 2 +- packages/grafana-prometheus/package.json | 12 +- packages/grafana-runtime/package.json | 10 +- packages/grafana-schema/package.json | 2 +- .../x/AnnotationsListPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/BarChartPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/BarGaugePanelCfg_types.gen.ts | 2 +- .../x/CandlestickPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/CanvasPanelCfg_types.gen.ts | 2 +- .../x/CloudWatchDataQuery_types.gen.ts | 2 +- .../x/DashboardListPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/DatagridPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/DebugPanelCfg_types.gen.ts | 2 +- .../x/ElasticsearchDataQuery_types.gen.ts | 2 +- .../panelcfg/x/GaugePanelCfg_types.gen.ts | 2 +- .../panelcfg/x/GeomapPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/HeatmapPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/HistogramPanelCfg_types.gen.ts | 2 +- .../logs/panelcfg/x/LogsPanelCfg_types.gen.ts | 2 +- .../dataquery/x/LokiDataQuery_types.gen.ts | 2 +- .../news/panelcfg/x/NewsPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/NodeGraphPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/PieChartPanelCfg_types.gen.ts | 2 +- .../stat/panelcfg/x/StatPanelCfg_types.gen.ts | 2 +- .../x/StateTimelinePanelCfg_types.gen.ts | 2 +- .../x/StatusHistoryPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/TablePanelCfg_types.gen.ts | 2 +- .../text/panelcfg/x/TextPanelCfg_types.gen.ts | 2 +- .../x/TimeSeriesPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/TrendPanelCfg_types.gen.ts | 2 +- .../panelcfg/x/XYChartPanelCfg_types.gen.ts | 2 +- packages/grafana-sql/package.json | 10 +- packages/grafana-ui/package.json | 8 +- .../datasource/azuremonitor/package.json | 14 +- .../datasource/cloud-monitoring/package.json | 14 +- .../package.json | 14 +- .../grafana-pyroscope-datasource/package.json | 12 +- .../grafana-testdata-datasource/package.json | 14 +- .../plugins/datasource/jaeger/package.json | 2 +- .../app/plugins/datasource/mssql/package.json | 14 +- .../app/plugins/datasource/mysql/package.json | 14 +- .../app/plugins/datasource/parca/package.json | 12 +- .../app/plugins/datasource/tempo/package.json | 4 +- .../plugins/datasource/zipkin/package.json | 2 +- yarn.lock | 158 +++++++++--------- 55 files changed, 205 insertions(+), 205 deletions(-) diff --git a/e2e/test-plugins/grafana-extensionstest-app/package.json b/e2e/test-plugins/grafana-extensionstest-app/package.json index d100164ce4a..0bef81cbf48 100644 --- a/e2e/test-plugins/grafana-extensionstest-app/package.json +++ b/e2e/test-plugins/grafana-extensionstest-app/package.json @@ -1,6 +1,6 @@ { "name": "@test-plugins/extensions-test-app", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "private": true, "scripts": { "build": "webpack -c ./webpack.config.ts --env production", diff --git a/e2e/test-plugins/grafana-test-datasource/package.json b/e2e/test-plugins/grafana-test-datasource/package.json index 0eff2b9f271..e4472e7a923 100644 --- a/e2e/test-plugins/grafana-test-datasource/package.json +++ b/e2e/test-plugins/grafana-test-datasource/package.json @@ -1,6 +1,6 @@ { "name": "@test-plugins/grafana-e2etest-datasource", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "private": true, "scripts": { "build": "webpack -c ./webpack.config.ts --env production", @@ -11,7 +11,7 @@ "author": "Grafana", "license": "Apache-2.0", "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "workspace:*", "@types/lodash": "4.17.7", "@types/node": "22.10.2", "@types/prismjs": "1.26.4", diff --git a/lerna.json b/lerna.json index 1f9cd412ea5..f39bf8763c4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "npmClient": "yarn", - "version": "11.5.0-pre" + "version": "11.6.0-pre" } diff --git a/package.json b/package.json index 25ef488c57d..682378efec3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "private": true, "name": "grafana", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "repository": "github:grafana/grafana", "scripts": { "build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js --progress", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 383401eb4a7..2f118f5c4bf 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/data", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Data Library", "keywords": [ "typescript" @@ -36,7 +36,7 @@ }, "dependencies": { "@braintree/sanitize-url": "7.0.1", - "@grafana/schema": "11.5.0-pre", + "@grafana/schema": "11.6.0-pre", "@types/d3-interpolate": "^3.0.0", "@types/string-hash": "1.1.3", "d3-interpolate": "3.0.1", diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index 0c797232400..261f8d89a18 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/e2e-selectors", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana End-to-End Test Selectors Library", "keywords": [ "cli", diff --git a/packages/grafana-eslint-rules/package.json b/packages/grafana-eslint-rules/package.json index 7e0c87a6117..fab662fb0ac 100644 --- a/packages/grafana-eslint-rules/package.json +++ b/packages/grafana-eslint-rules/package.json @@ -1,7 +1,7 @@ { "name": "@grafana/eslint-plugin", "description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "main": "./index.cjs", "author": "Grafana Labs", "license": "Apache-2.0", diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index fd9c092b5e7..b45e8137f03 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/flamegraph", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana flamegraph visualization component", "keywords": [ "grafana", @@ -44,8 +44,8 @@ ], "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "@leeoniya/ufuzzy": "1.0.18", "d3": "^7.8.5", "lodash": "4.17.21", diff --git a/packages/grafana-icons/package.json b/packages/grafana-icons/package.json index a39c28777a5..f5f41fdae68 100644 --- a/packages/grafana-icons/package.json +++ b/packages/grafana-icons/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/saga-icons", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "private": true, "description": "Icons for Grafana", "author": "Grafana Labs", diff --git a/packages/grafana-o11y-ds-frontend/package.json b/packages/grafana-o11y-ds-frontend/package.json index 903b3d61b26..9bf98a363fc 100644 --- a/packages/grafana-o11y-ds-frontend/package.json +++ b/packages/grafana-o11y-ds-frontend/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "name": "@grafana/o11y-ds-frontend", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Library to manage traces in Grafana.", "sideEffects": false, "repository": { @@ -18,12 +18,12 @@ }, "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/e2e-selectors": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "react-select": "5.10.0", "react-use": "17.6.0", "rxjs": "7.8.1", diff --git a/packages/grafana-plugin-configs/package.json b/packages/grafana-plugin-configs/package.json index af69b3119a2..384e4c2d42c 100644 --- a/packages/grafana-plugin-configs/package.json +++ b/packages/grafana-plugin-configs/package.json @@ -2,7 +2,7 @@ "name": "@grafana/plugin-configs", "description": "Shared dependencies and files for core plugins", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "tslib": "2.8.1" }, diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 253b495cc13..6a0b4e81593 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "AGPL-3.0-only", "name": "@grafana/prometheus", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Prometheus Library", "keywords": [ "typescript" @@ -38,13 +38,13 @@ "dependencies": { "@emotion/css": "11.13.5", "@floating-ui/react": "0.27.3", - "@grafana/data": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", "@grafana/faro-web-sdk": "1.12.3", "@grafana/llm": "0.12.0", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "@hello-pangea/dnd": "17.0.0", "@leeoniya/ufuzzy": "1.0.18", "@lezer/common": "1.2.3", @@ -77,7 +77,7 @@ }, "devDependencies": { "@emotion/eslint-plugin": "11.12.0", - "@grafana/e2e-selectors": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-image": "3.0.3", "@rollup/plugin-node-resolve": "16.0.0", diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 21c58965847..085b10800f2 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/runtime", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Runtime Library", "keywords": [ "grafana", @@ -37,11 +37,11 @@ "postpack": "mv package.json.bak package.json" }, "dependencies": { - "@grafana/data": "11.5.0-pre", - "@grafana/e2e-selectors": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", "@grafana/faro-web-sdk": "^1.3.6", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "history": "4.10.1", "lodash": "4.17.21", "react-use": "17.6.0", diff --git a/packages/grafana-schema/package.json b/packages/grafana-schema/package.json index 377ec3e5e0e..ad5f1622b94 100644 --- a/packages/grafana-schema/package.json +++ b/packages/grafana-schema/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/schema", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Schema Library", "keywords": [ "typescript" diff --git a/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts index 4d0873971d3..fb399d8d9af 100644 --- a/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/annotationslist/panelcfg/x/AnnotationsListPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { limit: number; diff --git a/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts index d74653e61de..ef9b15eb259 100644 --- a/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/barchart/panelcfg/x/BarChartPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip, common.OptionsWithTextFormatting { /** diff --git a/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts index 7dd762efd5c..6eeabf6c4da 100644 --- a/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/bargauge/panelcfg/x/BarGaugePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends common.OptionsWithLegend, common.SingleStatBaseOptions { displayMode: common.BarGaugeDisplayMode; diff --git a/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts index 16a8a0a66ef..51ffd29995a 100644 --- a/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/candlestick/panelcfg/x/CandlestickPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export enum VizDisplayMode { Candles = 'candles', diff --git a/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts index 2f568510e81..7172fd336ec 100644 --- a/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/canvas/panelcfg/x/CanvasPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export enum HorizontalConstraint { Center = 'center', diff --git a/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts index 9c1ad367642..e6b8fc13cd0 100644 --- a/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/cloudwatch/dataquery/x/CloudWatchDataQuery_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface MetricStat { /** diff --git a/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts index cc1a99926c8..376cd0293d6 100644 --- a/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/dashboardlist/panelcfg/x/DashboardListPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { /** diff --git a/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts index 5aeff1f864a..ffc4365a8ae 100644 --- a/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/datagrid/panelcfg/x/DatagridPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { selectedSeries: number; diff --git a/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts index 08a629c2a1a..d2119d55213 100644 --- a/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/debug/panelcfg/x/DebugPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export type UpdateConfig = { render: boolean, diff --git a/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts index 5766281d1e9..d11da7e9b28 100644 --- a/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/elasticsearch/dataquery/x/ElasticsearchDataQuery_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested); diff --git a/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts index 615ce43f8ee..f1a444fd18b 100644 --- a/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/gauge/panelcfg/x/GaugePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends common.SingleStatBaseOptions { minVizHeight: number; diff --git a/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts index 2a879ed44d7..f1960dd27a4 100644 --- a/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/geomap/panelcfg/x/GeomapPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { basemap: ui.MapLayerOptions; diff --git a/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts index 7aac8ca0adb..96ef75f8618 100644 --- a/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; /** * Controls the color mode of the heatmap diff --git a/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts index 1b93bffd74c..91e402b587f 100644 --- a/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/histogram/panelcfg/x/HistogramPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip { /** diff --git a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts index ca356143560..a2c30353a8e 100644 --- a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { dedupStrategy: common.LogsDedupStrategy; diff --git a/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts index 3d7b48dfe8f..e5e064c929a 100644 --- a/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export enum QueryEditorMode { Builder = 'builder', diff --git a/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts index 2586cfddfa5..36210e7648d 100644 --- a/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/news/panelcfg/x/NewsPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { /** diff --git a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts index 6e529363c38..34084472b19 100644 --- a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface ArcOption { /** diff --git a/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts index 97102b4194e..0f8681b7b78 100644 --- a/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/piechart/panelcfg/x/PieChartPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; /** * Select the pie chart display style. diff --git a/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts index ee114cc1a01..e731d7fa1fe 100644 --- a/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/stat/panelcfg/x/StatPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends common.SingleStatBaseOptions { colorMode: common.BigValueColorMode; diff --git a/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts index a62b8181e42..e589cdde3d8 100644 --- a/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/statetimeline/panelcfg/x/StateTimelinePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones { /** diff --git a/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts index a6138868be9..beb6db453da 100644 --- a/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/statushistory/panelcfg/x/StatusHistoryPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones { /** diff --git a/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts index f059948093e..bc9b29ca205 100644 --- a/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as ui from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options { /** diff --git a/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts index 485a0e734e9..a3cbd4096a9 100644 --- a/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/text/panelcfg/x/TextPanelCfg_types.gen.ts @@ -8,7 +8,7 @@ // // Run 'make gen-cue' from repository root to regenerate. -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export enum TextMode { Code = 'code', diff --git a/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts index 2c82fce72bb..be8096fcf67 100644 --- a/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/timeseries/panelcfg/x/TimeSeriesPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export interface Options extends common.OptionsWithTimezones { legend: common.VizLegendOptions; diff --git a/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts index bd626c2a0c7..e9e79e21c63 100644 --- a/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/trend/panelcfg/x/TrendPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; /** * Identical to timeseries... except it does not have timezone settings diff --git a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts index cef72cf3104..208d73a91c2 100644 --- a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts @@ -10,7 +10,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "11.5.0-pre"; +export const pluginVersion = "11.6.0-pre"; export enum PointShape { Circle = 'circle', diff --git a/packages/grafana-sql/package.json b/packages/grafana-sql/package.json index b737832e038..ef556ab0d49 100644 --- a/packages/grafana-sql/package.json +++ b/packages/grafana-sql/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "private": true, "name": "@grafana/sql", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git", @@ -15,11 +15,11 @@ }, "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/e2e-selectors": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "@react-awesome-query-builder/ui": "6.6.4", "immutable": "5.0.3", "lodash": "4.17.21", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 7892f942bee..ebe6a77ecdc 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/ui", - "version": "11.5.0-pre", + "version": "11.6.0-pre", "description": "Grafana Components Library", "keywords": [ "grafana", @@ -50,10 +50,10 @@ "@emotion/react": "11.14.0", "@emotion/serialize": "1.3.3", "@floating-ui/react": "0.27.3", - "@grafana/data": "11.5.0-pre", - "@grafana/e2e-selectors": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", "@grafana/faro-web-sdk": "^1.3.6", - "@grafana/schema": "11.5.0-pre", + "@grafana/schema": "11.6.0-pre", "@hello-pangea/dnd": "17.0.0", "@leeoniya/ufuzzy": "1.0.18", "@monaco-editor/react": "4.6.0", diff --git a/public/app/plugins/datasource/azuremonitor/package.json b/public/app/plugins/datasource/azuremonitor/package.json index b94e6cbfb9a..854abbb71e0 100644 --- a/public/app/plugins/datasource/azuremonitor/package.json +++ b/public/app/plugins/datasource/azuremonitor/package.json @@ -2,14 +2,14 @@ "name": "@grafana-plugins/grafana-azure-monitor-datasource", "description": "Grafana data source for Azure Monitor", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "@kusto/monaco-kusto": "^10.0.0", "fast-deep-equal": "^3.1.3", "i18next": "^24.0.0", @@ -25,8 +25,8 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.2.0", diff --git a/public/app/plugins/datasource/cloud-monitoring/package.json b/public/app/plugins/datasource/cloud-monitoring/package.json index 49699a2341c..4b12136a513 100644 --- a/public/app/plugins/datasource/cloud-monitoring/package.json +++ b/public/app/plugins/datasource/cloud-monitoring/package.json @@ -2,15 +2,15 @@ "name": "@grafana-plugins/stackdriver", "description": "Grafana data source for Google Cloud Monitoring", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", "@grafana/google-sdk": "0.1.2", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "debounce-promise": "3.1.2", "fast-deep-equal": "^3.1.3", "i18next": "^24.0.0", @@ -26,8 +26,8 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.2.0", diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json index 9b348a697bd..6cf8a965548 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/package.json +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/package.json @@ -2,22 +2,22 @@ "name": "@grafana-plugins/grafana-postgresql-datasource", "description": "PostgreSQL data source plugin", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/sql": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/sql": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.1", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/react": "16.2.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json index 1ad424096a3..d75bed9c7ca 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/grafana-pyroscope-datasource", "description": "Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "fast-deep-equal": "^3.1.3", "lodash": "4.17.21", "monaco-editor": "0.34.1", @@ -20,7 +20,7 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.2.0", diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/package.json b/public/app/plugins/datasource/grafana-testdata-datasource/package.json index cdab8016c21..1c283aa6baa 100644 --- a/public/app/plugins/datasource/grafana-testdata-datasource/package.json +++ b/public/app/plugins/datasource/grafana-testdata-datasource/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/grafana-testdata-datasource", "description": "Generates test data in different forms", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "d3-random": "^3.0.1", "lodash": "4.17.21", "micro-memoize": "^4.1.2", @@ -21,8 +21,8 @@ "uuid": "11.0.5" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/react": "16.2.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/jaeger/package.json b/public/app/plugins/datasource/jaeger/package.json index 2ae92fa4a79..99574d165d1 100644 --- a/public/app/plugins/datasource/jaeger/package.json +++ b/public/app/plugins/datasource/jaeger/package.json @@ -2,7 +2,7 @@ "name": "@grafana-plugins/jaeger", "description": "Jaeger plugin for Grafana", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", diff --git a/public/app/plugins/datasource/mssql/package.json b/public/app/plugins/datasource/mssql/package.json index 477be490956..4bc83b5cfc2 100644 --- a/public/app/plugins/datasource/mssql/package.json +++ b/public/app/plugins/datasource/mssql/package.json @@ -2,22 +2,22 @@ "name": "@grafana-plugins/mssql", "description": "MSSQL data source plugin", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/sql": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/sql": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.1", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/react": "16.2.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/mysql/package.json b/public/app/plugins/datasource/mysql/package.json index 86ad07e26c2..7b659769481 100644 --- a/public/app/plugins/datasource/mysql/package.json +++ b/public/app/plugins/datasource/mysql/package.json @@ -2,22 +2,22 @@ "name": "@grafana-plugins/mysql", "description": "MySQL data source plugin", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", "@grafana/plugin-ui": "0.10.1", - "@grafana/runtime": "11.5.0-pre", - "@grafana/sql": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/sql": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "lodash": "4.17.21", "react": "18.3.1", "rxjs": "7.8.1", "tslib": "2.8.1" }, "devDependencies": { - "@grafana/e2e-selectors": "11.5.0-pre", - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/e2e-selectors": "11.6.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/react": "16.2.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/parca/package.json b/public/app/plugins/datasource/parca/package.json index 4c32c0650ee..13394f1de77 100644 --- a/public/app/plugins/datasource/parca/package.json +++ b/public/app/plugins/datasource/parca/package.json @@ -2,13 +2,13 @@ "name": "@grafana-plugins/parca", "description": "Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", - "@grafana/data": "11.5.0-pre", - "@grafana/runtime": "11.5.0-pre", - "@grafana/schema": "11.5.0-pre", - "@grafana/ui": "11.5.0-pre", + "@grafana/data": "11.6.0-pre", + "@grafana/runtime": "11.6.0-pre", + "@grafana/schema": "11.6.0-pre", + "@grafana/ui": "11.6.0-pre", "lodash": "4.17.21", "monaco-editor": "0.34.1", "react": "18.3.1", @@ -18,7 +18,7 @@ "tslib": "2.8.1" }, "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/react": "16.2.0", "@testing-library/user-event": "14.6.1", diff --git a/public/app/plugins/datasource/tempo/package.json b/public/app/plugins/datasource/tempo/package.json index 036930b25f1..1cf120ff959 100644 --- a/public/app/plugins/datasource/tempo/package.json +++ b/public/app/plugins/datasource/tempo/package.json @@ -2,7 +2,7 @@ "name": "@grafana-plugins/tempo", "description": "Grafana plugin for the Tempo data source.", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", @@ -39,7 +39,7 @@ "uuid": "11.0.5" }, "devDependencies": { - "@grafana/plugin-configs": "11.5.0-pre", + "@grafana/plugin-configs": "11.6.0-pre", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.2.0", diff --git a/public/app/plugins/datasource/zipkin/package.json b/public/app/plugins/datasource/zipkin/package.json index cb874a51024..b40fb1cf8ec 100644 --- a/public/app/plugins/datasource/zipkin/package.json +++ b/public/app/plugins/datasource/zipkin/package.json @@ -2,7 +2,7 @@ "name": "@grafana-plugins/zipkin", "description": "Zipkin plugin for Grafana", "private": true, - "version": "11.5.0-pre", + "version": "11.6.0-pre", "dependencies": { "@emotion/css": "11.13.5", "@grafana/data": "workspace:*", diff --git a/yarn.lock b/yarn.lock index 183367c8947..f661f40cdcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2704,13 +2704,13 @@ __metadata: resolution: "@grafana-plugins/grafana-azure-monitor-datasource@workspace:public/app/plugins/datasource/azuremonitor" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@kusto/monaco-kusto": "npm:^10.0.0" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" @@ -2748,13 +2748,13 @@ __metadata: resolution: "@grafana-plugins/grafana-postgresql-datasource@workspace:public/app/plugins/datasource/grafana-postgresql-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/sql": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/sql": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/react": "npm:16.2.0" "@testing-library/user-event": "npm:14.6.1" @@ -2779,11 +2779,11 @@ __metadata: resolution: "@grafana-plugins/grafana-pyroscope-datasource@workspace:public/app/plugins/datasource/grafana-pyroscope-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" "@testing-library/react": "npm:16.2.0" @@ -2819,12 +2819,12 @@ __metadata: resolution: "@grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/react": "npm:16.2.0" "@testing-library/user-event": "npm:14.6.1" @@ -2900,13 +2900,13 @@ __metadata: resolution: "@grafana-plugins/mssql@workspace:public/app/plugins/datasource/mssql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/sql": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/sql": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/react": "npm:16.2.0" "@testing-library/user-event": "npm:14.6.1" @@ -2931,13 +2931,13 @@ __metadata: resolution: "@grafana-plugins/mysql@workspace:public/app/plugins/datasource/mysql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/sql": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/sql": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/react": "npm:16.2.0" "@testing-library/user-event": "npm:14.6.1" @@ -2962,11 +2962,11 @@ __metadata: resolution: "@grafana-plugins/parca@workspace:public/app/plugins/datasource/parca" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/plugin-configs": "npm:11.5.0-pre" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/react": "npm:16.2.0" "@testing-library/user-event": "npm:14.6.1" @@ -2994,14 +2994,14 @@ __metadata: resolution: "@grafana-plugins/stackdriver@workspace:public/app/plugins/datasource/cloud-monitoring" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/google-sdk": "npm:0.1.2" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" "@testing-library/react": "npm:16.2.0" @@ -3046,7 +3046,7 @@ __metadata: "@grafana/lezer-traceql": "npm:0.0.20" "@grafana/monaco-logql": "npm:^0.0.8" "@grafana/o11y-ds-frontend": "workspace:*" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/plugin-configs": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" "@grafana/runtime": "workspace:*" "@grafana/schema": "workspace:*" @@ -3194,12 +3194,12 @@ __metadata: languageName: node linkType: hard -"@grafana/data@npm:11.5.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": +"@grafana/data@npm:11.6.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": version: 0.0.0-use.local resolution: "@grafana/data@workspace:packages/grafana-data" dependencies: "@braintree/sanitize-url": "npm:7.0.1" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" "@rollup/plugin-node-resolve": "npm:16.0.0" "@types/d3-interpolate": "npm:^3.0.0" @@ -3257,7 +3257,7 @@ __metadata: languageName: node linkType: hard -"@grafana/e2e-selectors@npm:11.5.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors": +"@grafana/e2e-selectors@npm:11.6.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors": version: 0.0.0-use.local resolution: "@grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors" dependencies: @@ -3360,9 +3360,9 @@ __metadata: "@babel/preset-env": "npm:7.26.7" "@babel/preset-react": "npm:7.26.3" "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@leeoniya/ufuzzy": "npm:1.0.18" "@rollup/plugin-node-resolve": "npm:16.0.0" "@testing-library/dom": "npm:10.4.0" @@ -3468,13 +3468,13 @@ __metadata: resolution: "@grafana/o11y-ds-frontend@workspace:packages/grafana-o11y-ds-frontend" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:^6.1.2" "@testing-library/react": "npm:16.2.0" @@ -3498,7 +3498,7 @@ __metadata: languageName: unknown linkType: soft -"@grafana/plugin-configs@npm:11.5.0-pre, @grafana/plugin-configs@workspace:*, @grafana/plugin-configs@workspace:packages/grafana-plugin-configs": +"@grafana/plugin-configs@npm:11.6.0-pre, @grafana/plugin-configs@workspace:*, @grafana/plugin-configs@workspace:packages/grafana-plugin-configs": version: 0.0.0-use.local resolution: "@grafana/plugin-configs@workspace:packages/grafana-plugin-configs" dependencies: @@ -3599,15 +3599,15 @@ __metadata: "@emotion/css": "npm:11.13.5" "@emotion/eslint-plugin": "npm:11.12.0" "@floating-ui/react": "npm:0.27.3" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/faro-web-sdk": "npm:1.12.3" "@grafana/llm": "npm:0.12.0" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@hello-pangea/dnd": "npm:17.0.0" "@leeoniya/ufuzzy": "npm:1.0.18" "@lezer/common": "npm:1.2.3" @@ -3702,16 +3702,16 @@ __metadata: languageName: unknown linkType: soft -"@grafana/runtime@npm:11.5.0-pre, @grafana/runtime@workspace:*, @grafana/runtime@workspace:packages/grafana-runtime": +"@grafana/runtime@npm:11.6.0-pre, @grafana/runtime@workspace:*, @grafana/runtime@workspace:packages/grafana-runtime": version: 0.0.0-use.local resolution: "@grafana/runtime@workspace:packages/grafana-runtime" dependencies: - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@rollup/plugin-node-resolve": "npm:16.0.0" "@rollup/plugin-terser": "npm:0.4.4" "@testing-library/dom": "npm:10.4.0" @@ -3851,7 +3851,7 @@ __metadata: languageName: node linkType: hard -"@grafana/schema@npm:11.5.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": +"@grafana/schema@npm:11.6.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": version: 0.0.0-use.local resolution: "@grafana/schema@workspace:packages/grafana-schema" dependencies: @@ -3869,17 +3869,17 @@ __metadata: languageName: unknown linkType: soft -"@grafana/sql@npm:11.5.0-pre, @grafana/sql@workspace:*, @grafana/sql@workspace:packages/grafana-sql": +"@grafana/sql@npm:11.6.0-pre, @grafana/sql@workspace:*, @grafana/sql@workspace:packages/grafana-sql": version: 0.0.0-use.local resolution: "@grafana/sql@workspace:packages/grafana-sql" dependencies: "@emotion/css": "npm:11.13.5" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/plugin-ui": "npm:0.10.1" - "@grafana/runtime": "npm:11.5.0-pre" + "@grafana/runtime": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" - "@grafana/ui": "npm:11.5.0-pre" + "@grafana/ui": "npm:11.6.0-pre" "@react-awesome-query-builder/ui": "npm:6.6.4" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:^6.1.2" @@ -3997,7 +3997,7 @@ __metadata: languageName: node linkType: hard -"@grafana/ui@npm:11.5.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": +"@grafana/ui@npm:11.6.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": version: 0.0.0-use.local resolution: "@grafana/ui@workspace:packages/grafana-ui" dependencies: @@ -4007,10 +4007,10 @@ __metadata: "@emotion/serialize": "npm:1.3.3" "@faker-js/faker": "npm:^9.0.0" "@floating-ui/react": "npm:0.27.3" - "@grafana/data": "npm:11.5.0-pre" - "@grafana/e2e-selectors": "npm:11.5.0-pre" + "@grafana/data": "npm:11.6.0-pre" + "@grafana/e2e-selectors": "npm:11.6.0-pre" "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/schema": "npm:11.5.0-pre" + "@grafana/schema": "npm:11.6.0-pre" "@grafana/tsconfig": "npm:^2.0.0" "@hello-pangea/dnd": "npm:17.0.0" "@leeoniya/ufuzzy": "npm:1.0.18" @@ -8712,7 +8712,7 @@ __metadata: dependencies: "@emotion/css": "npm:11.11.2" "@grafana/data": "workspace:*" - "@grafana/plugin-configs": "npm:11.5.0-pre" + "@grafana/plugin-configs": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/schema": "workspace:*" "@grafana/ui": "workspace:*" From 8ac9c47222eeac362054b758fa073ff36bea996a Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Mon, 3 Feb 2025 16:08:15 -0700 Subject: [PATCH 09/12] Folders: Validator: add temporary fix (#100019) --- .../accesscontrol/ossaccesscontrol/folder.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/services/accesscontrol/ossaccesscontrol/folder.go b/pkg/services/accesscontrol/ossaccesscontrol/folder.go index 0ca7921723c..050570868f6 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/folder.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/folder.go @@ -2,6 +2,7 @@ package ossaccesscontrol import ( "context" + "errors" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -106,6 +107,17 @@ func ProvideFolderPermissions( }) if err != nil { + // if the folder is not found, this may be on the create path, + // where the write path to legacy will then go through the read + // path and try to read from both legacy & unified before it exists on both + if features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) && errors.Is(err, dashboards.ErrFolderNotFound) { + _, err = folderService.GetLegacy(ctx, &folder.GetFolderQuery{ + UID: &resourceID, + OrgID: orgID, + SignedInUser: ident, + }) + return err + } return err } From 0fa2d182d6323db54c4e749903140b1bc89fb46a Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Mon, 3 Feb 2025 21:38:57 -0700 Subject: [PATCH 10/12] K8s: Fix dashboard deletion in folders (#100023) --- .../folderimpl/folder_unifiedstorage.go | 34 +++- .../folderimpl/folder_unifiedstorage_test.go | 148 ++++++++++++++++++ 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/pkg/services/folder/folderimpl/folder_unifiedstorage.go b/pkg/services/folder/folderimpl/folder_unifiedstorage.go index a1473e7558d..6d4ae13da06 100644 --- a/pkg/services/folder/folderimpl/folder_unifiedstorage.go +++ b/pkg/services/folder/folderimpl/folder_unifiedstorage.go @@ -36,11 +36,13 @@ import ( "github.com/grafana/grafana/pkg/storage/unified/resource" "github.com/grafana/grafana/pkg/storage/unified/search" "github.com/grafana/grafana/pkg/util" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // interface to allow for testing type folderK8sHandler interface { getClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) + getDashboardClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) getNamespace(orgID int64) string getSearcher(ctx context.Context) resource.ResourceClient } @@ -688,7 +690,7 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol // if dashboard restore is on we don't delete public dashboards, the hard delete will take care of it later if !s.features.IsEnabledGlobally(featuremgmt.FlagDashboardRestore) { - // We need a list of dashboard uids inside the folder to delete related public dashboards + // We need a list of dashboard uids inside the folder to delete related dashboards & public dashboards var dashboardUIDs []string // we cannot use the dashboard service directly due to circular dependencies, // so either use the search client if the feature is enabled or use the dashboard store @@ -722,10 +724,17 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol if err != nil { return folder.ErrInternal.Errorf("failed to fetch dashboards: %w", err) } - dashboardUIDs = make([]string, len(hits.Hits)) + k8sDeleteClient, created := s.k8sclient.getDashboardClient(ctx, cmd.OrgID) + if !created { + return folder.ErrInternal.Errorf("failed to create client to get dashboards") + } for i, dashboard := range hits.Hits { dashboardUIDs[i] = dashboard.Name + err = k8sDeleteClient.Delete(ctx, dashboard.Name, metav1.DeleteOptions{}) + if err != nil { + return folder.ErrInternal.Errorf("failed to delete child dashboard: %w", err) + } } } else { dashes, err := s.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{SignedInUser: cmd.SignedInUser, FolderUIDs: folders, OrgId: cmd.OrgID}) @@ -735,6 +744,13 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol dashboardUIDs = make([]string, len(dashes)) for i, dashboard := range dashes { dashboardUIDs[i] = dashboard.UID + err = s.dashboardStore.DeleteDashboard(ctx, &dashboards.DeleteDashboardCommand{ + UID: dashboard.UID, + OrgID: cmd.OrgID, + }) + if err != nil { + return folder.ErrInternal.Errorf("failed to delete child dashboard: %w", err) + } } } // Delete all public dashboards in the folders @@ -986,6 +1002,20 @@ func (fk8s *foldk8sHandler) getClient(ctx context.Context, orgID int64) (dynamic return dyn.Resource(fk8s.gvr).Namespace(fk8s.getNamespace(orgID)), true } +func (fk8s *foldk8sHandler) getDashboardClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) { + cfg := fk8s.restConfigProvider(ctx) + if cfg == nil { + return nil, false + } + + dyn, err := dynamic.NewForConfig(cfg) + if err != nil { + return nil, false + } + + return dyn.Resource(dashboardv0.DashboardResourceInfo.GroupVersionResource()).Namespace(fk8s.getNamespace(orgID)), true +} + func (fk8s *foldk8sHandler) getNamespace(orgID int64) string { return fk8s.namespacer(orgID) } diff --git a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go index e2d50566662..b97b2ea51e9 100644 --- a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go +++ b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/grpc" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/dynamic" clientrest "k8s.io/client-go/rest" @@ -682,6 +683,46 @@ func (r resourceClientMock) Search(ctx context.Context, in *resource.ResourceSea }, nil } + if len(in.Options.Fields) > 0 && + in.Options.Fields[0].Key == resource.SEARCH_FIELD_FOLDER && + in.Options.Fields[0].Operator == "in" && + len(in.Options.Fields[0].Values) > 0 { + rows := []*resource.ResourceTableRow{} + for i, row := range in.Options.Fields[0].Values { + rows = append(rows, &resource.ResourceTableRow{ + Key: &resource.ResourceKey{ + Name: row, + Resource: "folders", + }, + Cells: [][]byte{ + []byte(fmt.Sprintf("%d", i)), // set legacy id as the row id + []byte(fmt.Sprintf("folder%d", i)), // set title as folder + row id + []byte(""), + }, + }) + } + return &resource.ResourceSearchResponse{ + Results: &resource.ResourceTable{ + Columns: []*resource.ResourceTableColumnDefinition{ + { + Name: "_id", + Type: resource.ResourceTableColumnDefinition_STRING, + }, + { + Name: "title", + Type: resource.ResourceTableColumnDefinition_STRING, + }, + { + Name: "folder", + Type: resource.ResourceTableColumnDefinition_STRING, + }, + }, + Rows: rows, + }, + TotalHits: int64(len(rows)), + }, nil + } + // not found return &resource.ResourceSearchResponse{ Results: &resource.ResourceTable{}, @@ -716,6 +757,11 @@ func (m *mockFoldersK8sCli) getClient(ctx context.Context, orgID int64) (dynamic return args.Get(0).(dynamic.ResourceInterface), args.Bool(1) } +func (m *mockFoldersK8sCli) getDashboardClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) { + args := m.Called(ctx, orgID) + return args.Get(0).(dynamic.ResourceInterface), args.Bool(1) +} + func (m *mockFoldersK8sCli) getNamespace(orgID int64) string { if orgID == 1 { return "default" @@ -834,3 +880,105 @@ func TestSearchFoldersFromApiServer(t *testing.T) { require.Equal(t, expectedResult, result) }) } + +type mockDashboardCli struct { + mock.Mock + dynamic.ResourceInterface +} + +func (c *mockDashboardCli) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error { + args := c.Called(ctx, name, options) + return args.Error(0) +} + +func TestDeleteFoldersFromApiServer(t *testing.T) { + fakeK8sClient := new(mockFoldersK8sCli) + fakeFolderStore := folder.NewFakeStore() + dashboardStore := dashboards.NewFakeDashboardStore(t) + publicDashboardFakeService := publicdashboards.NewFakePublicDashboardServiceWrapper(t) + service := Service{ + k8sclient: fakeK8sClient, + unifiedStore: fakeFolderStore, + dashboardStore: dashboardStore, + publicDashboardService: publicDashboardFakeService, + registry: make(map[string]folder.RegistryService), + features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesFoldersServiceV2), + } + fakeK8sClient.On("getSearcher", mock.Anything).Return(fakeK8sClient) + user := &user.SignedInUser{OrgID: 1} + ctx := identity.WithRequester(context.Background(), user) + guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{ + CanSaveValue: true, + CanViewValue: true, + }) + db, cfg := sqlstore.InitTestDB(t) + + alertingStore := ngstore.DBstore{ + SQLStore: db, + Cfg: cfg.UnifiedAlerting, + Logger: log.New("test-alerting-store"), + AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true}, + } + require.NoError(t, service.RegisterService(alertingStore)) + + t.Run("Should delete folder", func(t *testing.T) { + dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once() + publicDashboardFakeService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + err := service.deleteFromApiServer(ctx, &folder.DeleteFolderCommand{ + UID: "uid", + OrgID: 1, + SignedInUser: user, + }) + require.NoError(t, err) + }) + + t.Run("Should delete dashboards and public dashboards within the folder", func(t *testing.T) { + dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{ + { + UID: "test", + OrgID: 1, + }, + { + UID: "test2", + OrgID: 1, + }, + }, nil).Once() + dashboardStore.On("DeleteDashboard", mock.Anything, &dashboards.DeleteDashboardCommand{ + UID: "test", + OrgID: 1, + }).Return(nil).Once() + dashboardStore.On("DeleteDashboard", mock.Anything, &dashboards.DeleteDashboardCommand{ + UID: "test2", + OrgID: 1, + }).Return(nil).Once() + publicDashboardFakeService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"test", "test2"}).Return(nil).Once() + err := service.deleteFromApiServer(ctx, &folder.DeleteFolderCommand{ + UID: "uid", + OrgID: 1, + SignedInUser: user, + }) + require.NoError(t, err) + dashboardStore.AssertExpectations(t) + publicDashboardFakeService.AssertExpectations(t) + }) + + // enable k8s ff for dashboards, retest + service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesFoldersServiceV2, featuremgmt.FlagKubernetesCliDashboards) + + t.Run("Should delete dashboards and public dashboards within the folder through k8s if the ff is enabled", func(t *testing.T) { + dashboardK8sCli := mockDashboardCli{} + dashboardK8sCli.On("Delete", mock.Anything, "uid1", mock.Anything, mock.Anything).Return(nil).Once() + fakeK8sClient.On("getDashboardClient", mock.Anything, mock.Anything).Return(&dashboardK8sCli, true) + fakeK8sClient.On("getSearcher", mock.Anything).Return(fakeK8sClient) + publicDashboardFakeService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{"uid1"}).Return(nil).Once() + err := service.deleteFromApiServer(ctx, &folder.DeleteFolderCommand{ + UID: "uid1", + OrgID: 1, + SignedInUser: user, + }) + require.NoError(t, err) + dashboardStore.AssertExpectations(t) + publicDashboardFakeService.AssertExpectations(t) + dashboardK8sCli.AssertExpectations(t) + }) +} From 443cb092b4a469359f9bfb875f90eec4c68bc554 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Tue, 4 Feb 2025 10:32:29 +0200 Subject: [PATCH 11/12] RTK APIs: Support multiple source files (#100029) --- scripts/process-specs.ts | 71 +++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/scripts/process-specs.ts b/scripts/process-specs.ts index a53f6e6e6e1..fb96a8beaa9 100644 --- a/scripts/process-specs.ts +++ b/scripts/process-specs.ts @@ -129,33 +129,50 @@ function simplifySchemaName(schemaName: string) { } } -const sourceDir = path.resolve(__dirname, '../pkg/tests/apis/openapi_snapshots'); -const outputDir = path.resolve(__dirname, '../data/openapi'); - -// Create the output directory if it doesn't exist -if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); -} - -const files = fs.readdirSync(sourceDir).filter((file: string) => file.endsWith('.json')); - -for (const file of files) { - const inputPath = path.join(sourceDir, file); - const outputPath = path.join(outputDir, file); - - console.log(`Processing file "${file}"...`); - - const fileContent = fs.readFileSync(inputPath, 'utf-8'); - - let inputSpec; - try { - inputSpec = JSON.parse(fileContent); - } catch (err) { - console.error(`Invalid JSON file "${file}". Skipping this file.`); - continue; +/** + * Process all files in a source directory and write results to output directory + */ +function processDirectory(sourceDir: string, outputDir: string) { + // Skip if source directory doesn't exist + if (!fs.existsSync(sourceDir)) { + return; } - const outputSpec = processOpenAPISpec(inputSpec); - fs.writeFileSync(outputPath, JSON.stringify(outputSpec, null, 2), 'utf-8'); - console.log(`Processing completed for file "${file}".`); + // Create the output directory if it doesn't exist + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const files = fs.readdirSync(sourceDir).filter((file: string) => file.endsWith('.json')); + + for (const file of files) { + const inputPath = path.join(sourceDir, file); + const outputPath = path.join(outputDir, file); + + console.log(`Processing file "${file}"...`); + + const fileContent = fs.readFileSync(inputPath, 'utf-8'); + + let inputSpec; + try { + inputSpec = JSON.parse(fileContent); + } catch (err) { + console.error(`Invalid JSON file "${file}". Skipping this file.`); + continue; + } + + const outputSpec = processOpenAPISpec(inputSpec); + fs.writeFileSync(outputPath, JSON.stringify(outputSpec, null, 2), 'utf-8'); + console.log(`Processing completed for file "${file}".`); + } +} + +const sourceDirs = [ + path.resolve(__dirname, '../pkg/tests/apis/openapi_snapshots'), + path.resolve(__dirname, '../pkg/extensions/apiserver/tests/openapi_snapshots'), +]; +const outputDir = path.resolve(__dirname, '../data/openapi'); + +for (const sourceDir of sourceDirs) { + processDirectory(sourceDir, outputDir); } From c94b8b78aeb92a2e0af0738666667adb0d821080 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Tue, 4 Feb 2025 09:13:44 +0000 Subject: [PATCH 12/12] API client generation: Add `verify-api-clients` step to drone (#99987) * add verify-api-clients step to drone * change to check the verify step fails * ignore conf dir * rerun make drone * undo change to make step fail --- .drone.yml | 32 +++++++++++++++++++++- package.json | 2 +- scripts/drone/pipelines/lint_frontend.star | 3 ++ scripts/drone/steps/lib.star | 22 +++++++++++++++ scripts/process-specs.ts | 6 ++-- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/.drone.yml b/.drone.yml index 224e42dc60c..b00daa6bcaf 100644 --- a/.drone.yml +++ b/.drone.yml @@ -339,6 +339,16 @@ steps: - yarn-install image: node:22-bookworm name: verify-i18n +- commands: + - yarn generate-apis + - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" + ]; then\n echo $file_diff\n echo \"\nAPI client + generation has not been committed. Please run 'yarn generate-apis', commit the + changes and push again.\"\n exit 1\n fi\n " + depends_on: + - yarn-install + image: node:22-bookworm + name: verify-api-clients trigger: event: - pull_request @@ -1782,6 +1792,16 @@ steps: - yarn-install image: node:22-bookworm name: verify-i18n +- commands: + - yarn generate-apis + - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" + ]; then\n echo $file_diff\n echo \"\nAPI client + generation has not been committed. Please run 'yarn generate-apis', commit the + changes and push again.\"\n exit 1\n fi\n " + depends_on: + - yarn-install + image: node:22-bookworm + name: verify-api-clients trigger: branch: main event: @@ -2916,6 +2936,16 @@ steps: - yarn-install image: node:22-bookworm name: verify-i18n +- commands: + - yarn generate-apis + - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" + ]; then\n echo $file_diff\n echo \"\nAPI client + generation has not been committed. Please run 'yarn generate-apis', commit the + changes and push again.\"\n exit 1\n fi\n " + depends_on: + - yarn-install + image: node:22-bookworm + name: verify-api-clients trigger: branch: - instant @@ -5569,6 +5599,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: b7abfae4fe14ca97a65bc5869984d81407f61a66fd06301fb2023aab07238a5b +hmac: abd48dfdec08a719b414fba06b06dfc97560df31d81751cd833b5fdf5b7f9937 ... diff --git a/package.json b/package.json index 682378efec3..5fd921a0cc7 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "plugin:build:commit": "nx run-many -t build:commit --projects='tag:scope:plugin'", "plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100", "generate-icons": "nx run grafana-icons:generate", - "process-specs": "npx tsx scripts/process-specs.ts", + "process-specs": "node --experimental-strip-types scripts/process-specs.ts", "generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts" }, "grafana": { diff --git a/scripts/drone/pipelines/lint_frontend.star b/scripts/drone/pipelines/lint_frontend.star index 1f98e371bb9..381f79373db 100644 --- a/scripts/drone/pipelines/lint_frontend.star +++ b/scripts/drone/pipelines/lint_frontend.star @@ -12,6 +12,7 @@ load( "enterprise_setup_step", "identify_runner_step", "lint_frontend_step", + "verify_api_clients_step", "verify_i18n_step", "yarn_install_step", ) @@ -35,6 +36,7 @@ def lint_frontend_pipeline(trigger, ver_mode): init_steps = [] lint_step = lint_frontend_step() i18n_step = verify_i18n_step() + api_clients_step = verify_api_clients_step() volumes = [] @@ -54,6 +56,7 @@ def lint_frontend_pipeline(trigger, ver_mode): test_steps = [ lint_step, i18n_step, + api_clients_step, ] return pipeline( diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star index 36cd3a2db86..3ee633dfb06 100644 --- a/scripts/drone/steps/lib.star +++ b/scripts/drone/steps/lib.star @@ -676,6 +676,28 @@ def verify_i18n_step(): ], } +def verify_api_clients_step(): + uncommited_error_message = "\nAPI client generation has not been committed. Please run 'yarn generate-apis', commit the changes and push again." + return { + "name": "verify-api-clients", + "image": images["node_deb"], + "depends_on": [ + "yarn-install", + ], + "commands": [ + "yarn generate-apis", + # Verify that client generation has been run and committed + ''' + file_diff=$(git diff ':!conf') + if [ -n "$file_diff" ]; then + echo $file_diff + echo "{}" + exit 1 + fi + '''.format(uncommited_error_message), + ], + } + def test_a11y_frontend_step(ver_mode, port = 3001): """Runs automated accessiblity tests against the frontend. diff --git a/scripts/process-specs.ts b/scripts/process-specs.ts index fb96a8beaa9..f13fdf7cb74 100644 --- a/scripts/process-specs.ts +++ b/scripts/process-specs.ts @@ -168,10 +168,10 @@ function processDirectory(sourceDir: string, outputDir: string) { } const sourceDirs = [ - path.resolve(__dirname, '../pkg/tests/apis/openapi_snapshots'), - path.resolve(__dirname, '../pkg/extensions/apiserver/tests/openapi_snapshots'), + path.resolve(import.meta.dirname, '../pkg/tests/apis/openapi_snapshots'), + path.resolve(import.meta.dirname, '../pkg/extensions/apiserver/tests/openapi_snapshots'), ]; -const outputDir = path.resolve(__dirname, '../data/openapi'); +const outputDir = path.resolve(import.meta.dirname, '../data/openapi'); for (const sourceDir of sourceDirs) { processDirectory(sourceDir, outputDir);