fix(unified-storage): unlist the deleted resource's versions (#102588)
This commit is contained in:
@@ -521,22 +521,19 @@ func (b *backend) ReadResource(ctx context.Context, req *resource.ReadRequest) *
|
||||
|
||||
// TODO: validate key ?
|
||||
|
||||
if req.ResourceVersion > 0 {
|
||||
return b.readHistory(ctx, req.Key, req.ResourceVersion)
|
||||
}
|
||||
|
||||
readReq := &sqlResourceReadRequest{
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
Request: req,
|
||||
Response: NewReadResponse(),
|
||||
}
|
||||
|
||||
sr := sqlResourceRead
|
||||
if req.ResourceVersion > 0 {
|
||||
// read a specific version
|
||||
sr = sqlResourceHistoryRead
|
||||
}
|
||||
|
||||
var res *resource.BackendReadResponse
|
||||
err := b.db.WithTx(ctx, ReadCommittedRO, func(ctx context.Context, tx db.Tx) error {
|
||||
var err error
|
||||
res, err = dbutil.QueryRow(ctx, tx, sr, readReq)
|
||||
res, err = dbutil.QueryRow(ctx, tx, sqlResourceRead, readReq)
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -745,7 +742,38 @@ func (b *backend) listAtRevision(ctx context.Context, req *resource.ListRequest,
|
||||
return iter.listRV, err
|
||||
}
|
||||
|
||||
// getHistory fetches the resources from the resource table.
|
||||
// readHistory fetches the resource history from the resource_history table.
|
||||
func (b *backend) readHistory(ctx context.Context, key *resource.ResourceKey, rv int64) *resource.BackendReadResponse {
|
||||
_, span := b.tracer.Start(ctx, tracePrefix+".ReadHistory")
|
||||
defer span.End()
|
||||
|
||||
readReq := &sqlResourceHistoryReadRequest{
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
Request: &historyReadRequest{
|
||||
Key: key,
|
||||
ResourceVersion: rv,
|
||||
},
|
||||
Response: NewReadResponse(),
|
||||
}
|
||||
|
||||
var res *resource.BackendReadResponse
|
||||
err := b.db.WithTx(ctx, ReadCommittedRO, func(ctx context.Context, tx db.Tx) error {
|
||||
var err error
|
||||
res, err = dbutil.QueryRow(ctx, tx, sqlResourceHistoryRead, readReq)
|
||||
return err
|
||||
})
|
||||
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return &resource.BackendReadResponse{Error: resource.NewNotFoundError(key)}
|
||||
}
|
||||
if err != nil {
|
||||
return &resource.BackendReadResponse{Error: resource.AsErrorResult(err)}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// getHistory fetches the resource history from the resource_history table.
|
||||
func (b *backend) getHistory(ctx context.Context, req *resource.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"getHistory")
|
||||
defer span.End()
|
||||
@@ -784,6 +812,9 @@ func (b *backend) getHistory(ctx context.Context, req *resource.ListRequest, cb
|
||||
listReq.MinRV = req.ResourceVersion
|
||||
}
|
||||
|
||||
// Ignore last deleted history record when listing the trash, using exact matching or not older than matching with a specific RV
|
||||
useLatestDeletionAsMinRV := listReq.MinRV == 0 && !listReq.Trash && req.VersionMatchV2 != resource.ResourceVersionMatchV2_Exact
|
||||
|
||||
err := b.db.WithTx(ctx, ReadCommittedRO, func(ctx context.Context, tx db.Tx) error {
|
||||
var err error
|
||||
iter.listRV, err = b.fetchLatestRV(ctx, tx, b.dialect, req.Options.Key.Group, req.Options.Key.Resource)
|
||||
@@ -791,6 +822,14 @@ func (b *backend) getHistory(ctx context.Context, req *resource.ListRequest, cb
|
||||
return err
|
||||
}
|
||||
|
||||
if useLatestDeletionAsMinRV {
|
||||
latestDeletedRV, err := b.fetchLatestHistoryRV(ctx, tx, b.dialect, req.Options.Key, resource.WatchEvent_DELETED)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listReq.MinRV = latestDeletedRV + 1
|
||||
}
|
||||
|
||||
rows, err := dbutil.QueryRows(ctx, tx, sqlResourceHistoryGet, listReq)
|
||||
if rows != nil {
|
||||
defer func() {
|
||||
@@ -860,3 +899,23 @@ func (b *backend) fetchLatestRV(ctx context.Context, x db.ContextExecer, d sqlte
|
||||
}
|
||||
return res.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// fetchLatestHistoryRV returns the current maximum RV in the resource_history table
|
||||
func (b *backend) fetchLatestHistoryRV(ctx context.Context, x db.ContextExecer, d sqltemplate.Dialect, key *resource.ResourceKey, eventType resource.WatchEvent_Type) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"fetchLatestHistoryRV")
|
||||
defer span.End()
|
||||
res, err := dbutil.QueryRow(ctx, x, sqlResourceHistoryReadLatestRV, sqlResourceHistoryReadLatestRVRequest{
|
||||
SQLTemplate: sqltemplate.New(d),
|
||||
Request: &historyReadLatestRVRequest{
|
||||
Key: key,
|
||||
EventType: eventType,
|
||||
},
|
||||
Response: new(resourceHistoryReadLatestRVResponse),
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return 0, nil
|
||||
} else if err != nil {
|
||||
return 0, fmt.Errorf("get resource version: %w", err)
|
||||
}
|
||||
return res.ResourceVersion, nil
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
|
||||
sqlmock "github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
unifiedbackend "github.com/grafana/grafana/pkg/storage/unified/backend"
|
||||
@@ -18,6 +16,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db/dbimpl"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/test"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -401,6 +401,129 @@ func TestBackend_delete(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
type readHistoryRow struct {
|
||||
namespace string
|
||||
group string
|
||||
resource string
|
||||
name string
|
||||
folder string
|
||||
resource_version string
|
||||
value string
|
||||
}
|
||||
|
||||
func TestBackend_ReadResource(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("happy path", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
b, ctx := setupBackendTest(t)
|
||||
|
||||
expectedReadRow := readHistoryRow{
|
||||
namespace: "ns",
|
||||
group: "gr",
|
||||
resource: "rs",
|
||||
name: "nm",
|
||||
folder: "folder",
|
||||
resource_version: "300",
|
||||
value: "rv-300",
|
||||
}
|
||||
readResource := []string{"namespace", "group", "resource", "name", "folder", "resource_version", "value"}
|
||||
b.SQLMock.ExpectBegin()
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource").
|
||||
WillReturnRows(sqlmock.NewRows(readResource).
|
||||
AddRow(
|
||||
expectedReadRow.namespace,
|
||||
expectedReadRow.group,
|
||||
expectedReadRow.resource,
|
||||
expectedReadRow.name,
|
||||
expectedReadRow.folder,
|
||||
expectedReadRow.resource_version,
|
||||
expectedReadRow.value,
|
||||
))
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
Key: resKey,
|
||||
}
|
||||
rps := b.ReadResource(ctx, req)
|
||||
require.NotNil(t, rps)
|
||||
require.Equal(t, int64(300), rps.ResourceVersion)
|
||||
require.Equal(t, "rv-300", string(rps.Value))
|
||||
require.Equal(t, "folder", rps.Folder)
|
||||
})
|
||||
|
||||
t.Run("no resource found", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
b, ctx := setupBackendTest(t)
|
||||
|
||||
b.SQLMock.ExpectBegin()
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource").
|
||||
WillReturnRows(sqlmock.NewRows([]string{}))
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
Key: resKey,
|
||||
}
|
||||
res := b.ReadResource(ctx, req)
|
||||
require.NotNil(t, res.Error)
|
||||
require.Equal(t, res.Error.Code, int32(404))
|
||||
})
|
||||
|
||||
t.Run("with resource version", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
b, ctx := setupBackendTest(t)
|
||||
|
||||
expectedReadRow := readHistoryRow{
|
||||
namespace: "ns",
|
||||
group: "gr",
|
||||
resource: "rs",
|
||||
name: "nm",
|
||||
folder: "folder",
|
||||
resource_version: "300",
|
||||
value: "rv-300",
|
||||
}
|
||||
|
||||
readHistoryColumns := []string{"namespace", "group", "resource", "name", "folder", "resource_version", "value"}
|
||||
b.SQLMock.ExpectBegin()
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").
|
||||
WillReturnRows(sqlmock.NewRows(readHistoryColumns).
|
||||
AddRow(
|
||||
expectedReadRow.namespace,
|
||||
expectedReadRow.group,
|
||||
expectedReadRow.resource,
|
||||
expectedReadRow.name,
|
||||
expectedReadRow.folder,
|
||||
expectedReadRow.resource_version,
|
||||
expectedReadRow.value,
|
||||
))
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
Key: resKey,
|
||||
ResourceVersion: 300,
|
||||
}
|
||||
rps := b.ReadResource(ctx, req)
|
||||
require.NotNil(t, rps)
|
||||
require.Equal(t, int64(300), rps.ResourceVersion)
|
||||
require.Equal(t, "rv-300", string(rps.Value))
|
||||
require.Equal(t, "folder", rps.Folder)
|
||||
})
|
||||
|
||||
t.Run("error reading resource", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
b, ctx := setupBackendTest(t)
|
||||
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").
|
||||
WillReturnError(errTest)
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
Key: resKey,
|
||||
}
|
||||
rps := b.ReadResource(ctx, req)
|
||||
require.NotNil(t, rps.Error)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBackend_getHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -415,16 +538,19 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
cols := []string{"resource_version", "namespace", "name", "folder", "value"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
versionMatch resource.ResourceVersionMatchV2
|
||||
resourceVersion int64
|
||||
expectedVersions []int64
|
||||
expectedListRv int64
|
||||
expectedRowsCount int
|
||||
expectedErr string
|
||||
name string
|
||||
source resource.ListRequest_Source
|
||||
versionMatch resource.ResourceVersionMatchV2
|
||||
resourceVersion int64
|
||||
expectedVersions []int64
|
||||
expectedListRv int64
|
||||
expectedRowsCount int
|
||||
expectedErr string
|
||||
expectedLatestDeletionAsMinRV bool
|
||||
}{
|
||||
{
|
||||
name: "with ResourceVersionMatch_NotOlderThan",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
resourceVersion: rv2,
|
||||
expectedVersions: []int64{rv2, rv3}, // Should be in ASC order due to NotOlderThan
|
||||
@@ -432,15 +558,18 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
expectedRowsCount: 2,
|
||||
},
|
||||
{
|
||||
name: "with ResourceVersionMatch_NotOlderThan and ResourceVersion=0",
|
||||
versionMatch: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
resourceVersion: 0,
|
||||
expectedVersions: []int64{rv1, rv2, rv3}, // Should be in ASC order due to NotOlderThan
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
name: "with ResourceVersionMatch_NotOlderThan and ResourceVersion=0",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
resourceVersion: 0,
|
||||
expectedVersions: []int64{rv1, rv2, rv3}, // Should be in ASC order due to NotOlderThan
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
expectedLatestDeletionAsMinRV: true,
|
||||
},
|
||||
{
|
||||
name: "with ResourceVersionMatch_Exact",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_Exact,
|
||||
resourceVersion: rv2,
|
||||
expectedVersions: []int64{rv2},
|
||||
@@ -448,17 +577,27 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
expectedRowsCount: 1,
|
||||
},
|
||||
{
|
||||
name: "with ResourceVersionMatch_Unset (default)",
|
||||
expectedVersions: []int64{rv3, rv2, rv1}, // Should be in DESC order by default
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
name: "with ResourceVersionMatch_Unset (default)",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
expectedVersions: []int64{rv3, rv2, rv1}, // Should be in DESC order by default
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
expectedLatestDeletionAsMinRV: true,
|
||||
},
|
||||
{
|
||||
name: "error with ResourceVersionMatch_Exact and ResourceVersion <= 0",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_Exact,
|
||||
resourceVersion: 0,
|
||||
expectedErr: "expecting an explicit resource version query when using Exact matching",
|
||||
},
|
||||
{
|
||||
name: "with ListRequest_TRASH",
|
||||
source: resource.ListRequest_TRASH,
|
||||
expectedVersions: []int64{rv3, rv2, rv1}, // Should be in DESC order by default
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -472,7 +611,7 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
Options: &resource.ListOptions{Key: key},
|
||||
ResourceVersion: tc.resourceVersion,
|
||||
VersionMatchV2: tc.versionMatch,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: tc.source,
|
||||
}
|
||||
|
||||
// Set up mock expectations only if we don't expect an error
|
||||
@@ -508,6 +647,13 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
AddRow(rv3, 0)
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_version").WillReturnRows(latestRVRows)
|
||||
|
||||
if tc.expectedLatestDeletionAsMinRV {
|
||||
latestHistoryRVRows := sqlmock.NewRows([]string{"resource_version"}).
|
||||
AddRow(rv1)
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").
|
||||
WillReturnRows(latestHistoryRVRows)
|
||||
}
|
||||
|
||||
// Expect history query
|
||||
historyRows := sqlmock.NewRows(cols)
|
||||
for _, rv := range tc.expectedVersions {
|
||||
@@ -606,6 +752,7 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
req.NextPageToken = page.token.String()
|
||||
}
|
||||
|
||||
expectedLatestDeletionAsMinRV := false
|
||||
items := make([]int64, 0)
|
||||
callback := func(iter resource.ListIterator) error {
|
||||
for iter.Next() {
|
||||
@@ -615,7 +762,7 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
}
|
||||
|
||||
b.SQLMock.ExpectBegin()
|
||||
historyRows := setupHistoryTest(b, page.versions, rv60)
|
||||
historyRows := setupHistoryTest(b, page.versions, rv60, expectedLatestDeletionAsMinRV)
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").WillReturnRows(historyRows)
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
@@ -643,6 +790,7 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
Limit: 4,
|
||||
}
|
||||
|
||||
expectedLatestDeletionAsMinRV := true
|
||||
// First batch of items we expect, in ASC order (because of NotOlderThan flag)
|
||||
// Even with ResourceVersion=0, the order is ASC because we use SortAscending=true
|
||||
expectedVersions := []int64{rv51, rv52, rv53, rv54}
|
||||
@@ -656,7 +804,7 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
}
|
||||
|
||||
b.SQLMock.ExpectBegin()
|
||||
historyRows := setupHistoryTest(b, expectedVersions, rv60)
|
||||
historyRows := setupHistoryTest(b, expectedVersions, rv60, expectedLatestDeletionAsMinRV)
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").WillReturnRows(historyRows)
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
@@ -668,12 +816,19 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
}
|
||||
|
||||
// setupHistoryTest creates the necessary mock expectations for a history test
|
||||
func setupHistoryTest(b testBackend, resourceVersions []int64, latestRV int64) *sqlmock.Rows {
|
||||
func setupHistoryTest(b testBackend, resourceVersions []int64, latestRV int64, expectedLatestDeletionAsMinRV4 bool) *sqlmock.Rows {
|
||||
// Expect fetch latest RV call - set to the highest resource version
|
||||
latestRVRows := sqlmock.NewRows([]string{"resource_version", "unix_timestamp"}).
|
||||
AddRow(latestRV, 0)
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_version").WillReturnRows(latestRVRows)
|
||||
|
||||
if expectedLatestDeletionAsMinRV4 {
|
||||
latestHistoryRVRows := sqlmock.NewRows([]string{"resource_version"}).
|
||||
AddRow(latestRV)
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").
|
||||
WillReturnRows(latestHistoryRVRows)
|
||||
}
|
||||
|
||||
// Create the mock rows for the history items
|
||||
cols := []string{"resource_version", "namespace", "name", "folder", "value"}
|
||||
historyRows := sqlmock.NewRows(cols)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
SELECT
|
||||
COALESCE(MAX({{ .Ident "resource_version" }}), 0) AS {{ .Ident "resource_version" | .Into .Response.ResourceVersion }}
|
||||
|
||||
FROM {{ .Ident "resource_history" }}
|
||||
|
||||
WHERE {{ .Ident "namespace" }} = {{ .Arg .Request.Key.Namespace }}
|
||||
AND {{ .Ident "group" }} = {{ .Arg .Request.Key.Group }}
|
||||
AND {{ .Ident "resource" }} = {{ .Arg .Request.Key.Resource }}
|
||||
AND {{ .Ident "name" }} = {{ .Arg .Request.Key.Name }}
|
||||
{{ if gt .Request.EventType 0 }}
|
||||
AND {{ .Ident "action" }} = {{ .Arg .Request.EventType }}
|
||||
{{ end }}
|
||||
LIMIT 1
|
||||
;
|
||||
@@ -29,22 +29,23 @@ func mustTemplate(filename string) *template.Template {
|
||||
|
||||
// Templates.
|
||||
var (
|
||||
sqlResourceDelete = mustTemplate("resource_delete.sql")
|
||||
sqlResourceInsert = mustTemplate("resource_insert.sql")
|
||||
sqlResourceUpdate = mustTemplate("resource_update.sql")
|
||||
sqlResourceRead = mustTemplate("resource_read.sql")
|
||||
sqlResourceStats = mustTemplate("resource_stats.sql")
|
||||
sqlResourceList = mustTemplate("resource_list.sql")
|
||||
sqlResourceHistoryList = mustTemplate("resource_history_list.sql")
|
||||
sqlResourceUpdateRV = mustTemplate("resource_update_rv.sql")
|
||||
sqlResourceHistoryRead = mustTemplate("resource_history_read.sql")
|
||||
sqlResourceHistoryUpdateRV = mustTemplate("resource_history_update_rv.sql")
|
||||
sqlResourceHistoryInsert = mustTemplate("resource_history_insert.sql")
|
||||
sqlResourceHistoryPoll = mustTemplate("resource_history_poll.sql")
|
||||
sqlResourceHistoryGet = mustTemplate("resource_history_get.sql")
|
||||
sqlResourceHistoryDelete = mustTemplate("resource_history_delete.sql")
|
||||
sqlResourceHistoryPrune = mustTemplate("resource_history_prune.sql")
|
||||
sqlResourceInsertFromHistory = mustTemplate("resource_insert_from_history.sql")
|
||||
sqlResourceDelete = mustTemplate("resource_delete.sql")
|
||||
sqlResourceInsert = mustTemplate("resource_insert.sql")
|
||||
sqlResourceUpdate = mustTemplate("resource_update.sql")
|
||||
sqlResourceRead = mustTemplate("resource_read.sql")
|
||||
sqlResourceStats = mustTemplate("resource_stats.sql")
|
||||
sqlResourceList = mustTemplate("resource_list.sql")
|
||||
sqlResourceHistoryList = mustTemplate("resource_history_list.sql")
|
||||
sqlResourceUpdateRV = mustTemplate("resource_update_rv.sql")
|
||||
sqlResourceHistoryRead = mustTemplate("resource_history_read.sql")
|
||||
sqlResourceHistoryReadLatestRV = mustTemplate("resource_history_read_latest_rv.sql")
|
||||
sqlResourceHistoryUpdateRV = mustTemplate("resource_history_update_rv.sql")
|
||||
sqlResourceHistoryInsert = mustTemplate("resource_history_insert.sql")
|
||||
sqlResourceHistoryPoll = mustTemplate("resource_history_poll.sql")
|
||||
sqlResourceHistoryGet = mustTemplate("resource_history_get.sql")
|
||||
sqlResourceHistoryDelete = mustTemplate("resource_history_delete.sql")
|
||||
sqlResourceHistoryPrune = mustTemplate("resource_history_prune.sql")
|
||||
sqlResourceInsertFromHistory = mustTemplate("resource_insert_from_history.sql")
|
||||
|
||||
// sqlResourceLabelsInsert = mustTemplate("resource_labels_insert.sql")
|
||||
sqlResourceVersionGet = mustTemplate("resource_version_get.sql")
|
||||
@@ -190,6 +191,52 @@ func (r sqlResourceListRequest) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
type historyReadRequest struct {
|
||||
Key *resource.ResourceKey
|
||||
ResourceVersion int64
|
||||
}
|
||||
|
||||
type sqlResourceHistoryReadRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Request *historyReadRequest
|
||||
Response *resource.BackendReadResponse
|
||||
}
|
||||
|
||||
func (r sqlResourceHistoryReadRequest) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func (r sqlResourceHistoryReadRequest) Results() (*resource.BackendReadResponse, error) {
|
||||
return r.Response, nil
|
||||
}
|
||||
|
||||
type historyReadLatestRVRequest struct {
|
||||
Key *resource.ResourceKey
|
||||
EventType resource.WatchEvent_Type
|
||||
}
|
||||
|
||||
type sqlResourceHistoryReadLatestRVRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Request *historyReadLatestRVRequest
|
||||
Response *resourceHistoryReadLatestRVResponse
|
||||
}
|
||||
|
||||
func (r sqlResourceHistoryReadLatestRVRequest) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func (r sqlResourceHistoryReadLatestRVRequest) Results() (*resourceHistoryReadLatestRVResponse, error) {
|
||||
return r.Response, nil
|
||||
}
|
||||
|
||||
type resourceHistoryReadLatestRVResponse struct {
|
||||
ResourceVersion int64
|
||||
}
|
||||
|
||||
func (r *resourceHistoryReadLatestRVResponse) Results() (*resourceHistoryReadLatestRVResponse, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
type historyListRequest struct {
|
||||
ResourceVersion, Limit, Offset int64
|
||||
Folder string
|
||||
|
||||
@@ -147,9 +147,9 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
sqlResourceHistoryRead: {
|
||||
{
|
||||
Name: "single path",
|
||||
Data: &sqlResourceReadRequest{
|
||||
Data: &sqlResourceHistoryReadRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &resource.ReadRequest{
|
||||
Request: &historyReadRequest{
|
||||
ResourceVersion: 123,
|
||||
Key: &resource.ResourceKey{
|
||||
Namespace: "ns",
|
||||
@@ -163,6 +163,40 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
|
||||
sqlResourceHistoryReadLatestRV: {
|
||||
{
|
||||
Name: "single path",
|
||||
Data: &sqlResourceHistoryReadLatestRVRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &historyReadLatestRVRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gp",
|
||||
Resource: "rs",
|
||||
Name: "nm",
|
||||
},
|
||||
},
|
||||
Response: new(resourceHistoryReadLatestRVResponse),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "with WatchEvent_DELETED",
|
||||
Data: &sqlResourceHistoryReadLatestRVRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &historyReadLatestRVRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gp",
|
||||
Resource: "rs",
|
||||
Name: "nm",
|
||||
},
|
||||
EventType: resource.WatchEvent_DELETED,
|
||||
},
|
||||
Response: new(resourceHistoryReadLatestRVResponse),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
sqlResourceHistoryUpdateRV: {
|
||||
{
|
||||
Name: "single path",
|
||||
|
||||
Vendored
Executable
+9
@@ -0,0 +1,9 @@
|
||||
SELECT
|
||||
COALESCE(MAX(`resource_version`), 0) AS `resource_version`
|
||||
FROM `resource_history`
|
||||
WHERE `namespace` = 'ns'
|
||||
AND `group` = 'gp'
|
||||
AND `resource` = 'rs'
|
||||
AND `name` = 'nm'
|
||||
LIMIT 1
|
||||
;
|
||||
Vendored
Executable
+10
@@ -0,0 +1,10 @@
|
||||
SELECT
|
||||
COALESCE(MAX(`resource_version`), 0) AS `resource_version`
|
||||
FROM `resource_history`
|
||||
WHERE `namespace` = 'ns'
|
||||
AND `group` = 'gp'
|
||||
AND `resource` = 'rs'
|
||||
AND `name` = 'nm'
|
||||
AND `action` = 'DELETED'
|
||||
LIMIT 1
|
||||
;
|
||||
Vendored
Executable
+9
@@ -0,0 +1,9 @@
|
||||
SELECT
|
||||
COALESCE(MAX("resource_version"), 0) AS "resource_version"
|
||||
FROM "resource_history"
|
||||
WHERE "namespace" = 'ns'
|
||||
AND "group" = 'gp'
|
||||
AND "resource" = 'rs'
|
||||
AND "name" = 'nm'
|
||||
LIMIT 1
|
||||
;
|
||||
Vendored
Executable
+10
@@ -0,0 +1,10 @@
|
||||
SELECT
|
||||
COALESCE(MAX("resource_version"), 0) AS "resource_version"
|
||||
FROM "resource_history"
|
||||
WHERE "namespace" = 'ns'
|
||||
AND "group" = 'gp'
|
||||
AND "resource" = 'rs'
|
||||
AND "name" = 'nm'
|
||||
AND "action" = 'DELETED'
|
||||
LIMIT 1
|
||||
;
|
||||
Vendored
Executable
+9
@@ -0,0 +1,9 @@
|
||||
SELECT
|
||||
COALESCE(MAX("resource_version"), 0) AS "resource_version"
|
||||
FROM "resource_history"
|
||||
WHERE "namespace" = 'ns'
|
||||
AND "group" = 'gp'
|
||||
AND "resource" = 'rs'
|
||||
AND "name" = 'nm'
|
||||
LIMIT 1
|
||||
;
|
||||
pkg/storage/unified/sql/testdata/sqlite--resource_history_read_latest_rv-with WatchEvent_DELETED.sql
Vendored
Executable
+10
@@ -0,0 +1,10 @@
|
||||
SELECT
|
||||
COALESCE(MAX("resource_version"), 0) AS "resource_version"
|
||||
FROM "resource_history"
|
||||
WHERE "namespace" = 'ns'
|
||||
AND "group" = 'gp'
|
||||
AND "resource" = 'rs'
|
||||
AND "name" = 'nm'
|
||||
AND "action" = 'DELETED'
|
||||
LIMIT 1
|
||||
;
|
||||
Reference in New Issue
Block a user