mirror of
https://github.com/grafana/grafana.git
synced 2025-08-06 03:09:26 +08:00
K8s: Get trash fixes (#106411)
This commit is contained in:

committed by
GitHub

parent
8fdf86e56f
commit
7864c1660f
@ -780,7 +780,14 @@ func (b *backend) getHistory(ctx context.Context, req *resourcepb.ListRequest, c
|
||||
listReq.MinRV = latestDeletedRV + 1
|
||||
}
|
||||
|
||||
rows, err := dbutil.QueryRows(ctx, tx, sqlResourceHistoryGet, listReq)
|
||||
var rows db.Rows
|
||||
if listReq.Trash {
|
||||
// unlike history, trash will not return an object if an object of the same name is live
|
||||
// (i.e. in the resource table)
|
||||
rows, err = dbutil.QueryRows(ctx, tx, sqlResourceTrash, listReq)
|
||||
} else {
|
||||
rows, err = dbutil.QueryRows(ctx, tx, sqlResourceHistoryGet, listReq)
|
||||
}
|
||||
if rows != nil {
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
|
@ -15,9 +15,6 @@ WHERE 1 = 1
|
||||
{{ if .Key.Name }}
|
||||
AND {{ .Ident "name" }} = {{ .Arg .Key.Name }}
|
||||
{{ end }}
|
||||
{{ if .Trash }}
|
||||
AND {{ .Ident "action" }} = 3
|
||||
{{ end }}
|
||||
{{ if (gt .StartRV 0) }}
|
||||
{{ if .SortAscending }}
|
||||
AND {{ .Ident "resource_version" }} > {{ .Arg .StartRV }}
|
||||
|
54
pkg/storage/unified/sql/data/resource_trash.sql
Normal file
54
pkg/storage/unified/sql/data/resource_trash.sql
Normal file
@ -0,0 +1,54 @@
|
||||
SELECT
|
||||
h.{{ .Ident "guid" }},
|
||||
h.{{ .Ident "resource_version" }},
|
||||
h.{{ .Ident "namespace" }},
|
||||
h.{{ .Ident "group" }},
|
||||
h.{{ .Ident "resource" }},
|
||||
h.{{ .Ident "name" }},
|
||||
h.{{ .Ident "folder" }},
|
||||
h.{{ .Ident "value" }}
|
||||
FROM {{ .Ident "resource_history" }} h
|
||||
INNER JOIN (
|
||||
SELECT {{ .Ident "name" }}, MAX({{ .Ident "resource_version" }}) as max_rv
|
||||
FROM {{ .Ident "resource_history" }}
|
||||
WHERE 1 = 1
|
||||
AND {{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }}
|
||||
AND {{ .Ident "group" }} = {{ .Arg .Key.Group }}
|
||||
AND {{ .Ident "resource" }} = {{ .Arg .Key.Resource }}
|
||||
{{ if .Key.Name }}
|
||||
AND {{ .Ident "name" }} = {{ .Arg .Key.Name }}
|
||||
{{ end }}
|
||||
AND {{ .Ident "action" }} = 3
|
||||
{{ if (gt .StartRV 0) }}
|
||||
{{ if .SortAscending }}
|
||||
AND {{ .Ident "resource_version" }} > {{ .Arg .StartRV }}
|
||||
{{ else }}
|
||||
AND {{ .Ident "resource_version" }} < {{ .Arg .StartRV }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if (gt .MinRV 0) }}
|
||||
AND {{ .Ident "resource_version" }} >= {{ .Arg .MinRV }}
|
||||
{{ end }}
|
||||
{{ if (gt .ExactRV 0) }}
|
||||
AND {{ .Ident "resource_version" }} = {{ .Arg .ExactRV }}
|
||||
{{ end }}
|
||||
GROUP BY {{ .Ident "name" }}
|
||||
) max_versions ON h.{{ .Ident "name" }} = max_versions.{{ .Ident "name" }}
|
||||
AND h.{{ .Ident "resource_version" }} = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h.{{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }}
|
||||
AND h.{{ .Ident "group" }} = {{ .Arg .Key.Group }}
|
||||
AND h.{{ .Ident "resource" }} = {{ .Arg .Key.Resource }}
|
||||
AND h.{{ .Ident "action" }} = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM {{ .Ident "resource" }} r
|
||||
WHERE r.{{ .Ident "namespace" }} = h.{{ .Ident "namespace" }}
|
||||
AND r.{{ .Ident "group" }} = h.{{ .Ident "group" }}
|
||||
AND r.{{ .Ident "resource" }} = h.{{ .Ident "resource" }}
|
||||
AND r.{{ .Ident "name" }} = h.{{ .Ident "name" }}
|
||||
)
|
||||
{{ if .SortAscending }}
|
||||
ORDER BY h.{{ .Ident "resource_version" }} ASC
|
||||
{{ else }}
|
||||
ORDER BY h.{{ .Ident "resource_version" }} DESC
|
||||
{{ end }}
|
@ -46,6 +46,7 @@ var (
|
||||
sqlResourceHistoryGet = mustTemplate("resource_history_get.sql")
|
||||
sqlResourceHistoryDelete = mustTemplate("resource_history_delete.sql")
|
||||
sqlResourceHistoryPrune = mustTemplate("resource_history_prune.sql")
|
||||
sqlResourceTrash = mustTemplate("resource_trash.sql")
|
||||
sqlResourceInsertFromHistory = mustTemplate("resource_insert_from_history.sql")
|
||||
|
||||
// sqlResourceLabelsInsert = mustTemplate("resource_labels_insert.sql")
|
||||
|
@ -245,6 +245,9 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
sqlResourceTrash: {
|
||||
{
|
||||
Name: "read trash",
|
||||
Data: &sqlGetHistoryRequest{
|
||||
|
35
pkg/storage/unified/sql/testdata/mysql--resource_trash-read trash second page.sql
vendored
Executable file
35
pkg/storage/unified/sql/testdata/mysql--resource_trash-read trash second page.sql
vendored
Executable file
@ -0,0 +1,35 @@
|
||||
SELECT
|
||||
h.`guid`,
|
||||
h.`resource_version`,
|
||||
h.`namespace`,
|
||||
h.`group`,
|
||||
h.`resource`,
|
||||
h.`name`,
|
||||
h.`folder`,
|
||||
h.`value`
|
||||
FROM `resource_history` h
|
||||
INNER JOIN (
|
||||
SELECT `name`, MAX(`resource_version`) as max_rv
|
||||
FROM `resource_history`
|
||||
WHERE 1 = 1
|
||||
AND `namespace` = 'nn'
|
||||
AND `group` = 'gg'
|
||||
AND `resource` = 'rr'
|
||||
AND `action` = 3
|
||||
AND `resource_version` < 123456
|
||||
GROUP BY `name`
|
||||
) max_versions ON h.`name` = max_versions.`name`
|
||||
AND h.`resource_version` = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h.`namespace` = 'nn'
|
||||
AND h.`group` = 'gg'
|
||||
AND h.`resource` = 'rr'
|
||||
AND h.`action` = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `resource` r
|
||||
WHERE r.`namespace` = h.`namespace`
|
||||
AND r.`group` = h.`group`
|
||||
AND r.`resource` = h.`resource`
|
||||
AND r.`name` = h.`name`
|
||||
)
|
||||
ORDER BY h.`resource_version` DESC
|
34
pkg/storage/unified/sql/testdata/mysql--resource_trash-read trash.sql
vendored
Executable file
34
pkg/storage/unified/sql/testdata/mysql--resource_trash-read trash.sql
vendored
Executable file
@ -0,0 +1,34 @@
|
||||
SELECT
|
||||
h.`guid`,
|
||||
h.`resource_version`,
|
||||
h.`namespace`,
|
||||
h.`group`,
|
||||
h.`resource`,
|
||||
h.`name`,
|
||||
h.`folder`,
|
||||
h.`value`
|
||||
FROM `resource_history` h
|
||||
INNER JOIN (
|
||||
SELECT `name`, MAX(`resource_version`) as max_rv
|
||||
FROM `resource_history`
|
||||
WHERE 1 = 1
|
||||
AND `namespace` = 'nn'
|
||||
AND `group` = 'gg'
|
||||
AND `resource` = 'rr'
|
||||
AND `action` = 3
|
||||
GROUP BY `name`
|
||||
) max_versions ON h.`name` = max_versions.`name`
|
||||
AND h.`resource_version` = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h.`namespace` = 'nn'
|
||||
AND h.`group` = 'gg'
|
||||
AND h.`resource` = 'rr'
|
||||
AND h.`action` = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `resource` r
|
||||
WHERE r.`namespace` = h.`namespace`
|
||||
AND r.`group` = h.`group`
|
||||
AND r.`resource` = h.`resource`
|
||||
AND r.`name` = h.`name`
|
||||
)
|
||||
ORDER BY h.`resource_version` DESC
|
35
pkg/storage/unified/sql/testdata/postgres--resource_trash-read trash second page.sql
vendored
Executable file
35
pkg/storage/unified/sql/testdata/postgres--resource_trash-read trash second page.sql
vendored
Executable file
@ -0,0 +1,35 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
AND "resource_version" < 123456
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
34
pkg/storage/unified/sql/testdata/postgres--resource_trash-read trash.sql
vendored
Executable file
34
pkg/storage/unified/sql/testdata/postgres--resource_trash-read trash.sql
vendored
Executable file
@ -0,0 +1,34 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
35
pkg/storage/unified/sql/testdata/sqlite--resource_trash-read trash second page.sql
vendored
Executable file
35
pkg/storage/unified/sql/testdata/sqlite--resource_trash-read trash second page.sql
vendored
Executable file
@ -0,0 +1,35 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
AND "resource_version" < 123456
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
34
pkg/storage/unified/sql/testdata/sqlite--resource_trash-read trash.sql
vendored
Executable file
34
pkg/storage/unified/sql/testdata/sqlite--resource_trash-read trash.sql
vendored
Executable file
@ -0,0 +1,34 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
@ -35,6 +35,7 @@ const (
|
||||
TestGetResourceStats = "get resource stats"
|
||||
TestListHistory = "list history"
|
||||
TestListHistoryErrorReporting = "list history error reporting"
|
||||
TestListTrash = "list trash"
|
||||
TestCreateNewResource = "create new resource"
|
||||
)
|
||||
|
||||
@ -79,6 +80,7 @@ func RunStorageBackendTest(t *testing.T, newBackend NewBackendFunc, opts *TestOp
|
||||
{TestGetResourceStats, runTestIntegrationBackendGetResourceStats},
|
||||
{TestListHistory, runTestIntegrationBackendListHistory},
|
||||
{TestListHistoryErrorReporting, runTestIntegrationBackendListHistoryErrorReporting},
|
||||
{TestListTrash, runTestIntegrationBackendTrash},
|
||||
{TestCreateNewResource, runTestIntegrationBackendCreateNewResource},
|
||||
}
|
||||
|
||||
@ -1128,3 +1130,95 @@ func newServer(t *testing.T, b resource.StorageBackend) resource.ResourceServer
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func runTestIntegrationBackendTrash(t *testing.T, backend resource.StorageBackend, nsPrefix string) {
|
||||
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
|
||||
server := newServer(t, backend)
|
||||
ns := nsPrefix + "-ns-trash"
|
||||
|
||||
// item1 deleted with multiple history events
|
||||
rv1, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv1, int64(0))
|
||||
rvDelete1, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDelete1, rv1)
|
||||
rvDelete2, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDelete2, rvDelete1)
|
||||
|
||||
// item2 deleted and recreated, should not be returned in trash
|
||||
rv2, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv2, int64(0))
|
||||
rvDelete3, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDelete3, rv2)
|
||||
rv3, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv3, int64(0))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request *resourcepb.ListRequest
|
||||
expectedVersions []int64
|
||||
expectedValues []string
|
||||
minExpectedHeadRV int64
|
||||
expectedContinueRV int64
|
||||
expectedSortAsc bool
|
||||
}{
|
||||
{
|
||||
name: "returns the latest delete event",
|
||||
request: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_TRASH,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "item1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedVersions: []int64{rvDelete2},
|
||||
expectedValues: []string{"item1 DELETED"},
|
||||
minExpectedHeadRV: rvDelete2,
|
||||
expectedContinueRV: rvDelete2,
|
||||
expectedSortAsc: false,
|
||||
},
|
||||
{
|
||||
name: "does not return a version in the resource table",
|
||||
request: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_TRASH,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "item2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedVersions: []int64{},
|
||||
expectedValues: []string{},
|
||||
minExpectedHeadRV: rv3,
|
||||
expectedContinueRV: rv3,
|
||||
expectedSortAsc: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, err := server.List(ctx, tc.request)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, res.Error)
|
||||
expectedItemCount := len(tc.expectedVersions)
|
||||
require.Len(t, res.Items, expectedItemCount)
|
||||
for i := 0; i < expectedItemCount; i++ {
|
||||
require.Equal(t, tc.expectedVersions[i], res.Items[i].ResourceVersion)
|
||||
require.Contains(t, string(res.Items[i].Value), tc.expectedValues[i])
|
||||
}
|
||||
require.GreaterOrEqual(t, res.ResourceVersion, tc.minExpectedHeadRV)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user