Storage: fix dummy implementation and tests (#58019)

This commit is contained in:
Ryan McKinley 2022-11-01 17:33:36 -07:00 committed by GitHub
parent c43e97cf37
commit 0c3ed0219e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 40 deletions

View File

@ -101,7 +101,8 @@ func (i *dummyObjectServer) findObject(ctx context.Context, grn *object.GRN, ver
}
func (i *dummyObjectServer) Read(ctx context.Context, r *object.ReadObjectRequest) (*object.ReadObjectResponse, error) {
_, objVersion, err := i.findObject(ctx, r.GRN, r.Version)
grn := getFullGRN(ctx, r.GRN)
_, objVersion, err := i.findObject(ctx, grn, r.Version)
if err != nil {
return nil, err
}
@ -175,7 +176,7 @@ func (i *dummyObjectServer) update(ctx context.Context, r *object.WriteObjectReq
GRN: r.GRN,
Created: i.Object.Created,
CreatedBy: i.Object.CreatedBy,
Updated: time.Now().Unix(),
Updated: time.Now().UnixMilli(),
UpdatedBy: store.GetUserIDString(modifier),
Size: int64(len(r.Body)),
ETag: createContentsHash(r.Body),
@ -225,8 +226,8 @@ func (i *dummyObjectServer) insert(ctx context.Context, r *object.WriteObjectReq
modifier := store.GetUserIDString(store.UserFromContext(ctx))
rawObj := &object.RawObject{
GRN: r.GRN,
Updated: time.Now().Unix(),
Created: time.Now().Unix(),
Updated: time.Now().UnixMilli(),
Created: time.Now().UnixMilli(),
CreatedBy: modifier,
UpdatedBy: modifier,
Size: int64(len(r.Body)),
@ -265,12 +266,13 @@ func (i *dummyObjectServer) insert(ctx context.Context, r *object.WriteObjectReq
}
func (i *dummyObjectServer) Write(ctx context.Context, r *object.WriteObjectRequest) (*object.WriteObjectResponse, error) {
namespace := namespaceFromUID(r.GRN)
grn := getFullGRN(ctx, r.GRN)
namespace := namespaceFromUID(grn)
obj, err := i.collection.FindFirst(ctx, namespace, func(i *RawObjectWithHistory) (bool, error) {
if i == nil || r == nil {
return false, nil
}
return r.GRN.Equals(i.Object.GRN), nil
return grn.Equals(i.Object.GRN), nil
})
if err != nil {
return nil, err
@ -284,8 +286,9 @@ func (i *dummyObjectServer) Write(ctx context.Context, r *object.WriteObjectRequ
}
func (i *dummyObjectServer) Delete(ctx context.Context, r *object.DeleteObjectRequest) (*object.DeleteObjectResponse, error) {
_, err := i.collection.Delete(ctx, namespaceFromUID(r.GRN), func(i *RawObjectWithHistory) (bool, error) {
if r.GRN.Equals(i.Object.GRN) {
grn := getFullGRN(ctx, r.GRN)
_, err := i.collection.Delete(ctx, namespaceFromUID(grn), func(i *RawObjectWithHistory) (bool, error) {
if grn.Equals(i.Object.GRN) {
if r.PreviousVersion != "" && i.Object.Version != r.PreviousVersion {
return false, fmt.Errorf("expected the previous version to be %s, but was %s", r.PreviousVersion, i.Object.Version)
}
@ -306,7 +309,8 @@ func (i *dummyObjectServer) Delete(ctx context.Context, r *object.DeleteObjectRe
}
func (i *dummyObjectServer) History(ctx context.Context, r *object.ObjectHistoryRequest) (*object.ObjectHistoryResponse, error) {
obj, _, err := i.findObject(ctx, r.GRN, "")
grn := getFullGRN(ctx, r.GRN)
obj, _, err := i.findObject(ctx, grn, "")
if err != nil {
return nil, err
}
@ -370,3 +374,12 @@ func (i *dummyObjectServer) Search(ctx context.Context, r *object.ObjectSearchRe
Results: searchResults,
}, nil
}
// This sets the TenantId on the request GRN
func getFullGRN(ctx context.Context, grn *object.GRN) *object.GRN {
if grn.TenantId == 0 {
modifier := store.UserFromContext(ctx)
grn.TenantId = modifier.OrgID
}
return grn
}

View File

@ -6,6 +6,7 @@ import (
apikeygenprefix "github.com/grafana/grafana/pkg/components/apikeygenprefixed"
"github.com/grafana/grafana/pkg/server"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org"
saAPI "github.com/grafana/grafana/pkg/services/serviceaccounts/api"
saTests "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
@ -60,9 +61,14 @@ func createTestContext(t *testing.T) testContext {
t.Helper()
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
EnableFeatureToggles: []string{"grpcServer"},
GRPCServerAddress: "127.0.0.1:0", // :0 for choosing the port automatically
EnableFeatureToggles: []string{
featuremgmt.FlagGrpcServer,
featuremgmt.FlagObjectStore,
},
AppModeProduction: false, // required for migrations to run
GRPCServerAddress: "127.0.0.1:0", // :0 for choosing the port automatically
})
_, env := testinfra.StartGrafanaEnv(t, dir, path)
authToken, serviceAccountUser := createServiceAccountAdminToken(t, env)

View File

@ -1,8 +1,7 @@
package object_server_tests
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"reflect"
"testing"
@ -10,15 +9,11 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/store/object"
"github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
)
func createContentsHash(contents []byte) string {
hash := md5.Sum(contents)
return hex.EncodeToString(hash[:])
}
type rawObjectMatcher struct {
grn *object.GRN
createdRange []time.Time
@ -38,7 +33,9 @@ type objectVersionMatcher struct {
}
func timestampInRange(ts int64, tsRange []time.Time) bool {
return ts >= tsRange[0].Unix() && ts <= tsRange[1].Unix()
low := tsRange[0].UnixMilli() - 1
high := tsRange[1].UnixMilli() + 1
return ts >= low && ts <= high
}
func requireObjectMatch(t *testing.T, obj *object.RawObject, m rawObjectMatcher) {
@ -46,16 +43,27 @@ func requireObjectMatch(t *testing.T, obj *object.RawObject, m rawObjectMatcher)
require.NotNil(t, obj)
mismatches := ""
if m.grn != nil && !obj.GRN.Equals(m.grn) {
mismatches += fmt.Sprintf("expected: %v, actual: %v\n", m.grn, obj.GRN)
if m.grn != nil {
if m.grn.TenantId > 0 && m.grn.TenantId != obj.GRN.TenantId {
mismatches += fmt.Sprintf("expected tenant: %d, actual: %d\n", m.grn.TenantId, obj.GRN.TenantId)
}
if m.grn.Scope != "" && m.grn.Scope != obj.GRN.Scope {
mismatches += fmt.Sprintf("expected Scope: %s, actual: %s\n", m.grn.Scope, obj.GRN.Scope)
}
if m.grn.Kind != "" && m.grn.Kind != obj.GRN.Kind {
mismatches += fmt.Sprintf("expected Kind: %s, actual: %s\n", m.grn.Kind, obj.GRN.Kind)
}
if m.grn.UID != "" && m.grn.UID != obj.GRN.UID {
mismatches += fmt.Sprintf("expected UID: %s, actual: %s\n", m.grn.UID, obj.GRN.UID)
}
}
if len(m.createdRange) == 2 && !timestampInRange(obj.Created, m.createdRange) {
mismatches += fmt.Sprintf("expected createdBy range: [from %s to %s], actual created: %s\n", m.createdRange[0], m.createdRange[1], time.Unix(obj.Created, 0))
mismatches += fmt.Sprintf("expected Created range: [from %s to %s], actual created: %s\n", m.createdRange[0], m.createdRange[1], time.UnixMilli(obj.Created))
}
if len(m.updatedRange) == 2 && !timestampInRange(obj.Updated, m.updatedRange) {
mismatches += fmt.Sprintf("expected updatedRange range: [from %s to %s], actual updated: %s\n", m.updatedRange[0], m.updatedRange[1], time.Unix(obj.Updated, 0))
mismatches += fmt.Sprintf("expected Updated range: [from %s to %s], actual updated: %s\n", m.updatedRange[0], m.updatedRange[1], time.UnixMilli(obj.Updated))
}
if m.createdBy != "" && m.createdBy != obj.CreatedBy {
@ -66,14 +74,12 @@ func requireObjectMatch(t *testing.T, obj *object.RawObject, m rawObjectMatcher)
mismatches += fmt.Sprintf("updatedBy: expected:%s, found:%s\n", m.updatedBy, obj.UpdatedBy)
}
if !reflect.DeepEqual(m.body, obj.Body) {
mismatches += fmt.Sprintf("expected body len: %d, actual body len: %d\n", len(m.body), len(obj.Body))
}
expectedHash := createContentsHash(m.body)
actualHash := createContentsHash(obj.Body)
if expectedHash != actualHash {
mismatches += fmt.Sprintf("expected body hash: %s, actual body hash: %s\n", expectedHash, actualHash)
if len(m.body) > 0 {
if json.Valid(m.body) {
require.JSONEq(t, string(m.body), string(obj.Body), "expecting same body")
} else if !reflect.DeepEqual(m.body, obj.Body) {
mismatches += fmt.Sprintf("expected body len: %d, actual body len: %d\n", len(m.body), len(obj.Body))
}
}
if m.version != nil && *m.version != obj.Version {
@ -92,7 +98,7 @@ func requireVersionMatch(t *testing.T, obj *object.ObjectVersionInfo, m objectVe
}
if len(m.updatedRange) == 2 && !timestampInRange(obj.Updated, m.updatedRange) {
mismatches += fmt.Sprintf("expected updatedRange range: [from %s to %s], actual updated: %s\n", m.updatedRange[0], m.updatedRange[1], time.Unix(obj.Updated, 0))
mismatches += fmt.Sprintf("expected updatedRange range: [from %s to %s], actual updated: %s\n", m.updatedRange[0], m.updatedRange[1], time.UnixMilli(obj.Updated))
}
if m.updatedBy != "" && m.updatedBy != obj.UpdatedBy {
@ -120,7 +126,7 @@ func TestObjectServer(t *testing.T) {
grn := &object.GRN{
Kind: kind,
UID: "my-test-entity",
Scope: "entity",
Scope: models.ObjectStoreScopeEntity,
}
body := []byte("{\"name\":\"John\"}")
@ -159,10 +165,11 @@ func TestObjectServer(t *testing.T) {
})
require.NoError(t, err)
require.Nil(t, readResp.SummaryJson)
require.NotNil(t, readResp.Object)
foundGRN := readResp.Object.GRN
require.NotNil(t, foundGRN)
require.NotEqual(t, testCtx.user.OrgID, foundGRN.TenantId) // orgId becomes the tenant id when not set
require.Equal(t, testCtx.user.OrgID, foundGRN.TenantId) // orgId becomes the tenant id when not set
require.Equal(t, grn.Scope, foundGRN.Scope)
require.Equal(t, grn.Kind, foundGRN.Kind)
require.Equal(t, grn.UID, foundGRN.UID)
@ -196,6 +203,12 @@ func TestObjectServer(t *testing.T) {
t.Run("should be able to update an object", func(t *testing.T) {
before := time.Now()
grn := &object.GRN{
Kind: kind,
UID: util.GenerateShortUID(),
Scope: models.ObjectStoreScopeEntity,
}
writeReq1 := &object.WriteObjectRequest{
GRN: grn,
Body: body,
@ -302,8 +315,9 @@ func TestObjectServer(t *testing.T) {
w2, err := testCtx.client.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: uid2,
Kind: kind,
UID: uid2,
Kind: kind,
Scope: grn.Scope,
},
Body: body,
})
@ -311,8 +325,9 @@ func TestObjectServer(t *testing.T) {
w3, err := testCtx.client.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: uid3,
Kind: kind2,
UID: uid3,
Kind: kind2,
Scope: grn.Scope,
},
Body: body,
})
@ -320,8 +335,9 @@ func TestObjectServer(t *testing.T) {
w4, err := testCtx.client.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: uid4,
Kind: kind2,
UID: uid4,
Kind: kind2,
Scope: grn.Scope,
},
Body: body,
})