diff --git a/pkg/services/dashboardversion/dashverimpl/dashver.go b/pkg/services/dashboardversion/dashverimpl/dashver.go index 03b9b9ee2d9..c054154a72d 100644 --- a/pkg/services/dashboardversion/dashverimpl/dashver.go +++ b/pkg/services/dashboardversion/dashverimpl/dashver.go @@ -221,25 +221,42 @@ func (s *Service) getDashIDMaybeEmpty(ctx context.Context, uid string, orgID int return result.ID, nil } -func (s *Service) getHistoryThroughK8s(ctx context.Context, orgID int64, dashboardUID string, rv int64) (*dashver.DashboardVersionDTO, error) { - out, err := s.k8sclient.Get(ctx, dashboardUID, orgID, v1.GetOptions{ResourceVersion: strconv.FormatInt(rv, 10)}) - if err != nil { - if apierrors.IsNotFound(err) { +func (s *Service) getHistoryThroughK8s(ctx context.Context, orgID int64, dashboardUID string, version int64) (*dashver.DashboardVersionDTO, error) { + // this is an unideal implementation - we have to list all versions and filter here, since there currently is no way to query for the + // generation id in unified storage, so we cannot query for the dashboard version directly, and we cannot use search as history is not indexed. + // use batches to make sure we don't load too much data at once. + const batchSize = 50 + labelSelector := utils.LabelKeyGetHistory + "=" + dashboardUID + var continueToken string + for { + out, err := s.k8sclient.List(ctx, orgID, v1.ListOptions{ + LabelSelector: labelSelector, + Limit: int64(batchSize), + Continue: continueToken, + }) + if err != nil { + if apierrors.IsNotFound(err) { + return nil, dashboards.ErrDashboardNotFound + } + return nil, err + } + if out == nil { return nil, dashboards.ErrDashboardNotFound } - return nil, err - } - if out == nil { - return nil, dashboards.ErrDashboardNotFound + for _, item := range out.Items { + if item.GetGeneration() == version { + return s.UnstructuredToLegacyDashboardVersion(ctx, &item, orgID) + } + } + + continueToken = out.GetContinue() + if continueToken == "" || len(out.Items) == 0 { + break + } } - dash, err := s.UnstructuredToLegacyDashboardVersion(ctx, out, orgID) - if err != nil { - return nil, err - } - - return dash, nil + return nil, dashboards.ErrDashboardNotFound } func (s *Service) listHistoryThroughK8s(ctx context.Context, orgID int64, dashboardUID string, limit int64, continueToken string) (*dashver.DashboardVersionResponse, error) { @@ -313,18 +330,13 @@ func (s *Service) UnstructuredToLegacyDashboardVersion(ctx context.Context, item created = *updated } - id, err := obj.GetResourceVersionInt64() - if err != nil { - return nil, err - } - restoreVer, err := getRestoreVersion(obj.GetMessage()) if err != nil { return nil, err } out := dashver.DashboardVersionDTO{ - ID: id, + ID: dashVersion, DashboardID: obj.GetDeprecatedInternalID(), // nolint:staticcheck DashboardUID: uid, Created: created, diff --git a/pkg/services/dashboardversion/dashverimpl/dashver_test.go b/pkg/services/dashboardversion/dashverimpl/dashver_test.go index 1e34b144d30..4f210ea906f 100644 --- a/pkg/services/dashboardversion/dashverimpl/dashver_test.go +++ b/pkg/services/dashboardversion/dashverimpl/dashver_test.go @@ -74,7 +74,8 @@ func TestDashboardVersionService(t *testing.T) { require.NoError(t, err) obj.SetUpdatedTimestamp(&updatedTimestamp) mockCli.On("GetUserFromMeta", mock.Anything, "user:1").Return(&user.User{ID: 1}, nil) - mockCli.On("Get", mock.Anything, "uid", int64(1), v1.GetOptions{ResourceVersion: "10"}, mock.Anything).Return(dash, nil).Once() + mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(&unstructured.UnstructuredList{ + Items: []unstructured.Unstructured{*dash}}, nil).Once() res, err := dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{ DashboardID: 42, OrgID: 1, @@ -82,7 +83,7 @@ func TestDashboardVersionService(t *testing.T) { }) require.Nil(t, err) require.Equal(t, res, &dashver.DashboardVersionDTO{ - ID: 12, // RV should be used + ID: 10, Version: 10, ParentVersion: 9, DashboardID: 42, @@ -93,22 +94,23 @@ func TestDashboardVersionService(t *testing.T) { }) mockCli.On("GetUserFromMeta", mock.Anything, "user:2").Return(&user.User{ID: 2}, nil) - mockCli.On("Get", mock.Anything, "uid", int64(1), v1.GetOptions{ResourceVersion: "11"}, mock.Anything).Return(&unstructured.Unstructured{ - Object: map[string]any{ - "metadata": map[string]any{ - "name": "uid", - "resourceVersion": "11", - "generation": int64(11), - "labels": map[string]any{ - utils.LabelKeyDeprecatedInternalID: "42", // nolint:staticcheck + mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(&unstructured.UnstructuredList{ + Items: []unstructured.Unstructured{{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "uid", + "resourceVersion": "11", + "generation": int64(11), + "labels": map[string]any{ + utils.LabelKeyDeprecatedInternalID: "42", // nolint:staticcheck + }, + "annotations": map[string]any{ + utils.AnnoKeyCreatedBy: "user:1", + utils.AnnoKeyUpdatedBy: "user:2", // if updated by is set, that is the version creator + }, }, - "annotations": map[string]any{ - utils.AnnoKeyCreatedBy: "user:1", - utils.AnnoKeyUpdatedBy: "user:2", // if updated by is set, that is the version creator - }, - }, - "spec": map[string]any{}, - }}, nil).Once() + "spec": map[string]any{}, + }}}}, nil).Once() res, err = dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{ DashboardID: 42, OrgID: 1, @@ -116,7 +118,7 @@ func TestDashboardVersionService(t *testing.T) { }) require.Nil(t, err) require.Equal(t, res, &dashver.DashboardVersionDTO{ - ID: 11, // RV should be used + ID: 11, Version: 11, ParentVersion: 10, DashboardID: 42, @@ -133,7 +135,7 @@ func TestDashboardVersionService(t *testing.T) { dashboardVersionService.k8sclient = mockCli dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders) dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil) - mockCli.On("Get", mock.Anything, "uid", int64(1), v1.GetOptions{ResourceVersion: "10"}, mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "dashboards.dashboard.grafana.app", Resource: "dashboard"}, "uid")) + mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "dashboards.dashboard.grafana.app", Resource: "dashboard"}, "uid")) _, err := dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{ DashboardID: 42, @@ -285,7 +287,7 @@ func TestListDashboardVersions(t *testing.T) { require.Equal(t, 1, len(res.Versions)) require.EqualValues(t, &dashver.DashboardVersionResponse{ Versions: []*dashver.DashboardVersionDTO{{ - ID: 12, // should take rv + ID: 5, DashboardID: 42, ParentVersion: 4, Version: 5, // should take from spec