refactor(unified-storage): move generated protos to own pkg (#105356)
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type LargeObjectSupportFake struct {
|
||||
@@ -28,12 +28,12 @@ func (s *LargeObjectSupportFake) MaxSize() int {
|
||||
return 10 * 1024 * 1024
|
||||
}
|
||||
|
||||
func (s *LargeObjectSupportFake) Deconstruct(ctx context.Context, key *resource.ResourceKey, client resource.BlobStoreClient, obj utils.GrafanaMetaAccessor, raw []byte) error {
|
||||
func (s *LargeObjectSupportFake) Deconstruct(_ context.Context, _ *resourcepb.ResourceKey, _ resourcepb.BlobStoreClient, _ utils.GrafanaMetaAccessor, _ []byte) error {
|
||||
s.deconstructed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LargeObjectSupportFake) Reconstruct(ctx context.Context, key *resource.ResourceKey, client resource.BlobStoreClient, obj utils.GrafanaMetaAccessor) error {
|
||||
func (s *LargeObjectSupportFake) Reconstruct(_ context.Context, _ *resourcepb.ResourceKey, _ resourcepb.BlobStoreClient, _ utils.GrafanaMetaAccessor) error {
|
||||
s.reconstructed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ require (
|
||||
github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250422074709-7c8433fbb2c2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
gocloud.dev v0.40.0
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/grpc v1.72.1
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/apiserver v0.32.3
|
||||
k8s.io/client-go v0.32.3
|
||||
|
||||
@@ -869,8 +869,7 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type LargeObjectSupport interface {
|
||||
@@ -27,11 +27,11 @@ type LargeObjectSupport interface {
|
||||
|
||||
// Deconstruct takes a large object, write most of it to blob storage and leave a few metadata bits around to help with list
|
||||
// NOTE: changes to the object must be handled by mutating the input obj
|
||||
Deconstruct(ctx context.Context, key *resource.ResourceKey, client resource.BlobStoreClient, obj utils.GrafanaMetaAccessor, raw []byte) error
|
||||
Deconstruct(ctx context.Context, key *resourcepb.ResourceKey, client resourcepb.BlobStoreClient, obj utils.GrafanaMetaAccessor, raw []byte) error
|
||||
|
||||
// Reconstruct will join the resource+blob back into a complete resource
|
||||
// NOTE: changes to the object must be handled by mutating the input obj
|
||||
Reconstruct(ctx context.Context, key *resource.ResourceKey, client resource.BlobStoreClient, obj utils.GrafanaMetaAccessor) error
|
||||
Reconstruct(ctx context.Context, key *resourcepb.ResourceKey, client resourcepb.BlobStoreClient, obj utils.GrafanaMetaAccessor) error
|
||||
}
|
||||
|
||||
var _ LargeObjectSupport = (*BasicLargeObjectSupport)(nil)
|
||||
@@ -64,7 +64,7 @@ func (s *BasicLargeObjectSupport) MaxSize() int {
|
||||
}
|
||||
|
||||
// Deconstruct implements LargeObjectSupport.
|
||||
func (s *BasicLargeObjectSupport) Deconstruct(ctx context.Context, key *resource.ResourceKey, client resource.BlobStoreClient, obj utils.GrafanaMetaAccessor, raw []byte) error {
|
||||
func (s *BasicLargeObjectSupport) Deconstruct(ctx context.Context, key *resourcepb.ResourceKey, client resourcepb.BlobStoreClient, obj utils.GrafanaMetaAccessor, raw []byte) error {
|
||||
if key.Group != s.TheGroupResource.Group {
|
||||
return fmt.Errorf("requested group mismatch")
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func (s *BasicLargeObjectSupport) Deconstruct(ctx context.Context, key *resource
|
||||
}
|
||||
|
||||
// Save the blob
|
||||
info, err := client.PutBlob(ctx, &resource.PutBlobRequest{
|
||||
info, err := client.PutBlob(ctx, &resourcepb.PutBlobRequest{
|
||||
ContentType: "application/json",
|
||||
Value: val,
|
||||
Resource: key,
|
||||
@@ -125,7 +125,7 @@ func (s *BasicLargeObjectSupport) Deconstruct(ctx context.Context, key *resource
|
||||
}
|
||||
|
||||
// Reconstruct implements LargeObjectSupport.
|
||||
func (s *BasicLargeObjectSupport) Reconstruct(ctx context.Context, key *resource.ResourceKey, client resource.BlobStoreClient, obj utils.GrafanaMetaAccessor) error {
|
||||
func (s *BasicLargeObjectSupport) Reconstruct(ctx context.Context, key *resourcepb.ResourceKey, client resourcepb.BlobStoreClient, obj utils.GrafanaMetaAccessor) error {
|
||||
blobInfo := obj.GetBlob()
|
||||
if blobInfo == nil {
|
||||
return fmt.Errorf("the object does not have a blob")
|
||||
@@ -135,8 +135,8 @@ func (s *BasicLargeObjectSupport) Reconstruct(ctx context.Context, key *resource
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rsp, err := client.GetBlob(ctx, &resource.GetBlobRequest{
|
||||
Resource: &resource.ResourceKey{
|
||||
rsp, err := client.GetBlob(ctx, &resourcepb.GetBlobRequest{
|
||||
Resource: &resourcepb.ResourceKey{
|
||||
Group: s.TheGroupResource.Group,
|
||||
Resource: s.TheGroupResource.Resource,
|
||||
Namespace: obj.GetNamespace(),
|
||||
|
||||
@@ -16,8 +16,9 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
authtypes "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var errResourceIsManagedInRepository = fmt.Errorf("this resource is managed by a repository")
|
||||
@@ -97,7 +98,7 @@ func enforceManagerProperties(auth authtypes.AuthInfo, obj utils.GrafanaMetaAcce
|
||||
|
||||
func (s *Storage) handleManagedResourceRouting(ctx context.Context,
|
||||
err error,
|
||||
action resource.WatchEvent_Type,
|
||||
action resourcepb.WatchEvent_Type,
|
||||
key string,
|
||||
orig runtime.Object,
|
||||
rsp runtime.Object,
|
||||
@@ -137,7 +138,7 @@ func (s *Storage) handleManagedResourceRouting(ctx context.Context,
|
||||
return err
|
||||
}
|
||||
|
||||
if action == resource.WatchEvent_DELETED {
|
||||
if action == resourcepb.WatchEvent_DELETED {
|
||||
// TODO? can we copy orig into rsp without a full get?
|
||||
if err = s.Get(ctx, key, storage.GetOptions{}, rsp); err != nil { // COPY?
|
||||
return err
|
||||
@@ -153,9 +154,9 @@ func (s *Storage) handleManagedResourceRouting(ctx context.Context,
|
||||
|
||||
var req *rest.Request
|
||||
switch action {
|
||||
case resource.WatchEvent_ADDED:
|
||||
case resourcepb.WatchEvent_ADDED:
|
||||
req = client.Post()
|
||||
case resource.WatchEvent_MODIFIED:
|
||||
case resourcepb.WatchEvent_MODIFIED:
|
||||
req = client.Put()
|
||||
default:
|
||||
return fmt.Errorf("unsupported provisioning action: %v, %w", action, err)
|
||||
|
||||
@@ -8,14 +8,15 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
authtypes "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type permissionCreatorFunc = func(ctx context.Context) error
|
||||
|
||||
func afterCreatePermissionCreator(ctx context.Context,
|
||||
key *resource.ResourceKey,
|
||||
key *resourcepb.ResourceKey,
|
||||
grantPermisions string,
|
||||
obj runtime.Object,
|
||||
setter DefaultPermissionSetter,
|
||||
|
||||
@@ -8,14 +8,15 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
authtypes "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestAfterCreatePermissionCreator(t *testing.T) {
|
||||
mockSetter := func(ctx context.Context, key *resource.ResourceKey, auth authtypes.AuthInfo, val utils.GrafanaMetaAccessor) error {
|
||||
mockSetter := func(ctx context.Context, key *resourcepb.ResourceKey, auth authtypes.AuthInfo, val utils.GrafanaMetaAccessor) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ func TestAfterCreatePermissionCreator(t *testing.T) {
|
||||
UserID: 1,
|
||||
})
|
||||
obj := &v0alpha1.Dashboard{}
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "test",
|
||||
@@ -92,7 +93,7 @@ func TestAfterCreatePermissionCreator(t *testing.T) {
|
||||
UserID: 1,
|
||||
})
|
||||
obj := &v0alpha1.Dashboard{}
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "test",
|
||||
|
||||
@@ -17,8 +17,9 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
authtypes "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func logN(n, b float64) float64 {
|
||||
@@ -197,7 +198,7 @@ func (s *Storage) handleLargeResources(ctx context.Context, obj utils.GrafanaMet
|
||||
return nil, fmt.Errorf("request object is too big (%s > %s)", formatBytes(size), formatBytes(support.MaxSize()))
|
||||
}
|
||||
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: s.gr.Group,
|
||||
Resource: s.gr.Resource,
|
||||
Namespace: obj.GetNamespace(),
|
||||
|
||||
@@ -32,10 +32,12 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
authtypes "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,7 +48,7 @@ const (
|
||||
|
||||
var _ storage.Interface = (*Storage)(nil)
|
||||
|
||||
type DefaultPermissionSetter = func(ctx context.Context, key *resource.ResourceKey, id authtypes.AuthInfo, obj utils.GrafanaMetaAccessor) error
|
||||
type DefaultPermissionSetter = func(ctx context.Context, key *resourcepb.ResourceKey, id authtypes.AuthInfo, obj utils.GrafanaMetaAccessor) error
|
||||
|
||||
// Optional settings that apply to a single resource
|
||||
type StorageOptions struct {
|
||||
@@ -76,7 +78,7 @@ type Storage struct {
|
||||
indexers *cache.Indexers
|
||||
|
||||
store resource.ResourceClient
|
||||
getKey func(string) (*resource.ResourceKey, error)
|
||||
getKey func(string) (*resourcepb.ResourceKey, error)
|
||||
snowflake *snowflake.Node // used to enforce internal ids
|
||||
configProvider RestConfigProvider // used for provisioning
|
||||
|
||||
@@ -101,7 +103,7 @@ func NewStorage(
|
||||
config *storagebackend.ConfigForResource,
|
||||
store resource.ResourceClient,
|
||||
keyFunc func(obj runtime.Object) (string, error),
|
||||
keyParser func(key string) (*resource.ResourceKey, error),
|
||||
keyParser func(key string) (*resourcepb.ResourceKey, error),
|
||||
newFunc func() runtime.Object,
|
||||
newListFunc func() runtime.Object,
|
||||
getAttrsFunc storage.AttrFunc,
|
||||
@@ -139,7 +141,7 @@ func NewStorage(
|
||||
|
||||
// The key parsing callback allows us to support the hardcoded paths from upstream tests
|
||||
if s.getKey == nil {
|
||||
s.getKey = func(key string) (*resource.ResourceKey, error) {
|
||||
s.getKey = func(key string) (*resourcepb.ResourceKey, error) {
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -150,7 +152,7 @@ func NewStorage(
|
||||
if k.Resource == "" {
|
||||
return nil, apierrors.NewInternalError(fmt.Errorf("missing resource in request"))
|
||||
}
|
||||
return &resource.ResourceKey{
|
||||
return &resourcepb.ResourceKey{
|
||||
Namespace: k.Namespace,
|
||||
Group: k.Group,
|
||||
Resource: k.Resource,
|
||||
@@ -177,10 +179,10 @@ func (s *Storage) convertToObject(data []byte, obj runtime.Object) (runtime.Obje
|
||||
func (s *Storage) Create(ctx context.Context, key string, obj runtime.Object, out runtime.Object, ttl uint64) error {
|
||||
var err error
|
||||
var permissions string
|
||||
req := &resource.CreateRequest{}
|
||||
req := &resourcepb.CreateRequest{}
|
||||
req.Value, permissions, err = s.prepareObjectForStorage(ctx, obj)
|
||||
if err != nil {
|
||||
return s.handleManagedResourceRouting(ctx, err, resource.WatchEvent_ADDED, key, obj, out)
|
||||
return s.handleManagedResourceRouting(ctx, err, resourcepb.WatchEvent_ADDED, key, obj, out)
|
||||
}
|
||||
|
||||
req.Key, err = s.getKey(key)
|
||||
@@ -258,7 +260,7 @@ func (s *Storage) Delete(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := &resource.DeleteRequest{Key: k}
|
||||
cmd := &resourcepb.DeleteRequest{Key: k}
|
||||
|
||||
if preconditions != nil {
|
||||
if err := preconditions.Check(key, out); err != nil {
|
||||
@@ -287,7 +289,7 @@ func (s *Storage) Delete(
|
||||
return fmt.Errorf("unable to read object %w", err)
|
||||
}
|
||||
if err = checkManagerPropertiesOnDelete(info, meta); err != nil {
|
||||
return s.handleManagedResourceRouting(ctx, err, resource.WatchEvent_DELETED, key, out, out)
|
||||
return s.handleManagedResourceRouting(ctx, err, resourcepb.WatchEvent_DELETED, key, out, out)
|
||||
}
|
||||
|
||||
rsp, err := s.store.Delete(ctx, cmd)
|
||||
@@ -315,7 +317,7 @@ func (s *Storage) Watch(ctx context.Context, key string, opts storage.ListOption
|
||||
return watch.NewEmptyWatch(), nil
|
||||
}
|
||||
|
||||
cmd := &resource.WatchRequest{
|
||||
cmd := &resourcepb.WatchRequest{
|
||||
Since: req.ResourceVersion,
|
||||
Options: req.Options,
|
||||
SendInitialEvents: false,
|
||||
@@ -349,7 +351,7 @@ func (s *Storage) Watch(ctx context.Context, key string, opts storage.ListOption
|
||||
// match 'opts.ResourceVersion' according 'opts.ResourceVersionMatch'.
|
||||
func (s *Storage) Get(ctx context.Context, key string, opts storage.GetOptions, objPtr runtime.Object) error {
|
||||
var err error
|
||||
req := &resource.ReadRequest{}
|
||||
req := &resourcepb.ReadRequest{}
|
||||
req.Key, err = s.getKey(key)
|
||||
if err != nil {
|
||||
if opts.IgnoreNotFound {
|
||||
@@ -497,7 +499,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
existingBytes []byte
|
||||
err error
|
||||
)
|
||||
req := &resource.UpdateRequest{}
|
||||
req := &resourcepb.UpdateRequest{}
|
||||
req.Key, err = s.getKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -511,7 +513,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
|
||||
for attempt := 1; attempt <= MaxUpdateAttempts; attempt = attempt + 1 {
|
||||
// Read the latest value
|
||||
readResponse, err := s.store.Read(ctx, &resource.ReadRequest{Key: req.Key})
|
||||
readResponse, err := s.store.Read(ctx, &resourcepb.ReadRequest{Key: req.Key})
|
||||
if err != nil {
|
||||
return resource.GetError(resource.AsErrorResult(err))
|
||||
}
|
||||
@@ -587,7 +589,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
|
||||
req.Value, err = s.prepareObjectForUpdate(ctx, updatedObj, existingObj)
|
||||
if err != nil {
|
||||
return s.handleManagedResourceRouting(ctx, err, resource.WatchEvent_MODIFIED, key, updatedObj, destination)
|
||||
return s.handleManagedResourceRouting(ctx, err, resourcepb.WatchEvent_MODIFIED, key, updatedObj, destination)
|
||||
}
|
||||
|
||||
var rv uint64
|
||||
|
||||
@@ -29,10 +29,11 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
storagetesting "github.com/grafana/grafana/pkg/apiserver/storage/testing"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -160,17 +161,17 @@ func TestDeleteWithSuggestionAndConflict(t *testing.T) {
|
||||
}
|
||||
|
||||
type resourceClientMock struct {
|
||||
resource.ResourceStoreClient
|
||||
resource.ResourceIndexClient
|
||||
resource.ManagedObjectIndexClient
|
||||
resource.BulkStoreClient
|
||||
resource.BlobStoreClient
|
||||
resource.DiagnosticsClient
|
||||
resourcepb.ResourceStoreClient
|
||||
resourcepb.ResourceIndexClient
|
||||
resourcepb.ManagedObjectIndexClient
|
||||
resourcepb.BulkStoreClient
|
||||
resourcepb.BlobStoreClient
|
||||
resourcepb.DiagnosticsClient
|
||||
}
|
||||
|
||||
// always return GRPC Unauthenticated code
|
||||
func (r resourceClientMock) List(ctx context.Context, in *resource.ListRequest, opts ...grpc.CallOption) (*resource.ListResponse, error) {
|
||||
return &resource.ListResponse{}, status.Error(codes.Unauthenticated, "missing token")
|
||||
func (r resourceClientMock) List(ctx context.Context, in *resourcepb.ListRequest, opts ...grpc.CallOption) (*resourcepb.ListResponse, error) {
|
||||
return &resourcepb.ListResponse{}, status.Error(codes.Unauthenticated, "missing token")
|
||||
}
|
||||
|
||||
func TestGRPCtoHTTPStatusMapping(t *testing.T) {
|
||||
|
||||
@@ -15,11 +15,11 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type streamDecoder struct {
|
||||
client resource.ResourceStore_WatchClient
|
||||
client resourcepb.ResourceStore_WatchClient
|
||||
newFunc func() runtime.Object
|
||||
predicate storage.SelectionPredicate
|
||||
codec runtime.Codec
|
||||
@@ -27,7 +27,7 @@ type streamDecoder struct {
|
||||
done sync.WaitGroup
|
||||
}
|
||||
|
||||
func newStreamDecoder(client resource.ResourceStore_WatchClient, newFunc func() runtime.Object, predicate storage.SelectionPredicate, codec runtime.Codec, cancelWatch context.CancelFunc) *streamDecoder {
|
||||
func newStreamDecoder(client resourcepb.ResourceStore_WatchClient, newFunc func() runtime.Object, predicate storage.SelectionPredicate, codec runtime.Codec, cancelWatch context.CancelFunc) *streamDecoder {
|
||||
return &streamDecoder{
|
||||
client: client,
|
||||
newFunc: newFunc,
|
||||
@@ -36,7 +36,7 @@ func newStreamDecoder(client resource.ResourceStore_WatchClient, newFunc func()
|
||||
cancelWatch: cancelWatch,
|
||||
}
|
||||
}
|
||||
func (d *streamDecoder) toObject(w *resource.WatchEvent_Resource) (runtime.Object, error) {
|
||||
func (d *streamDecoder) toObject(w *resourcepb.WatchEvent_Resource) (runtime.Object, error) {
|
||||
var obj runtime.Object
|
||||
var err error
|
||||
obj, _, err = d.codec.Decode(w.Value, nil, d.newFunc())
|
||||
@@ -56,7 +56,7 @@ func (d *streamDecoder) Decode() (action watch.EventType, object runtime.Object,
|
||||
defer d.done.Done()
|
||||
decode:
|
||||
for {
|
||||
var evt *resource.WatchEvent
|
||||
var evt *resourcepb.WatchEvent
|
||||
var err error
|
||||
select {
|
||||
case <-d.client.Context().Done():
|
||||
@@ -79,7 +79,7 @@ decode:
|
||||
}
|
||||
|
||||
// Error event
|
||||
if evt.Type == resource.WatchEvent_ERROR {
|
||||
if evt.Type == resourcepb.WatchEvent_ERROR {
|
||||
err = fmt.Errorf("stream error")
|
||||
klog.Errorf("client: error receiving result: %s", err)
|
||||
return watch.Error, nil, err
|
||||
@@ -90,7 +90,7 @@ decode:
|
||||
continue decode
|
||||
}
|
||||
|
||||
if evt.Type == resource.WatchEvent_BOOKMARK {
|
||||
if evt.Type == resourcepb.WatchEvent_BOOKMARK {
|
||||
obj := d.newFunc()
|
||||
|
||||
// here k8s expects an empty object with just resource version and k8s.io/initial-events-end annotation
|
||||
@@ -113,7 +113,7 @@ decode:
|
||||
|
||||
var watchAction watch.EventType
|
||||
switch evt.Type {
|
||||
case resource.WatchEvent_ADDED:
|
||||
case resourcepb.WatchEvent_ADDED:
|
||||
// apply any predicates not handled in storage
|
||||
matches, err := d.predicate.Matches(obj)
|
||||
if err != nil {
|
||||
@@ -125,7 +125,7 @@ decode:
|
||||
}
|
||||
|
||||
watchAction = watch.Added
|
||||
case resource.WatchEvent_MODIFIED:
|
||||
case resourcepb.WatchEvent_MODIFIED:
|
||||
watchAction = watch.Modified
|
||||
|
||||
// apply any predicates not handled in storage
|
||||
@@ -175,7 +175,7 @@ decode:
|
||||
// if the object didn't previously match, send an Added event
|
||||
watchAction = watch.Added
|
||||
}
|
||||
case resource.WatchEvent_DELETED:
|
||||
case resourcepb.WatchEvent_DELETED:
|
||||
watchAction = watch.Deleted
|
||||
|
||||
// if we have a previous object, return that in the deleted event
|
||||
|
||||
@@ -15,15 +15,14 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource.ListRequest, storage.SelectionPredicate, error) {
|
||||
func toListRequest(k *resourcepb.ResourceKey, opts storage.ListOptions) (*resourcepb.ListRequest, storage.SelectionPredicate, error) {
|
||||
predicate := opts.Predicate
|
||||
req := &resource.ListRequest{
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: opts.Predicate.Limit,
|
||||
Options: &resource.ListOptions{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: k,
|
||||
},
|
||||
NextPageToken: predicate.Continue,
|
||||
@@ -39,11 +38,11 @@ func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource
|
||||
|
||||
switch opts.ResourceVersionMatch {
|
||||
case "":
|
||||
req.VersionMatchV2 = resource.ResourceVersionMatchV2_Unset
|
||||
req.VersionMatchV2 = resourcepb.ResourceVersionMatchV2_Unset
|
||||
case metav1.ResourceVersionMatchNotOlderThan:
|
||||
req.VersionMatchV2 = resource.ResourceVersionMatchV2_NotOlderThan
|
||||
req.VersionMatchV2 = resourcepb.ResourceVersionMatchV2_NotOlderThan
|
||||
case metav1.ResourceVersionMatchExact:
|
||||
req.VersionMatchV2 = resource.ResourceVersionMatchV2_Exact
|
||||
req.VersionMatchV2 = resourcepb.ResourceVersionMatchV2_Exact
|
||||
default:
|
||||
return nil, predicate, apierrors.NewBadRequest(
|
||||
fmt.Sprintf("unsupported version match: %v", opts.ResourceVersionMatch),
|
||||
@@ -85,12 +84,12 @@ func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource
|
||||
|
||||
switch v {
|
||||
case utils.LabelKeyGetTrash:
|
||||
req.Source = resource.ListRequest_TRASH
|
||||
req.Source = resourcepb.ListRequest_TRASH
|
||||
if vals[0] != "true" {
|
||||
return nil, predicate, apierrors.NewBadRequest("expecting true for: " + v)
|
||||
}
|
||||
case utils.LabelKeyGetHistory:
|
||||
req.Source = resource.ListRequest_HISTORY
|
||||
req.Source = resourcepb.ListRequest_HISTORY
|
||||
req.Options.Key.Name = vals[0]
|
||||
}
|
||||
|
||||
@@ -99,7 +98,7 @@ func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource
|
||||
return req, storage.Everything, nil
|
||||
}
|
||||
|
||||
req.Options.Labels = append(req.Options.Labels, &resource.Requirement{
|
||||
req.Options.Labels = append(req.Options.Labels, &resourcepb.Requirement{
|
||||
Key: v,
|
||||
Operator: string(r.Operator()),
|
||||
Values: r.Values().List(),
|
||||
@@ -110,7 +109,7 @@ func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource
|
||||
if opts.Predicate.Field != nil && !opts.Predicate.Field.Empty() {
|
||||
requirements := opts.Predicate.Field.Requirements()
|
||||
for _, r := range requirements {
|
||||
requirement := &resource.Requirement{Key: r.Field, Operator: string(r.Operator)}
|
||||
requirement := &resourcepb.Requirement{Key: r.Field, Operator: string(r.Operator)}
|
||||
if r.Value != "" {
|
||||
requirement.Values = append(requirement.Values, r.Value)
|
||||
}
|
||||
|
||||
@@ -11,30 +11,30 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestToListRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key *resource.ResourceKey
|
||||
key *resourcepb.ResourceKey
|
||||
opts storage.ListOptions
|
||||
want *resource.ListRequest
|
||||
want *resourcepb.ListRequest
|
||||
wantPredicate storage.SelectionPredicate
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "basic list request",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
},
|
||||
opts: storage.ListOptions{},
|
||||
want: &resource.ListRequest{
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -46,7 +46,7 @@ func TestToListRequest(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with resource version",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -54,11 +54,11 @@ func TestToListRequest(t *testing.T) {
|
||||
opts: storage.ListOptions{
|
||||
ResourceVersion: "123",
|
||||
},
|
||||
want: &resource.ListRequest{
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
ResourceVersion: 123,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -70,7 +70,7 @@ func TestToListRequest(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid resource version",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -84,7 +84,7 @@ func TestToListRequest(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with label selector",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -94,15 +94,15 @@ func TestToListRequest(t *testing.T) {
|
||||
Label: labels.SelectorFromSet(labels.Set{"key": "value"}),
|
||||
},
|
||||
},
|
||||
want: &resource.ListRequest{
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
},
|
||||
Labels: []*resource.Requirement{
|
||||
Labels: []*resourcepb.Requirement{
|
||||
{
|
||||
Key: "key",
|
||||
Operator: string(selection.Equals),
|
||||
@@ -118,7 +118,7 @@ func TestToListRequest(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with trash label",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -128,13 +128,13 @@ func TestToListRequest(t *testing.T) {
|
||||
Label: labels.SelectorFromSet(labels.Set{utils.LabelKeyGetTrash: "true"}),
|
||||
},
|
||||
},
|
||||
want: &resource.ListRequest{
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
Source: resource.ListRequest_TRASH,
|
||||
Options: &resource.ListOptions{
|
||||
Source: resourcepb.ListRequest_TRASH,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Labels: nil,
|
||||
Fields: nil,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -146,7 +146,7 @@ func TestToListRequest(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with history label",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -156,13 +156,13 @@ func TestToListRequest(t *testing.T) {
|
||||
Label: labels.SelectorFromSet(labels.Set{utils.LabelKeyGetHistory: "test-name"}),
|
||||
},
|
||||
},
|
||||
want: &resource.ListRequest{
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Options: &resource.ListOptions{
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Labels: nil,
|
||||
Fields: nil,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -175,7 +175,7 @@ func TestToListRequest(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with fullpath label",
|
||||
key: &resource.ResourceKey{
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
@@ -185,12 +185,12 @@ func TestToListRequest(t *testing.T) {
|
||||
Label: labels.SelectorFromSet(labels.Set{utils.LabelGetFullpath: "true"}),
|
||||
},
|
||||
},
|
||||
want: &resource.ListRequest{
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
Options: &resource.ListOptions{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Labels: nil,
|
||||
Fields: nil,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db/dbimpl"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
@@ -130,7 +131,7 @@ func testSetup(t testing.TB, opts ...setupOption) (context.Context, storage.Inte
|
||||
require.NoError(t, err)
|
||||
|
||||
// Issue a health check to ensure the server is initialized
|
||||
_, err = server.IsHealthy(ctx, &resource.HealthCheckRequest{})
|
||||
_, err = server.IsHealthy(ctx, &resourcepb.HealthCheckRequest{})
|
||||
require.NoError(t, err)
|
||||
case StorageTypeUnified:
|
||||
if testing.Short() {
|
||||
@@ -377,7 +378,7 @@ func newPodList() runtime.Object {
|
||||
return &example.PodList{}
|
||||
}
|
||||
|
||||
func testKeyParser(val string) (*resource.ResourceKey, error) {
|
||||
func testKeyParser(val string) (*resourcepb.ResourceKey, error) {
|
||||
k, err := grafanaregistry.ParseKey(val)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(val, "pods/") {
|
||||
@@ -407,7 +408,7 @@ func testKeyParser(val string) (*resource.ResourceKey, error) {
|
||||
if k.Resource == "" {
|
||||
return nil, apierrors.NewInternalError(fmt.Errorf("missing resource in request"))
|
||||
}
|
||||
return &resource.ResourceKey{
|
||||
return &resourcepb.ResourceKey{
|
||||
Namespace: k.Namespace,
|
||||
Group: k.Group,
|
||||
Resource: k.Resource,
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func NewFederatedClient(base resource.ResourceClient, sql legacysql.LegacyDatabaseProvider) resource.ResourceClient {
|
||||
@@ -26,7 +27,7 @@ type federatedClient struct {
|
||||
}
|
||||
|
||||
// Get the resource stats
|
||||
func (s *federatedClient) GetStats(ctx context.Context, in *resource.ResourceStatsRequest, opts ...grpc.CallOption) (*resource.ResourceStatsResponse, error) {
|
||||
func (s *federatedClient) GetStats(ctx context.Context, in *resourcepb.ResourceStatsRequest, opts ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) {
|
||||
rsp, err := s.ResourceClient.GetStats(ctx, in, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/federated"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
)
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestDirectSQLStats(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = request.WithNamespace(ctx, "default")
|
||||
|
||||
stats, err := store.GetStats(ctx, &resource.ResourceStatsRequest{
|
||||
stats, err := store.GetStats(ctx, &resourcepb.ResourceStatsRequest{
|
||||
Namespace: "default",
|
||||
Folder: folder1UID,
|
||||
})
|
||||
@@ -147,7 +147,7 @@ func TestDirectSQLStats(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = request.WithNamespace(ctx, "default")
|
||||
|
||||
stats, err := store.GetStats(ctx, &resource.ResourceStatsRequest{
|
||||
stats, err := store.GetStats(ctx, &resourcepb.ResourceStatsRequest{
|
||||
Namespace: "default",
|
||||
Folder: folder2UID,
|
||||
})
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"fmt"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// Read stats from legacy SQL
|
||||
@@ -15,7 +16,7 @@ type LegacyStatsGetter struct {
|
||||
SQL legacysql.LegacyDatabaseProvider
|
||||
}
|
||||
|
||||
func (s *LegacyStatsGetter) GetStats(ctx context.Context, in *resource.ResourceStatsRequest) (*resource.ResourceStatsResponse, error) {
|
||||
func (s *LegacyStatsGetter) GetStats(ctx context.Context, in *resourcepb.ResourceStatsRequest) (*resourcepb.ResourceStatsResponse, error) {
|
||||
info, err := claims.ParseNamespace(in.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read namespace")
|
||||
@@ -29,7 +30,7 @@ func (s *LegacyStatsGetter) GetStats(ctx context.Context, in *resource.ResourceS
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rsp := &resource.ResourceStatsResponse{}
|
||||
rsp := &resourcepb.ResourceStatsResponse{}
|
||||
err = helper.DB.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
fn := func(table, where, g, r string, existCheck bool) error {
|
||||
// if existCheck is true, do not error out if the table does not exist
|
||||
@@ -46,7 +47,7 @@ func (s *LegacyStatsGetter) GetStats(ctx context.Context, in *resource.ResourceS
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rsp.Stats = append(rsp.Stats, &resource.ResourceStatsResponse_Stats{
|
||||
rsp.Stats = append(rsp.Stats, &resourcepb.ResourceStatsResponse_Stats{
|
||||
Group: g, // all legacy for now
|
||||
Resource: r,
|
||||
Count: count,
|
||||
|
||||
@@ -9,11 +9,12 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.BulkStoreClient = (*writerClient)(nil)
|
||||
_ resource.BulkStore_BulkProcessClient = (*writerClient)(nil)
|
||||
_ resourcepb.BulkStoreClient = (*writerClient)(nil)
|
||||
_ resourcepb.BulkStore_BulkProcessClient = (*writerClient)(nil)
|
||||
|
||||
errUnimplemented = errors.New("not implemented (BulkResourceWriter as BulkStoreClient shim)")
|
||||
)
|
||||
@@ -29,12 +30,12 @@ func NewBulkResourceWriterClient(writer resource.BulkResourceWriter) *writerClie
|
||||
}
|
||||
|
||||
// Send implements resource.ResourceStore_BulkProcessClient.
|
||||
func (w *writerClient) Send(req *resource.BulkRequest) error {
|
||||
func (w *writerClient) Send(req *resourcepb.BulkRequest) error {
|
||||
return w.writer.Write(w.ctx, req.Key, req.Value)
|
||||
}
|
||||
|
||||
// BulkProcess implements resource.ResourceStoreClient.
|
||||
func (w *writerClient) BulkProcess(ctx context.Context, opts ...grpc.CallOption) (resource.BulkStore_BulkProcessClient, error) {
|
||||
func (w *writerClient) BulkProcess(ctx context.Context, _ ...grpc.CallOption) (resourcepb.BulkStore_BulkProcessClient, error) {
|
||||
if w.ctx != nil {
|
||||
return nil, fmt.Errorf("only one batch request supported")
|
||||
}
|
||||
@@ -43,7 +44,7 @@ func (w *writerClient) BulkProcess(ctx context.Context, opts ...grpc.CallOption)
|
||||
}
|
||||
|
||||
// CloseAndRecv implements resource.ResourceStore_BulkProcessClient.
|
||||
func (w *writerClient) CloseAndRecv() (*resource.BulkResponse, error) {
|
||||
func (w *writerClient) CloseAndRecv() (*resourcepb.BulkResponse, error) {
|
||||
return w.writer.CloseWithResults()
|
||||
}
|
||||
|
||||
@@ -63,12 +64,12 @@ func (w *writerClient) Header() (metadata.MD, error) {
|
||||
}
|
||||
|
||||
// RecvMsg implements resource.ResourceStore_BulkProcessClient.
|
||||
func (w *writerClient) RecvMsg(m any) error {
|
||||
func (w *writerClient) RecvMsg(_ any) error {
|
||||
return errUnimplemented
|
||||
}
|
||||
|
||||
// SendMsg implements resource.ResourceStore_BulkProcessClient.
|
||||
func (w *writerClient) SendMsg(m any) error {
|
||||
func (w *writerClient) SendMsg(_ any) error {
|
||||
return errUnimplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/apache/arrow-go/v18/parquet/file"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -39,7 +40,7 @@ type parquetReader struct {
|
||||
bufferIndex int
|
||||
rowGroupIDX int
|
||||
|
||||
req *resource.BulkRequest
|
||||
req *resourcepb.BulkRequest
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -60,14 +61,14 @@ func (r *parquetReader) Next() bool {
|
||||
i := r.bufferIndex
|
||||
r.bufferIndex++
|
||||
|
||||
r.req = &resource.BulkRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
r.req = &resourcepb.BulkRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: r.group.buffer[i].String(),
|
||||
Resource: r.resource.buffer[i].String(),
|
||||
Namespace: r.namespace.buffer[i].String(),
|
||||
Name: r.name.buffer[i].String(),
|
||||
},
|
||||
Action: resource.BulkRequest_Action(r.action.buffer[i]),
|
||||
Action: resourcepb.BulkRequest_Action(r.action.buffer[i]),
|
||||
Value: r.value.buffer[i].Bytes(),
|
||||
Folder: r.folder.buffer[i].String(),
|
||||
}
|
||||
@@ -88,7 +89,7 @@ func (r *parquetReader) Next() bool {
|
||||
}
|
||||
|
||||
// Request implements resource.BulkRequestIterator.
|
||||
func (r *parquetReader) Request() *resource.BulkRequest {
|
||||
func (r *parquetReader) Request() *resourcepb.BulkRequest {
|
||||
return r.req
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestParquetWriteThenRead(t *testing.T) {
|
||||
@@ -75,7 +76,7 @@ func TestParquetWriteThenRead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
for reader.Next() {
|
||||
req := reader.Request()
|
||||
keys = append(keys, req.Key.SearchID())
|
||||
keys = append(keys, resource.SearchID(req.Key))
|
||||
}
|
||||
|
||||
// Verify that we read all values
|
||||
@@ -101,14 +102,14 @@ func TestParquetWriteThenRead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
for reader.Next() {
|
||||
req := reader.Request()
|
||||
keys = append(keys, req.Key.SearchID())
|
||||
keys = append(keys, resource.SearchID(req.Key))
|
||||
}
|
||||
require.NoError(t, reader.err)
|
||||
require.Empty(t, keys)
|
||||
})
|
||||
}
|
||||
|
||||
func toKeyAndBytes(ctx context.Context, group string, res string, obj *unstructured.Unstructured) (context.Context, *resource.ResourceKey, []byte) {
|
||||
func toKeyAndBytes(ctx context.Context, group string, res string, obj *unstructured.Unstructured) (context.Context, *resourcepb.ResourceKey, []byte) {
|
||||
if obj.GetKind() == "" {
|
||||
obj.SetKind(res)
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func toKeyAndBytes(ctx context.Context, group string, res string, obj *unstructu
|
||||
obj.SetAPIVersion(group + "/vXyz")
|
||||
}
|
||||
data, _ := obj.MarshalJSON()
|
||||
return ctx, &resource.ResourceKey{
|
||||
return ctx, &resourcepb.ResourceKey{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Resource: res,
|
||||
Group: group,
|
||||
|
||||
@@ -13,8 +13,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -28,8 +30,8 @@ func NewParquetWriter(f io.Writer) (*parquetWriter, error) {
|
||||
schema: newSchema(nil),
|
||||
buffer: 1024 * 10 * 100 * 10, // 10MB
|
||||
logger: logging.DefaultLogger.With("logger", "parquet.writer"),
|
||||
rsp: &resource.BulkResponse{},
|
||||
summary: make(map[string]*resource.BulkResponse_Summary),
|
||||
rsp: &resourcepb.BulkResponse{},
|
||||
summary: make(map[string]*resourcepb.BulkResponse_Summary),
|
||||
}
|
||||
|
||||
props := parquet.NewWriterProperties(
|
||||
@@ -44,7 +46,7 @@ func NewParquetWriter(f io.Writer) (*parquetWriter, error) {
|
||||
}
|
||||
|
||||
// ProcessBulk implements resource.BulkProcessingBackend.
|
||||
func (w *parquetWriter) ProcessBulk(ctx context.Context, setting resource.BulkSettings, iter resource.BulkRequestIterator) *resource.BulkResponse {
|
||||
func (w *parquetWriter) ProcessBulk(ctx context.Context, setting resource.BulkSettings, iter resource.BulkRequestIterator) *resourcepb.BulkResponse {
|
||||
defer func() { _ = w.Close() }()
|
||||
|
||||
var err error
|
||||
@@ -66,7 +68,7 @@ func (w *parquetWriter) ProcessBulk(ctx context.Context, setting resource.BulkSe
|
||||
w.logger.Warn("error closing parquet file", "err", err)
|
||||
}
|
||||
if rsp == nil {
|
||||
rsp = &resource.BulkResponse{}
|
||||
rsp = &resourcepb.BulkResponse{}
|
||||
}
|
||||
if err != nil {
|
||||
rsp.Error = resource.AsErrorResult(err)
|
||||
@@ -92,11 +94,11 @@ type parquetWriter struct {
|
||||
action *array.Int8Builder
|
||||
value *array.StringBuilder
|
||||
|
||||
rsp *resource.BulkResponse
|
||||
summary map[string]*resource.BulkResponse_Summary
|
||||
rsp *resourcepb.BulkResponse
|
||||
summary map[string]*resourcepb.BulkResponse_Summary
|
||||
}
|
||||
|
||||
func (w *parquetWriter) CloseWithResults() (*resource.BulkResponse, error) {
|
||||
func (w *parquetWriter) CloseWithResults() (*resourcepb.BulkResponse, error) {
|
||||
err := w.Close()
|
||||
return w.rsp, err
|
||||
}
|
||||
@@ -143,7 +145,7 @@ func (w *parquetWriter) init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *parquetWriter) Write(ctx context.Context, key *resource.ResourceKey, value []byte) error {
|
||||
func (w *parquetWriter) Write(ctx context.Context, key *resourcepb.ResourceKey, value []byte) error {
|
||||
w.rsp.Processed++
|
||||
obj := &unstructured.Unstructured{}
|
||||
err := obj.UnmarshalJSON(value)
|
||||
@@ -164,14 +166,14 @@ func (w *parquetWriter) Write(ctx context.Context, key *resource.ResourceKey, va
|
||||
w.folder.Append(meta.GetFolder())
|
||||
w.value.Append(string(value))
|
||||
|
||||
var action resource.WatchEvent_Type
|
||||
var action resourcepb.WatchEvent_Type
|
||||
switch meta.GetGeneration() {
|
||||
case 0, 1:
|
||||
action = resource.WatchEvent_ADDED
|
||||
action = resourcepb.WatchEvent_ADDED
|
||||
case utils.DeletedGeneration:
|
||||
action = resource.WatchEvent_DELETED
|
||||
action = resourcepb.WatchEvent_DELETED
|
||||
default:
|
||||
action = resource.WatchEvent_MODIFIED
|
||||
action = resourcepb.WatchEvent_MODIFIED
|
||||
}
|
||||
w.action.Append(int8(action))
|
||||
|
||||
@@ -181,14 +183,14 @@ func (w *parquetWriter) Write(ctx context.Context, key *resource.ResourceKey, va
|
||||
return w.flush()
|
||||
}
|
||||
|
||||
summary := w.summary[key.NSGR()]
|
||||
summary := w.summary[resource.NSGR(key)]
|
||||
if summary == nil {
|
||||
summary = &resource.BulkResponse_Summary{
|
||||
summary = &resourcepb.BulkResponse_Summary{
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
Resource: key.Resource,
|
||||
}
|
||||
w.summary[key.NSGR()] = summary
|
||||
w.summary[resource.NSGR(key)] = summary
|
||||
w.rsp.Summary = append(w.rsp.Summary, summary)
|
||||
}
|
||||
summary.Count++
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
syntax = "proto3";
|
||||
package resource;
|
||||
|
||||
option go_package = "github.com/grafana/grafana/pkg/storage/unified/resourcepb";
|
||||
|
||||
import "resource.proto";
|
||||
|
||||
//----------------------------
|
||||
// Blob Support
|
||||
//----------------------------
|
||||
|
||||
message PutBlobRequest {
|
||||
enum Method {
|
||||
// Use the inline raw []byte
|
||||
GRPC = 0;
|
||||
|
||||
// Get a signed URL and PUT the value
|
||||
HTTP = 1;
|
||||
}
|
||||
|
||||
// The resource that will use this blob
|
||||
// NOTE: the name may not yet exist, but group+resource are required
|
||||
ResourceKey resource = 1;
|
||||
|
||||
// How to upload
|
||||
Method method = 2;
|
||||
|
||||
// Content type header
|
||||
string content_type = 3;
|
||||
|
||||
// Raw value to write
|
||||
// Not valid when method == HTTP
|
||||
bytes value = 4;
|
||||
}
|
||||
|
||||
message PutBlobResponse {
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// The blob uid. This must be saved into the resource to support access
|
||||
string uid = 2;
|
||||
|
||||
// The URL where this value can be PUT
|
||||
string url = 3;
|
||||
|
||||
// Size of the uploaded blob
|
||||
int64 size = 4;
|
||||
|
||||
// Content hash used for an etag
|
||||
string hash = 5;
|
||||
|
||||
// Validated mimetype (from content_type)
|
||||
string mime_type = 6;
|
||||
|
||||
// Validated charset (from content_type)
|
||||
string charset = 7;
|
||||
}
|
||||
|
||||
message GetBlobRequest {
|
||||
ResourceKey resource = 1;
|
||||
|
||||
// The new resource version
|
||||
int64 resource_version = 2;
|
||||
|
||||
// Do not return a pre-signed URL (when possible)
|
||||
bool must_proxy_bytes = 3;
|
||||
|
||||
// The blob UID -- when empty, the value is loaded from annotations in the matching resource
|
||||
string uid = 4;
|
||||
}
|
||||
|
||||
message GetBlobResponse {
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// (optional) When possible, the system will return a presigned URL
|
||||
// that can be used to actually read the full blob+metadata
|
||||
// When this is set, neither info nor value will be set
|
||||
string url = 2;
|
||||
|
||||
// Content type
|
||||
string content_type = 3;
|
||||
|
||||
// The raw object value
|
||||
bytes value = 4;
|
||||
}
|
||||
|
||||
service BlobStore {
|
||||
// Upload a blob that will be saved in a resource
|
||||
rpc PutBlob(PutBlobRequest) returns (PutBlobResponse);
|
||||
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
rpc GetBlob(GetBlobRequest) returns (GetBlobResponse);
|
||||
|
||||
// NOTE: there is no direct access to delete blobs
|
||||
// >> cleanup will be managed via garbage collection or direct access to the underlying storage
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
version: v1
|
||||
plugins:
|
||||
- plugin: go
|
||||
out: pkg/storage/unified/resource
|
||||
out: pkg/storage/unified/resourcepb
|
||||
opt: paths=source_relative
|
||||
- plugin: go-grpc
|
||||
out: pkg/storage/unified/resource
|
||||
out: pkg/storage/unified/resourcepb
|
||||
opt:
|
||||
- paths=source_relative
|
||||
- require_unimplemented_servers=false
|
||||
@@ -1,4 +1,6 @@
|
||||
version: v1
|
||||
version: v2
|
||||
modules:
|
||||
- path: .
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
||||
+4
-230
@@ -1,7 +1,7 @@
|
||||
syntax = "proto3";
|
||||
package resource;
|
||||
|
||||
option go_package = "github.com/grafana/grafana/pkg/storage/unified/resource";
|
||||
option go_package = "github.com/grafana/grafana/pkg/storage/unified/resourcepb";
|
||||
|
||||
message ResourceKey {
|
||||
// Namespace (tenant)
|
||||
@@ -331,7 +331,7 @@ message WatchEvent {
|
||||
message BulkRequest {
|
||||
enum Action {
|
||||
// will be an error
|
||||
UNKNOWN = 0;
|
||||
UNKNOWN = 0;
|
||||
|
||||
// Matches Watch event enum
|
||||
ADDED = 1;
|
||||
@@ -345,7 +345,7 @@ message BulkRequest {
|
||||
// Requested action
|
||||
Action action = 2;
|
||||
|
||||
// The resource value
|
||||
// The resource value
|
||||
bytes value = 3;
|
||||
|
||||
// Hint that a new version will be written on-top of this
|
||||
@@ -382,135 +382,10 @@ message BulkResponse {
|
||||
// Summary status for the processed values
|
||||
repeated Summary summary = 3;
|
||||
|
||||
// Rejected
|
||||
// Rejected
|
||||
repeated Rejected rejected = 4;
|
||||
}
|
||||
|
||||
|
||||
// Get statistics across multiple resources
|
||||
// For these queries, we do not need authorization to see the actual values
|
||||
message ResourceStatsRequest {
|
||||
// Namespace (tenant)
|
||||
string namespace = 1;
|
||||
|
||||
// An optional list of group/resource identifiers
|
||||
// when empty, we assume searching across everything
|
||||
// NOTE, this query may need to federate across a few storage instances
|
||||
repeated string kinds = 2;
|
||||
|
||||
// Limit the stats within a folder (not recursive!)
|
||||
string folder = 3;
|
||||
}
|
||||
|
||||
message ResourceStatsResponse {
|
||||
message Stats {
|
||||
// Resource group
|
||||
string group = 1;
|
||||
// Resource name
|
||||
string resource = 2;
|
||||
// Number of items
|
||||
int64 count = 3;
|
||||
}
|
||||
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// All results exist within this key
|
||||
repeated Stats stats = 2;
|
||||
}
|
||||
|
||||
// Search within a single resource
|
||||
message ResourceSearchRequest {
|
||||
message Sort {
|
||||
string field = 1;
|
||||
bool desc = 2; // defaults to ascending
|
||||
}
|
||||
|
||||
message Facet {
|
||||
string field = 1;
|
||||
int64 limit = 2;
|
||||
// For now, only term queries, eventually?
|
||||
// numeric queries
|
||||
// date queries
|
||||
}
|
||||
|
||||
// The key must include namespace + group + resource
|
||||
ListOptions options = 1;
|
||||
|
||||
// To search additional resource types, add additional keys to this list
|
||||
// NOTE: queries will only support federation across kinds with common fields
|
||||
repeated ResourceKey federated = 2;
|
||||
|
||||
// When a query exists, it is parsed and used to influence
|
||||
// query string for chosen implementation (currently just bleve)
|
||||
// The score is only relevant when a query exists
|
||||
string query = 3;
|
||||
|
||||
// max results
|
||||
int64 limit = 4;
|
||||
|
||||
// where to start the query (eg, From)
|
||||
int64 offset = 5;
|
||||
|
||||
// sorting
|
||||
repeated Sort sortBy = 6;
|
||||
|
||||
// calculate field statistics
|
||||
map<string,Facet> facet = 7;
|
||||
|
||||
// the return fields (empty will return everything)
|
||||
repeated string fields = 8;
|
||||
|
||||
// explain each result (added to the each row)
|
||||
bool explain = 9;
|
||||
|
||||
bool is_deleted = 10;
|
||||
|
||||
int64 page = 11;
|
||||
|
||||
int64 permission = 12;
|
||||
}
|
||||
|
||||
message ResourceSearchResponse {
|
||||
message Facet {
|
||||
string field = 1;
|
||||
// The distinct terms
|
||||
int64 total = 2;
|
||||
// The number of documents that do *not* have this field
|
||||
int64 missing = 3;
|
||||
// Top term stats
|
||||
repeated TermFacet terms = 4;
|
||||
// numeric range
|
||||
// date range facets
|
||||
}
|
||||
|
||||
message TermFacet {
|
||||
string term = 1;
|
||||
int64 count = 2;
|
||||
}
|
||||
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// All results exist within this key
|
||||
ResourceKey key = 2;
|
||||
|
||||
// Query results
|
||||
ResourceTable results = 3;
|
||||
|
||||
// The total hit count
|
||||
int64 total_hits = 4;
|
||||
|
||||
// indicates how expensive was the query with respect to bytes read
|
||||
double query_cost = 5;
|
||||
|
||||
// maximum score across all fields
|
||||
double max_score = 6;
|
||||
|
||||
// Facet results
|
||||
map<string,Facet> facet = 7;
|
||||
}
|
||||
|
||||
// List items within a resource type & repository name
|
||||
// Access control is managed above this request
|
||||
message ListManagedObjectsRequest {
|
||||
@@ -712,86 +587,6 @@ message ResourceTableRow {
|
||||
bytes object = 4;
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
// Blob Support
|
||||
//----------------------------
|
||||
|
||||
message PutBlobRequest {
|
||||
enum Method {
|
||||
// Use the inline raw []byte
|
||||
GRPC = 0;
|
||||
|
||||
// Get a signed URL and PUT the value
|
||||
HTTP = 1;
|
||||
}
|
||||
|
||||
// The resource that will use this blob
|
||||
// NOTE: the name may not yet exist, but group+resource are required
|
||||
ResourceKey resource = 1;
|
||||
|
||||
// How to upload
|
||||
Method method = 2;
|
||||
|
||||
// Content type header
|
||||
string content_type = 3;
|
||||
|
||||
// Raw value to write
|
||||
// Not valid when method == HTTP
|
||||
bytes value = 4;
|
||||
}
|
||||
|
||||
message PutBlobResponse {
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// The blob uid. This must be saved into the resource to support access
|
||||
string uid = 2;
|
||||
|
||||
// The URL where this value can be PUT
|
||||
string url = 3;
|
||||
|
||||
// Size of the uploaded blob
|
||||
int64 size = 4;
|
||||
|
||||
// Content hash used for an etag
|
||||
string hash = 5;
|
||||
|
||||
// Validated mimetype (from content_type)
|
||||
string mime_type = 6;
|
||||
|
||||
// Validated charset (from content_type)
|
||||
string charset = 7;
|
||||
}
|
||||
|
||||
message GetBlobRequest {
|
||||
ResourceKey resource = 1;
|
||||
|
||||
// The new resource version
|
||||
int64 resource_version = 2;
|
||||
|
||||
// Do not return a pre-signed URL (when possible)
|
||||
bool must_proxy_bytes = 3;
|
||||
|
||||
// The blob UID -- when empty, the value is loaded from annotations in the matching resource
|
||||
string uid = 4;
|
||||
}
|
||||
|
||||
message GetBlobResponse {
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// (optional) When possible, the system will return a presigned URL
|
||||
// that can be used to actually read the full blob+metadata
|
||||
// When this is set, neither info nor value will be set
|
||||
string url = 2;
|
||||
|
||||
// Content type
|
||||
string content_type = 3;
|
||||
|
||||
// The raw object value
|
||||
bytes value = 4;
|
||||
}
|
||||
|
||||
// This provides the CRUD+List+Watch support needed for a k8s apiserver
|
||||
// The semantics and behaviors of this service are constrained by kubernetes
|
||||
// This does not understand the resource schemas, only deals with json bytes
|
||||
@@ -820,15 +615,6 @@ service BulkStore {
|
||||
rpc BulkProcess(stream BulkRequest) returns (BulkResponse);
|
||||
}
|
||||
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
service ResourceIndex {
|
||||
rpc Search(ResourceSearchRequest) returns (ResourceSearchResponse);
|
||||
|
||||
// Get the resource stats
|
||||
rpc GetStats(ResourceStatsRequest) returns (ResourceStatsResponse);
|
||||
}
|
||||
|
||||
// Query managed objects
|
||||
// Results access control is based on access to the repository *not* the items
|
||||
service ManagedObjectIndex {
|
||||
@@ -839,18 +625,6 @@ service ManagedObjectIndex {
|
||||
rpc ListManagedObjects(ListManagedObjectsRequest) returns (ListManagedObjectsResponse);
|
||||
}
|
||||
|
||||
service BlobStore {
|
||||
// Upload a blob that will be saved in a resource
|
||||
rpc PutBlob(PutBlobRequest) returns (PutBlobResponse);
|
||||
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
rpc GetBlob(GetBlobRequest) returns (GetBlobResponse);
|
||||
|
||||
// NOTE: there is no direct access to delete blobs
|
||||
// >> cleanup will be managed via garbage collection or direct access to the underlying storage
|
||||
}
|
||||
|
||||
// Clients can use this service directly
|
||||
// NOTE: This is read only, and no read afer write guarantees
|
||||
service Diagnostics {
|
||||
@@ -0,0 +1,140 @@
|
||||
syntax = "proto3";
|
||||
package resource;
|
||||
|
||||
option go_package = "github.com/grafana/grafana/pkg/storage/unified/resourcepb";
|
||||
|
||||
import "resource.proto";
|
||||
|
||||
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
service ResourceIndex {
|
||||
rpc Search(ResourceSearchRequest) returns (ResourceSearchResponse);
|
||||
|
||||
// Get the resource stats
|
||||
rpc GetStats(ResourceStatsRequest) returns (ResourceStatsResponse);
|
||||
}
|
||||
|
||||
// Get statistics across multiple resources
|
||||
// For these queries, we do not need authorization to see the actual values
|
||||
message ResourceStatsRequest {
|
||||
// Namespace (tenant)
|
||||
string namespace = 1;
|
||||
|
||||
// An optional list of group/resource identifiers
|
||||
// when empty, we assume searching across everything
|
||||
// NOTE, this query may need to federate across a few storage instances
|
||||
repeated string kinds = 2;
|
||||
|
||||
// Limit the stats within a folder (not recursive!)
|
||||
string folder = 3;
|
||||
}
|
||||
|
||||
message ResourceStatsResponse {
|
||||
message Stats {
|
||||
// Resource group
|
||||
string group = 1;
|
||||
// Resource name
|
||||
string resource = 2;
|
||||
// Number of items
|
||||
int64 count = 3;
|
||||
}
|
||||
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// All results exist within this key
|
||||
repeated Stats stats = 2;
|
||||
}
|
||||
|
||||
// Search within a single resource
|
||||
message ResourceSearchRequest {
|
||||
message Sort {
|
||||
string field = 1;
|
||||
bool desc = 2; // defaults to ascending
|
||||
}
|
||||
|
||||
message Facet {
|
||||
string field = 1;
|
||||
int64 limit = 2;
|
||||
// For now, only term queries, eventually?
|
||||
// numeric queries
|
||||
// date queries
|
||||
}
|
||||
|
||||
// The key must include namespace + group + resource
|
||||
ListOptions options = 1;
|
||||
|
||||
// To search additional resource types, add additional keys to this list
|
||||
// NOTE: queries will only support federation across kinds with common fields
|
||||
repeated ResourceKey federated = 2;
|
||||
|
||||
// When a query exists, it is parsed and used to influence
|
||||
// query string for chosen implementation (currently just bleve)
|
||||
// The score is only relevant when a query exists
|
||||
string query = 3;
|
||||
|
||||
// max results
|
||||
int64 limit = 4;
|
||||
|
||||
// where to start the query (eg, From)
|
||||
int64 offset = 5;
|
||||
|
||||
// sorting
|
||||
repeated Sort sortBy = 6;
|
||||
|
||||
// calculate field statistics
|
||||
map<string,Facet> facet = 7;
|
||||
|
||||
// the return fields (empty will return everything)
|
||||
repeated string fields = 8;
|
||||
|
||||
// explain each result (added to the each row)
|
||||
bool explain = 9;
|
||||
|
||||
bool is_deleted = 10;
|
||||
|
||||
int64 page = 11;
|
||||
|
||||
int64 permission = 12;
|
||||
}
|
||||
|
||||
message ResourceSearchResponse {
|
||||
message Facet {
|
||||
string field = 1;
|
||||
// The distinct terms
|
||||
int64 total = 2;
|
||||
// The number of documents that do *not* have this field
|
||||
int64 missing = 3;
|
||||
// Top term stats
|
||||
repeated TermFacet terms = 4;
|
||||
// numeric range
|
||||
// date range facets
|
||||
}
|
||||
|
||||
message TermFacet {
|
||||
string term = 1;
|
||||
int64 count = 2;
|
||||
}
|
||||
|
||||
// Error details
|
||||
ErrorResult error = 1;
|
||||
|
||||
// All results exist within this key
|
||||
ResourceKey key = 2;
|
||||
|
||||
// Query results
|
||||
ResourceTable results = 3;
|
||||
|
||||
// The total hit count
|
||||
int64 total_hits = 4;
|
||||
|
||||
// indicates how expensive was the query with respect to bytes read
|
||||
double query_cost = 5;
|
||||
|
||||
// maximum score across all fields
|
||||
double max_score = 6;
|
||||
|
||||
// Facet results
|
||||
map<string,Facet> facet = 7;
|
||||
}
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
const grpcMetaKeyCollection = "x-gf-batch-collection"
|
||||
@@ -25,28 +27,28 @@ type BulkRequestIterator interface {
|
||||
Next() bool
|
||||
|
||||
// The next event we should process
|
||||
Request() *BulkRequest
|
||||
Request() *resourcepb.BulkRequest
|
||||
|
||||
// Rollback requested
|
||||
RollbackRequested() bool
|
||||
}
|
||||
|
||||
type BulkProcessingBackend interface {
|
||||
ProcessBulk(ctx context.Context, setting BulkSettings, iter BulkRequestIterator) *BulkResponse
|
||||
ProcessBulk(ctx context.Context, setting BulkSettings, iter BulkRequestIterator) *resourcepb.BulkResponse
|
||||
}
|
||||
|
||||
type BulkResourceWriter interface {
|
||||
io.Closer
|
||||
|
||||
Write(ctx context.Context, key *ResourceKey, value []byte) error
|
||||
Write(ctx context.Context, key *resourcepb.ResourceKey, value []byte) error
|
||||
|
||||
// Called when finished writing
|
||||
CloseWithResults() (*BulkResponse, error)
|
||||
CloseWithResults() (*resourcepb.BulkResponse, error)
|
||||
}
|
||||
|
||||
type BulkSettings struct {
|
||||
// All requests will be within this namespace/group/resource
|
||||
Collection []*ResourceKey
|
||||
Collection []*resourcepb.ResourceKey
|
||||
|
||||
// The batch will include everything from the collection
|
||||
// - all existing values will be removed/replaced if the batch completes successfully
|
||||
@@ -60,7 +62,7 @@ func (x *BulkSettings) ToMD() metadata.MD {
|
||||
md := make(metadata.MD)
|
||||
if len(x.Collection) > 0 {
|
||||
for _, v := range x.Collection {
|
||||
md[grpcMetaKeyCollection] = append(md[grpcMetaKeyCollection], v.SearchID())
|
||||
md[grpcMetaKeyCollection] = append(md[grpcMetaKeyCollection], SearchID(v))
|
||||
}
|
||||
}
|
||||
if x.RebuildCollection {
|
||||
@@ -78,8 +80,8 @@ func NewBulkSettings(md metadata.MD) (BulkSettings, error) {
|
||||
switch k {
|
||||
case grpcMetaKeyCollection:
|
||||
for _, c := range v {
|
||||
key := &ResourceKey{}
|
||||
err := key.ReadSearchID(c)
|
||||
key := &resourcepb.ResourceKey{}
|
||||
err := ReadSearchID(key, c)
|
||||
if err != nil {
|
||||
return settings, fmt.Errorf("error reading collection metadata: %s / %w", c, err)
|
||||
}
|
||||
@@ -96,12 +98,12 @@ func NewBulkSettings(md metadata.MD) (BulkSettings, error) {
|
||||
|
||||
// BulkWrite implements ResourceServer.
|
||||
// All requests must be to the same NAMESPACE/GROUP/RESOURCE
|
||||
func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) error {
|
||||
ctx := stream.Context()
|
||||
user, ok := authlib.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
},
|
||||
@@ -110,8 +112,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "unable to read metadata gRPC request",
|
||||
Code: http.StatusPreconditionFailed,
|
||||
},
|
||||
@@ -123,8 +125,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
}
|
||||
settings, err := NewBulkSettings(md)
|
||||
if err != nil {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "error reading settings",
|
||||
Reason: err.Error(),
|
||||
Code: http.StatusPreconditionFailed,
|
||||
@@ -133,8 +135,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
}
|
||||
|
||||
if len(settings.Collection) < 1 {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "Missing target collection(s) in request header",
|
||||
Code: http.StatusBadRequest,
|
||||
},
|
||||
@@ -151,8 +153,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
Verb: utils.VerbDeleteCollection,
|
||||
})
|
||||
if err != nil || !rsp.Allowed {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: fmt.Sprintf("Requester must be able to: %s", utils.VerbDeleteCollection),
|
||||
Code: http.StatusForbidden,
|
||||
},
|
||||
@@ -160,15 +162,15 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
}
|
||||
|
||||
// This will be called for each request -- with the folder ID
|
||||
runner.checker[k.NSGR()], err = s.access.Compile(ctx, user, authlib.ListRequest{
|
||||
runner.checker[NSGR(k)], err = s.access.Compile(ctx, user, authlib.ListRequest{
|
||||
Namespace: k.Namespace,
|
||||
Group: k.Group,
|
||||
Resource: k.Resource,
|
||||
Verb: utils.VerbCreate,
|
||||
})
|
||||
if err != nil {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "Unable to check `create` permission",
|
||||
Code: http.StatusForbidden,
|
||||
},
|
||||
@@ -176,8 +178,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "Bulk currently only supports RebuildCollection",
|
||||
Code: http.StatusBadRequest,
|
||||
},
|
||||
@@ -186,8 +188,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
|
||||
backend, ok := s.backend.(BulkProcessingBackend)
|
||||
if !ok {
|
||||
return stream.SendAndClose(&BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "The server backend does not support batch processing",
|
||||
Code: http.StatusNotImplemented,
|
||||
},
|
||||
@@ -197,8 +199,8 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
// BulkProcess requests
|
||||
rsp := backend.ProcessBulk(ctx, settings, runner)
|
||||
if rsp == nil {
|
||||
rsp = &BulkResponse{
|
||||
Error: &ErrorResult{
|
||||
rsp = &resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: "Nothing returned from process batch",
|
||||
},
|
||||
@@ -218,7 +220,7 @@ func (s *server) BulkProcess(stream BulkStore_BulkProcessServer) error {
|
||||
}, summary.Count, summary.ResourceVersion)
|
||||
if err != nil {
|
||||
s.log.Warn("error building search index after batch load", "err", err)
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: "err building search index: " + summary.Resource,
|
||||
Reason: err.Error(),
|
||||
@@ -234,9 +236,9 @@ var (
|
||||
)
|
||||
|
||||
type batchRunner struct {
|
||||
stream BulkStore_BulkProcessServer
|
||||
stream resourcepb.BulkStore_BulkProcessServer
|
||||
rollback bool
|
||||
request *BulkRequest
|
||||
request *resourcepb.BulkRequest
|
||||
err error
|
||||
checker map[string]authlib.ItemChecker
|
||||
}
|
||||
@@ -262,7 +264,7 @@ func (b *batchRunner) Next() bool {
|
||||
|
||||
if b.request != nil {
|
||||
key := b.request.Key
|
||||
k := key.NSGR()
|
||||
k := NSGR(key)
|
||||
checker, ok := b.checker[k]
|
||||
if !ok {
|
||||
b.err = fmt.Errorf("missing access control for: %s", k)
|
||||
@@ -277,7 +279,7 @@ func (b *batchRunner) Next() bool {
|
||||
}
|
||||
|
||||
// Request implements BulkRequestIterator.
|
||||
func (b *batchRunner) Request() *BulkRequest {
|
||||
func (b *batchRunner) Request() *resourcepb.BulkRequest {
|
||||
if b.rollback {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type CDKBackendOptions struct {
|
||||
@@ -73,7 +74,7 @@ type cdkBackend struct {
|
||||
stream chan<- *WrittenEvent
|
||||
}
|
||||
|
||||
func (s *cdkBackend) getPath(key *ResourceKey, rv int64) string {
|
||||
func (s *cdkBackend) getPath(key *resourcepb.ResourceKey, rv int64) string {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(s.root)
|
||||
|
||||
@@ -116,9 +117,9 @@ func (s *cdkBackend) GetResourceStats(ctx context.Context, namespace string, min
|
||||
}
|
||||
|
||||
func (s *cdkBackend) WriteEvent(ctx context.Context, event WriteEvent) (rv int64, err error) {
|
||||
if event.Type == WatchEvent_ADDED {
|
||||
if event.Type == resourcepb.WatchEvent_ADDED {
|
||||
// ReadResource deals with deleted values (i.e. a file exists but has generation -999).
|
||||
resp := s.ReadResource(ctx, &ReadRequest{Key: event.Key})
|
||||
resp := s.ReadResource(ctx, &resourcepb.ReadRequest{Key: event.Key})
|
||||
if resp.Error != nil && resp.Error.Code != http.StatusNotFound {
|
||||
return 0, GetError(resp.Error)
|
||||
}
|
||||
@@ -155,7 +156,7 @@ func (s *cdkBackend) WriteEvent(ctx context.Context, event WriteEvent) (rv int64
|
||||
return rv, err
|
||||
}
|
||||
|
||||
func (s *cdkBackend) ReadResource(ctx context.Context, req *ReadRequest) *BackendReadResponse {
|
||||
func (s *cdkBackend) ReadResource(ctx context.Context, req *resourcepb.ReadRequest) *BackendReadResponse {
|
||||
rv := req.ResourceVersion
|
||||
|
||||
path := s.getPath(req.Key, rv)
|
||||
@@ -184,12 +185,12 @@ func (s *cdkBackend) ReadResource(ctx context.Context, req *ReadRequest) *Backen
|
||||
if raw == nil && req.ResourceVersion > 0 {
|
||||
if req.ResourceVersion > s.rv.Load() {
|
||||
return &BackendReadResponse{
|
||||
Error: &ErrorResult{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Code: http.StatusGatewayTimeout,
|
||||
Reason: string(metav1.StatusReasonTimeout), // match etcd behavior
|
||||
Message: "ResourceVersion is larger than max",
|
||||
Details: &ErrorDetails{
|
||||
Causes: []*ErrorCause{
|
||||
Details: &resourcepb.ErrorDetails{
|
||||
Causes: []*resourcepb.ErrorCause{
|
||||
{
|
||||
Reason: string(metav1.CauseTypeResourceVersionTooLarge),
|
||||
Message: fmt.Sprintf("requested: %d, current %d", req.ResourceVersion, s.rv.Load()),
|
||||
@@ -201,7 +202,7 @@ func (s *cdkBackend) ReadResource(ctx context.Context, req *ReadRequest) *Backen
|
||||
}
|
||||
|
||||
// If the there was an explicit request, get the latest
|
||||
rsp := s.ReadResource(ctx, &ReadRequest{Key: req.Key})
|
||||
rsp := s.ReadResource(ctx, &resourcepb.ReadRequest{Key: req.Key})
|
||||
if rsp != nil && len(rsp.Value) > 0 {
|
||||
raw = rsp.Value
|
||||
rv = rsp.ResourceVersion
|
||||
@@ -233,8 +234,8 @@ func isDeletedValue(raw []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *cdkBackend) ListIterator(ctx context.Context, req *ListRequest, cb func(ListIterator) error) (int64, error) {
|
||||
if req.Source != ListRequest_STORE {
|
||||
func (s *cdkBackend) ListIterator(ctx context.Context, req *resourcepb.ListRequest, cb func(ListIterator) error) (int64, error) {
|
||||
if req.Source != resourcepb.ListRequest_STORE {
|
||||
return 0, fmt.Errorf("listing from history not supported in CDK backend")
|
||||
}
|
||||
|
||||
@@ -356,7 +357,7 @@ func (c *cdkListIterator) Folder() string {
|
||||
|
||||
var _ ListIterator = (*cdkListIterator)(nil)
|
||||
|
||||
func buildTree(ctx context.Context, s *cdkBackend, key *ResourceKey) (*cdkListIterator, error) {
|
||||
func buildTree(ctx context.Context, s *cdkBackend, key *resourcepb.ResourceKey) (*cdkListIterator, error) {
|
||||
byPrefix := make(map[string]*cdkResource)
|
||||
path := s.getPath(key, 0)
|
||||
iter := s.bucket.List(&blob.ListOptions{Prefix: path, Delimiter: ""}) // "" is recursive
|
||||
|
||||
@@ -2,7 +2,7 @@ package resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
context "context"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"gocloud.dev/blob"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
// Supported drivers
|
||||
_ "gocloud.dev/blob/azureblob"
|
||||
@@ -85,7 +86,7 @@ type cdkBlobSupport struct {
|
||||
expiration time.Duration
|
||||
}
|
||||
|
||||
func (s *cdkBlobSupport) getBlobPath(key *ResourceKey, info *utils.BlobInfo) (string, error) {
|
||||
func (s *cdkBlobSupport) getBlobPath(key *resourcepb.ResourceKey, info *utils.BlobInfo) (string, error) {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(s.root)
|
||||
|
||||
@@ -129,7 +130,7 @@ func (s *cdkBlobSupport) SupportsSignedURLs() bool {
|
||||
return s.cansignurls
|
||||
}
|
||||
|
||||
func (s *cdkBlobSupport) PutResourceBlob(ctx context.Context, req *PutBlobRequest) (*PutBlobResponse, error) {
|
||||
func (s *cdkBlobSupport) PutResourceBlob(ctx context.Context, req *resourcepb.PutBlobRequest) (*resourcepb.PutBlobResponse, error) {
|
||||
info := &utils.BlobInfo{
|
||||
UID: uuid.New().String(),
|
||||
}
|
||||
@@ -139,8 +140,8 @@ func (s *cdkBlobSupport) PutResourceBlob(ctx context.Context, req *PutBlobReques
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rsp := &PutBlobResponse{Uid: info.UID, MimeType: info.MimeType, Charset: info.Charset}
|
||||
if req.Method == PutBlobRequest_HTTP {
|
||||
rsp := &resourcepb.PutBlobResponse{Uid: info.UID, MimeType: info.MimeType, Charset: info.Charset}
|
||||
if req.Method == resourcepb.PutBlobRequest_HTTP {
|
||||
rsp.Url, err = s.bucket.SignedURL(ctx, path, &blob.SignedURLOptions{
|
||||
Method: "PUT",
|
||||
Expiry: s.expiration,
|
||||
@@ -176,12 +177,13 @@ func (s *cdkBlobSupport) PutResourceBlob(ctx context.Context, req *PutBlobReques
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
func (s *cdkBlobSupport) GetResourceBlob(ctx context.Context, resource *ResourceKey, info *utils.BlobInfo, mustProxy bool) (*GetBlobResponse, error) {
|
||||
func (s *cdkBlobSupport) GetResourceBlob(ctx context.Context, resource *resourcepb.ResourceKey, info *utils.BlobInfo,
|
||||
mustProxy bool) (*resourcepb.GetBlobResponse, error) {
|
||||
path, err := s.getBlobPath(resource, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp := &GetBlobResponse{ContentType: info.ContentType()}
|
||||
rsp := &resourcepb.GetBlobResponse{ContentType: info.ContentType()}
|
||||
if mustProxy || !s.cansignurls {
|
||||
rsp.Value, err = s.bucket.ReadAll(ctx, path)
|
||||
return rsp, err
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gocloud.dev/blob/fileblob"
|
||||
"gocloud.dev/blob/memblob"
|
||||
@@ -36,16 +38,16 @@ func TestCDKBlobStore(t *testing.T) {
|
||||
|
||||
t.Run("can write then read a blob", func(t *testing.T) {
|
||||
raw := []byte(`{"hello": "world"}`)
|
||||
key := &ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "playlist.grafana.app",
|
||||
Resource: "rrrr", // can be anything
|
||||
Namespace: "default",
|
||||
Name: "fdgsv37qslr0ga",
|
||||
}
|
||||
|
||||
rsp, err := store.PutResourceBlob(ctx, &PutBlobRequest{
|
||||
rsp, err := store.PutResourceBlob(ctx, &resourcepb.PutBlobRequest{
|
||||
Resource: key,
|
||||
Method: PutBlobRequest_GRPC,
|
||||
Method: resourcepb.PutBlobRequest_GRPC,
|
||||
ContentType: "application/json",
|
||||
Value: raw,
|
||||
})
|
||||
|
||||
@@ -23,36 +23,37 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type ResourceClient interface {
|
||||
ResourceStoreClient
|
||||
ResourceIndexClient
|
||||
ManagedObjectIndexClient
|
||||
BulkStoreClient
|
||||
BlobStoreClient
|
||||
DiagnosticsClient
|
||||
resourcepb.ResourceStoreClient
|
||||
resourcepb.ResourceIndexClient
|
||||
resourcepb.ManagedObjectIndexClient
|
||||
resourcepb.BulkStoreClient
|
||||
resourcepb.BlobStoreClient
|
||||
resourcepb.DiagnosticsClient
|
||||
}
|
||||
|
||||
// Internal implementation
|
||||
type resourceClient struct {
|
||||
ResourceStoreClient
|
||||
ResourceIndexClient
|
||||
ManagedObjectIndexClient
|
||||
BulkStoreClient
|
||||
BlobStoreClient
|
||||
DiagnosticsClient
|
||||
resourcepb.ResourceStoreClient
|
||||
resourcepb.ResourceIndexClient
|
||||
resourcepb.ManagedObjectIndexClient
|
||||
resourcepb.BulkStoreClient
|
||||
resourcepb.BlobStoreClient
|
||||
resourcepb.DiagnosticsClient
|
||||
}
|
||||
|
||||
func NewLegacyResourceClient(channel grpc.ClientConnInterface) ResourceClient {
|
||||
cc := grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)
|
||||
return &resourceClient{
|
||||
ResourceStoreClient: NewResourceStoreClient(cc),
|
||||
ResourceIndexClient: NewResourceIndexClient(cc),
|
||||
ManagedObjectIndexClient: NewManagedObjectIndexClient(cc),
|
||||
BulkStoreClient: NewBulkStoreClient(cc),
|
||||
BlobStoreClient: NewBlobStoreClient(cc),
|
||||
DiagnosticsClient: NewDiagnosticsClient(cc),
|
||||
ResourceStoreClient: resourcepb.NewResourceStoreClient(cc),
|
||||
ResourceIndexClient: resourcepb.NewResourceIndexClient(cc),
|
||||
ManagedObjectIndexClient: resourcepb.NewManagedObjectIndexClient(cc),
|
||||
BulkStoreClient: resourcepb.NewBulkStoreClient(cc),
|
||||
BlobStoreClient: resourcepb.NewBlobStoreClient(cc),
|
||||
DiagnosticsClient: resourcepb.NewDiagnosticsClient(cc),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +64,12 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
|
||||
|
||||
grpcAuthInt := grpcutils.NewUnsafeAuthenticator(tracer)
|
||||
for _, desc := range []*grpc.ServiceDesc{
|
||||
&ResourceStore_ServiceDesc,
|
||||
&ResourceIndex_ServiceDesc,
|
||||
&ManagedObjectIndex_ServiceDesc,
|
||||
&BlobStore_ServiceDesc,
|
||||
&BulkStore_ServiceDesc,
|
||||
&Diagnostics_ServiceDesc,
|
||||
&resourcepb.ResourceStore_ServiceDesc,
|
||||
&resourcepb.ResourceIndex_ServiceDesc,
|
||||
&resourcepb.ManagedObjectIndex_ServiceDesc,
|
||||
&resourcepb.BlobStore_ServiceDesc,
|
||||
&resourcepb.BulkStore_ServiceDesc,
|
||||
&resourcepb.Diagnostics_ServiceDesc,
|
||||
} {
|
||||
channel.RegisterService(
|
||||
grpchan.InterceptServer(
|
||||
@@ -87,12 +88,12 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient {
|
||||
|
||||
cc := grpchan.InterceptClientConn(channel, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor)
|
||||
return &resourceClient{
|
||||
ResourceStoreClient: NewResourceStoreClient(cc),
|
||||
ResourceIndexClient: NewResourceIndexClient(cc),
|
||||
ManagedObjectIndexClient: NewManagedObjectIndexClient(cc),
|
||||
BulkStoreClient: NewBulkStoreClient(cc),
|
||||
BlobStoreClient: NewBlobStoreClient(cc),
|
||||
DiagnosticsClient: NewDiagnosticsClient(cc),
|
||||
ResourceStoreClient: resourcepb.NewResourceStoreClient(cc),
|
||||
ResourceIndexClient: resourcepb.NewResourceIndexClient(cc),
|
||||
ManagedObjectIndexClient: resourcepb.NewManagedObjectIndexClient(cc),
|
||||
BulkStoreClient: resourcepb.NewBulkStoreClient(cc),
|
||||
BlobStoreClient: resourcepb.NewBlobStoreClient(cc),
|
||||
DiagnosticsClient: resourcepb.NewDiagnosticsClient(cc),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +130,12 @@ func NewRemoteResourceClient(tracer trace.Tracer, conn grpc.ClientConnInterface,
|
||||
|
||||
cc := grpchan.InterceptClientConn(conn, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor)
|
||||
return &resourceClient{
|
||||
ResourceStoreClient: NewResourceStoreClient(cc),
|
||||
ResourceIndexClient: NewResourceIndexClient(cc),
|
||||
BlobStoreClient: NewBlobStoreClient(cc),
|
||||
BulkStoreClient: NewBulkStoreClient(cc),
|
||||
ManagedObjectIndexClient: NewManagedObjectIndexClient(cc),
|
||||
DiagnosticsClient: NewDiagnosticsClient(cc),
|
||||
ResourceStoreClient: resourcepb.NewResourceStoreClient(cc),
|
||||
ResourceIndexClient: resourcepb.NewResourceIndexClient(cc),
|
||||
BlobStoreClient: resourcepb.NewBlobStoreClient(cc),
|
||||
BulkStoreClient: resourcepb.NewBulkStoreClient(cc),
|
||||
ManagedObjectIndexClient: resourcepb.NewManagedObjectIndexClient(cc),
|
||||
DiagnosticsClient: resourcepb.NewDiagnosticsClient(cc),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,19 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// Convert raw resource bytes into an IndexableDocument
|
||||
type DocumentBuilder interface {
|
||||
// Convert raw bytes into an document that can be written
|
||||
BuildDocument(ctx context.Context, key *ResourceKey, rv int64, value []byte) (*IndexableDocument, error)
|
||||
BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*IndexableDocument, error)
|
||||
}
|
||||
|
||||
// Registry of the searchable document fields
|
||||
type SearchableDocumentFields interface {
|
||||
Fields() []string
|
||||
Field(name string) *ResourceTableColumnDefinition
|
||||
Field(name string) *resourcepb.ResourceTableColumnDefinition
|
||||
}
|
||||
|
||||
// Some kinds will require special processing for their namespace
|
||||
@@ -51,7 +52,7 @@ type DocumentBuilderSupplier interface {
|
||||
// Although public, this is *NOT* an end user interface
|
||||
type IndexableDocument struct {
|
||||
// The resource key
|
||||
Key *ResourceKey `json:"key"`
|
||||
Key *resourcepb.ResourceKey `json:"key"`
|
||||
|
||||
// The k8s name
|
||||
Name string `json:"name,omitempty"`
|
||||
@@ -165,7 +166,7 @@ func (m ResourceReferences) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
// Create a new indexable document based on a generic k8s resource
|
||||
func NewIndexableDocument(key *ResourceKey, rv int64, obj utils.GrafanaMetaAccessor) *IndexableDocument {
|
||||
func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.GrafanaMetaAccessor) *IndexableDocument {
|
||||
title := obj.FindTitle(key.Name)
|
||||
if title == key.Name {
|
||||
// TODO: something wrong with FindTitle
|
||||
@@ -216,7 +217,7 @@ func StandardDocumentBuilder() DocumentBuilder {
|
||||
|
||||
type standardDocumentBuilder struct{}
|
||||
|
||||
func (s *standardDocumentBuilder) BuildDocument(ctx context.Context, key *ResourceKey, rv int64, value []byte) (*IndexableDocument, error) {
|
||||
func (s *standardDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*IndexableDocument, error) {
|
||||
tmp := &unstructured.Unstructured{}
|
||||
err := tmp.UnmarshalJSON(value)
|
||||
if err != nil {
|
||||
@@ -238,7 +239,7 @@ type searchableDocumentFields struct {
|
||||
}
|
||||
|
||||
// This requires unique names
|
||||
func NewSearchableDocumentFields(columns []*ResourceTableColumnDefinition) (SearchableDocumentFields, error) {
|
||||
func NewSearchableDocumentFields(columns []*resourcepb.ResourceTableColumnDefinition) (SearchableDocumentFields, error) {
|
||||
f := &searchableDocumentFields{
|
||||
names: make([]string, len(columns)),
|
||||
fields: make(map[string]*resourceTableColumn),
|
||||
@@ -261,7 +262,7 @@ func (x *searchableDocumentFields) Fields() []string {
|
||||
return x.names
|
||||
}
|
||||
|
||||
func (x *searchableDocumentFields) Field(name string) *ResourceTableColumnDefinition {
|
||||
func (x *searchableDocumentFields) Field(name string) *resourcepb.ResourceTableColumnDefinition {
|
||||
name = strings.TrimPrefix(name, SEARCH_FIELD_PREFIX)
|
||||
|
||||
f, ok := x.fields[name]
|
||||
@@ -308,94 +309,94 @@ var standardSearchFields SearchableDocumentFields
|
||||
func StandardSearchFields() SearchableDocumentFields {
|
||||
standardSearchFieldsInit.Do(func() {
|
||||
var err error
|
||||
standardSearchFields, err = NewSearchableDocumentFields([]*ResourceTableColumnDefinition{
|
||||
standardSearchFields, err = NewSearchableDocumentFields([]*resourcepb.ResourceTableColumnDefinition{
|
||||
{
|
||||
Name: SEARCH_FIELD_ID,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "Unique Identifier. {namespace}/{group}/{resource}/{name}",
|
||||
Properties: &ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
NotNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_GROUP_RESOURCE,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "The resource kind: {group}/{resource}",
|
||||
Properties: &ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
NotNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_NAMESPACE,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "Tenant isolation",
|
||||
Properties: &ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
NotNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_NAME,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "Kubernetes name. Unique identifier within a namespace+group+resource",
|
||||
Properties: &ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
NotNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_TITLE,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "Display name for the resource",
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_DESCRIPTION,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "An account of the resource.",
|
||||
Properties: &ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
FreeText: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_TAGS,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
IsArray: true,
|
||||
Description: "Unique tags",
|
||||
Properties: &ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_FOLDER,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "Kubernetes name for the folder",
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_RV,
|
||||
Type: ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "resource version",
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_CREATED,
|
||||
Type: ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "created timestamp", // date?
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_EXPLAIN,
|
||||
Type: ResourceTableColumnDefinition_OBJECT,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_OBJECT,
|
||||
Description: "Explain why this result matches (depends on the engine)",
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_SCORE,
|
||||
Type: ResourceTableColumnDefinition_DOUBLE,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DOUBLE,
|
||||
Description: "The search score",
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_LEGACY_ID,
|
||||
Type: ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Deprecated legacy id of the resource",
|
||||
},
|
||||
{
|
||||
Name: SEARCH_FIELD_MANAGER_KIND,
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "Type of manager, which is responsible for managing the resource",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestStandardDocumentBuilder(t *testing.T) {
|
||||
@@ -16,7 +18,7 @@ func TestStandardDocumentBuilder(t *testing.T) {
|
||||
|
||||
body, err := os.ReadFile("testdata/playlist-resource.json")
|
||||
require.NoError(t, err)
|
||||
doc, err := builder.BuildDocument(ctx, &ResourceKey{
|
||||
doc, err := builder.BuildDocument(ctx, &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "playlists.grafana.app",
|
||||
Resource: "playlists",
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
grpcstatus "google.golang.org/grpc/status"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// Package-level errors.
|
||||
@@ -29,18 +31,18 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func NewBadRequestError(msg string) *ErrorResult {
|
||||
return &ErrorResult{
|
||||
func NewBadRequestError(msg string) *resourcepb.ErrorResult {
|
||||
return &resourcepb.ErrorResult{
|
||||
Message: msg,
|
||||
Code: http.StatusBadRequest,
|
||||
Reason: string(metav1.StatusReasonBadRequest),
|
||||
}
|
||||
}
|
||||
|
||||
func NewNotFoundError(key *ResourceKey) *ErrorResult {
|
||||
return &ErrorResult{
|
||||
func NewNotFoundError(key *resourcepb.ResourceKey) *resourcepb.ErrorResult {
|
||||
return &resourcepb.ErrorResult{
|
||||
Code: http.StatusNotFound,
|
||||
Details: &ErrorDetails{
|
||||
Details: &resourcepb.ErrorDetails{
|
||||
Group: key.Group,
|
||||
Kind: key.Resource, // yup, resource as kind same is true in apierrors.NewNotFound()
|
||||
Name: key.Name,
|
||||
@@ -49,7 +51,7 @@ func NewNotFoundError(key *ResourceKey) *ErrorResult {
|
||||
}
|
||||
|
||||
// Convert golang errors to status result errors that can be returned to a client
|
||||
func AsErrorResult(err error) *ErrorResult {
|
||||
func AsErrorResult(err error) *resourcepb.ErrorResult {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -57,13 +59,13 @@ func AsErrorResult(err error) *ErrorResult {
|
||||
var apistatus apierrors.APIStatus
|
||||
if errors.As(err, &apistatus) {
|
||||
s := apistatus.Status()
|
||||
res := &ErrorResult{
|
||||
res := &resourcepb.ErrorResult{
|
||||
Message: s.Message,
|
||||
Reason: string(s.Reason),
|
||||
Code: s.Code,
|
||||
}
|
||||
if s.Details != nil {
|
||||
res.Details = &ErrorDetails{
|
||||
res.Details = &resourcepb.ErrorDetails{
|
||||
Group: s.Details.Group,
|
||||
Kind: s.Details.Kind,
|
||||
Name: s.Details.Name,
|
||||
@@ -71,7 +73,7 @@ func AsErrorResult(err error) *ErrorResult {
|
||||
RetryAfterSeconds: s.Details.RetryAfterSeconds,
|
||||
}
|
||||
for _, c := range s.Details.Causes {
|
||||
res.Details.Causes = append(res.Details.Causes, &ErrorCause{
|
||||
res.Details.Causes = append(res.Details.Causes, &resourcepb.ErrorCause{
|
||||
Reason: string(c.Type),
|
||||
Message: c.Message,
|
||||
Field: c.Field,
|
||||
@@ -88,13 +90,13 @@ func AsErrorResult(err error) *ErrorResult {
|
||||
code = runtime.HTTPStatusFromCode(st.Code())
|
||||
}
|
||||
|
||||
return &ErrorResult{
|
||||
return &resourcepb.ErrorResult{
|
||||
Message: err.Error(),
|
||||
Code: int32(code),
|
||||
}
|
||||
}
|
||||
|
||||
func GetError(res *ErrorResult) error {
|
||||
func GetError(res *resourcepb.ErrorResult) error {
|
||||
if res == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type WriteEvent struct {
|
||||
Type WatchEvent_Type // ADDED, MODIFIED, DELETED
|
||||
Key *ResourceKey // the request key
|
||||
PreviousRV int64 // only for Update+Delete
|
||||
Type resourcepb.WatchEvent_Type // ADDED, MODIFIED, DELETED
|
||||
Key *resourcepb.ResourceKey // the request key
|
||||
PreviousRV int64 // only for Update+Delete
|
||||
|
||||
// The json payload (without resourceVersion)
|
||||
Value []byte
|
||||
@@ -23,8 +24,8 @@ type WriteEvent struct {
|
||||
|
||||
// WrittenEvent is a WriteEvent reported with a resource version.
|
||||
type WrittenEvent struct {
|
||||
Type WatchEvent_Type
|
||||
Key *ResourceKey
|
||||
Type resourcepb.WatchEvent_Type
|
||||
Key *resourcepb.ResourceKey
|
||||
PreviousRV int64
|
||||
|
||||
// The json payload (without resourceVersion)
|
||||
|
||||
@@ -7,6 +7,7 @@ replace (
|
||||
github.com/grafana/grafana/apps/folder => ../../../../apps/folder
|
||||
github.com/grafana/grafana/pkg/apimachinery => ../../../apimachinery
|
||||
github.com/grafana/grafana/pkg/apiserver => ../../../apiserver
|
||||
github.com/grafana/grafana/pkg/storage/unified/resourcepb => ../resourcepb
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -21,6 +22,7 @@ require (
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250422074709-7c8433fbb2c2
|
||||
github.com/grafana/grafana/apps/folder v0.0.0-20250422074709-7c8433fbb2c2
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2
|
||||
github.com/grafana/grafana/pkg/storage/unified/resourcepb v0.0.0-00010101000000-000000000000
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
@@ -30,8 +32,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
gocloud.dev v0.40.0
|
||||
golang.org/x/sync v0.14.0
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
google.golang.org/grpc v1.72.1
|
||||
k8s.io/apimachinery v0.32.3
|
||||
)
|
||||
|
||||
@@ -232,6 +233,7 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
@@ -850,8 +850,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// Compile-time assertion
|
||||
@@ -17,13 +19,13 @@ type HealthService interface {
|
||||
grpcAuth.ServiceAuthFuncOverride
|
||||
}
|
||||
|
||||
func ProvideHealthService(server DiagnosticsServer) (grpc_health_v1.HealthServer, error) {
|
||||
func ProvideHealthService(server resourcepb.DiagnosticsServer) (grpc_health_v1.HealthServer, error) {
|
||||
h := &healthServer{srv: server}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
type healthServer struct {
|
||||
srv DiagnosticsServer
|
||||
srv resourcepb.DiagnosticsServer
|
||||
}
|
||||
|
||||
// AuthFuncOverride for no auth for health service.
|
||||
@@ -45,8 +47,8 @@ func (s *healthServer) List(ctx context.Context, req *grpc_health_v1.HealthListR
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *healthServer) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
|
||||
r, err := s.srv.IsHealthy(ctx, &HealthCheckRequest{})
|
||||
func (s *healthServer) Check(ctx context.Context, _ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
|
||||
r, err := s.srv.IsHealthy(ctx, &resourcepb.HealthCheckRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -56,8 +58,8 @@ func (s *healthServer) Check(ctx context.Context, req *grpc_health_v1.HealthChec
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *healthServer) Watch(req *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error {
|
||||
h, err := s.srv.IsHealthy(stream.Context(), &HealthCheckRequest{})
|
||||
func (s *healthServer) Watch(_ *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error {
|
||||
h, err := s.srv.IsHealthy(stream.Context(), &resourcepb.HealthCheckRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,7 +79,7 @@ func (s *healthServer) Watch(req *grpc_health_v1.HealthCheckRequest, stream grpc
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// get current health status
|
||||
h, err := s.srv.IsHealthy(stream.Context(), &HealthCheckRequest{})
|
||||
h, err := s.srv.IsHealthy(stream.Context(), &resourcepb.HealthCheckRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package resource
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
sync "sync"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -11,11 +11,13 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
t.Run("will return serving response when healthy", func(t *testing.T) {
|
||||
stub := &diag{healthResponse: HealthCheckResponse_SERVING}
|
||||
stub := &diag{healthResponse: resourcepb.HealthCheckResponse_SERVING}
|
||||
svc, err := ProvideHealthService(stub)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -27,7 +29,7 @@ func TestHealthCheck(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("will return not serving response when not healthy", func(t *testing.T) {
|
||||
stub := &diag{healthResponse: HealthCheckResponse_NOT_SERVING}
|
||||
stub := &diag{healthResponse: resourcepb.HealthCheckResponse_NOT_SERVING}
|
||||
svc, err := ProvideHealthService(stub)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -41,7 +43,7 @@ func TestHealthCheck(t *testing.T) {
|
||||
|
||||
func TestHealthWatch(t *testing.T) {
|
||||
t.Run("watch will return message when called", func(t *testing.T) {
|
||||
stub := &diag{healthResponse: HealthCheckResponse_SERVING}
|
||||
stub := &diag{healthResponse: resourcepb.HealthCheckResponse_SERVING}
|
||||
svc, err := ProvideHealthService(stub)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -58,7 +60,7 @@ func TestHealthWatch(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("watch will return error when context cancelled", func(t *testing.T) {
|
||||
stub := &diag{healthResponse: HealthCheckResponse_NOT_SERVING}
|
||||
stub := &diag{healthResponse: resourcepb.HealthCheckResponse_NOT_SERVING}
|
||||
svc, err := ProvideHealthService(stub)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -72,19 +74,19 @@ func TestHealthWatch(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
var _ DiagnosticsServer = &diag{}
|
||||
var _ resourcepb.DiagnosticsServer = &diag{}
|
||||
|
||||
type diag struct {
|
||||
healthResponse HealthCheckResponse_ServingStatus
|
||||
healthResponse resourcepb.HealthCheckResponse_ServingStatus
|
||||
error error
|
||||
}
|
||||
|
||||
func (s *diag) IsHealthy(ctx context.Context, req *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||
func (s *diag) IsHealthy(ctx context.Context, req *resourcepb.HealthCheckRequest) (*resourcepb.HealthCheckResponse, error) {
|
||||
if s.error != nil {
|
||||
return nil, s.error
|
||||
}
|
||||
|
||||
return &HealthCheckResponse{Status: s.healthResponse}, nil
|
||||
return &resourcepb.HealthCheckResponse{Status: s.healthResponse}, nil
|
||||
}
|
||||
|
||||
type fakeHealthWatchServer struct {
|
||||
|
||||
@@ -3,9 +3,11 @@ package resource
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func verifyRequestKey(key *ResourceKey) *ErrorResult {
|
||||
func verifyRequestKey(key *resourcepb.ResourceKey) *resourcepb.ErrorResult {
|
||||
if key == nil {
|
||||
return NewBadRequestError("missing resource key")
|
||||
}
|
||||
@@ -18,7 +20,7 @@ func verifyRequestKey(key *ResourceKey) *ErrorResult {
|
||||
return nil
|
||||
}
|
||||
|
||||
func matchesQueryKey(query *ResourceKey, key *ResourceKey) bool {
|
||||
func matchesQueryKey(query *resourcepb.ResourceKey, key *resourcepb.ResourceKey) bool {
|
||||
if query.Group != key.Group {
|
||||
return false
|
||||
}
|
||||
@@ -37,7 +39,7 @@ func matchesQueryKey(query *ResourceKey, key *ResourceKey) bool {
|
||||
const clusterNamespace = "**cluster**"
|
||||
|
||||
// Convert the key to a search ID string
|
||||
func (x *ResourceKey) SearchID() string {
|
||||
func SearchID(x *resourcepb.ResourceKey) string {
|
||||
var sb strings.Builder
|
||||
if x.Namespace == "" {
|
||||
sb.WriteString(clusterNamespace)
|
||||
@@ -55,7 +57,7 @@ func (x *ResourceKey) SearchID() string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (x *ResourceKey) ReadSearchID(v string) error {
|
||||
func ReadSearchID(x *resourcepb.ResourceKey, v string) error {
|
||||
parts := strings.Split(v, "/")
|
||||
if len(parts) < 3 {
|
||||
return fmt.Errorf("invalid search id (expecting 3 slashes)")
|
||||
@@ -75,7 +77,7 @@ func (x *ResourceKey) ReadSearchID(v string) error {
|
||||
}
|
||||
|
||||
// The namespace/group/resource
|
||||
func (x *ResourceKey) NSGR() string {
|
||||
func NSGR(x *resourcepb.ResourceKey) string {
|
||||
var sb strings.Builder
|
||||
if x.Namespace == "" {
|
||||
sb.WriteString(clusterNamespace)
|
||||
|
||||
@@ -4,15 +4,17 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestKeyMatching(t *testing.T) {
|
||||
t.Run("key matching", func(t *testing.T) {
|
||||
require.True(t, matchesQueryKey(&ResourceKey{
|
||||
require.True(t, matchesQueryKey(&resourcepb.ResourceKey{
|
||||
Group: "ggg",
|
||||
Resource: "rrr",
|
||||
Namespace: "ns",
|
||||
}, &ResourceKey{
|
||||
}, &resourcepb.ResourceKey{
|
||||
Group: "ggg",
|
||||
Resource: "rrr",
|
||||
Namespace: "ns",
|
||||
@@ -23,32 +25,32 @@ func TestKeyMatching(t *testing.T) {
|
||||
func TestSearchIDKeys(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected *ResourceKey // nil error
|
||||
expected *resourcepb.ResourceKey // nil error
|
||||
}{
|
||||
{input: "a"}, // error
|
||||
{input: "default/group/resource/name",
|
||||
expected: &ResourceKey{
|
||||
expected: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "name",
|
||||
}},
|
||||
{input: "/group/resource/",
|
||||
expected: &ResourceKey{
|
||||
expected: &resourcepb.ResourceKey{
|
||||
Namespace: "",
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "",
|
||||
}},
|
||||
{input: "default/group/resource",
|
||||
expected: &ResourceKey{
|
||||
expected: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "",
|
||||
}},
|
||||
{input: "**cluster**/group/resource/aaa", // cluster namespace
|
||||
expected: &ResourceKey{
|
||||
expected: &resourcepb.ResourceKey{
|
||||
Namespace: "",
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -57,8 +59,8 @@ func TestSearchIDKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tmp := &ResourceKey{}
|
||||
err := tmp.ReadSearchID(test.input)
|
||||
tmp := &resourcepb.ResourceKey{}
|
||||
err := ReadSearchID(tmp, test.input)
|
||||
if err == nil {
|
||||
require.Equal(t, test.expected, tmp, test.input)
|
||||
}
|
||||
|
||||
@@ -4,19 +4,21 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// MigrateVersionMatch handles backwards compatibility for ResourceVersionMatch
|
||||
// by migrating from the deprecated version_match to version_match_v2.
|
||||
// It returns an error if the version match is unknown.
|
||||
func MigrateListRequestVersionMatch(req *ListRequest, logger logging.Logger) error {
|
||||
if req.VersionMatch != nil && req.GetVersionMatchV2() == ResourceVersionMatchV2_UNKNOWN {
|
||||
func MigrateListRequestVersionMatch(req *resourcepb.ListRequest, logger logging.Logger) error {
|
||||
if req.VersionMatch != nil && req.GetVersionMatchV2() == resourcepb.ResourceVersionMatchV2_UNKNOWN {
|
||||
switch req.GetVersionMatch() {
|
||||
case ResourceVersionMatch_DEPRECATED_NotOlderThan:
|
||||
case resourcepb.ResourceVersionMatch_DEPRECATED_NotOlderThan:
|
||||
// This is not a typo. The old implementation actually did behave like Unset.
|
||||
req.VersionMatchV2 = ResourceVersionMatchV2_Unset
|
||||
case ResourceVersionMatch_DEPRECATED_Exact:
|
||||
req.VersionMatchV2 = ResourceVersionMatchV2_Exact
|
||||
req.VersionMatchV2 = resourcepb.ResourceVersionMatchV2_Unset
|
||||
case resourcepb.ResourceVersionMatch_DEPRECATED_Exact:
|
||||
req.VersionMatchV2 = resourcepb.ResourceVersionMatchV2_Exact
|
||||
default:
|
||||
return fmt.Errorf("unknown version match: %v", req.GetVersionMatch())
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var (
|
||||
_ DiagnosticsServer = (*noopService)(nil)
|
||||
_ LifecycleHooks = (*noopService)(nil)
|
||||
_ resourcepb.DiagnosticsServer = (*noopService)(nil)
|
||||
_ LifecycleHooks = (*noopService)(nil)
|
||||
)
|
||||
|
||||
// noopService is a helper implementation to simplify tests
|
||||
@@ -24,12 +26,12 @@ func (n *noopService) Stop(context.Context) error {
|
||||
}
|
||||
|
||||
// IsHealthy implements DiagnosticsServer
|
||||
func (n *noopService) IsHealthy(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||
return &HealthCheckResponse{
|
||||
Status: HealthCheckResponse_SERVING,
|
||||
func (n *noopService) IsHealthy(context.Context, *resourcepb.HealthCheckRequest) (*resourcepb.HealthCheckResponse, error) {
|
||||
return &resourcepb.HealthCheckResponse{
|
||||
Status: resourcepb.HealthCheckResponse_SERVING,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *noopService) Read(context.Context, *ReadRequest) (*ReadResponse, error) {
|
||||
func (n *noopService) Read(context.Context, *resourcepb.ReadRequest) (*resourcepb.ReadResponse, error) {
|
||||
return nil, ErrNotImplementedYet
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
const maxBatchSize = 1000
|
||||
@@ -43,8 +45,8 @@ const (
|
||||
|
||||
type BulkIndexItem struct {
|
||||
Action IndexAction
|
||||
Key *ResourceKey // Only used for delete actions
|
||||
Doc *IndexableDocument // Only used for index actions
|
||||
Key *resourcepb.ResourceKey // Only used for delete actions
|
||||
Doc *IndexableDocument // Only used for index actions
|
||||
}
|
||||
|
||||
type BulkIndexRequest struct {
|
||||
@@ -59,13 +61,13 @@ type ResourceIndex interface {
|
||||
|
||||
// Search within a namespaced resource
|
||||
// When working with federated queries, the additional indexes will be passed in explicitly
|
||||
Search(ctx context.Context, access types.AccessClient, req *ResourceSearchRequest, federate []ResourceIndex) (*ResourceSearchResponse, error)
|
||||
Search(ctx context.Context, access types.AccessClient, req *resourcepb.ResourceSearchRequest, federate []ResourceIndex) (*resourcepb.ResourceSearchResponse, error)
|
||||
|
||||
// List within an response
|
||||
ListManagedObjects(ctx context.Context, req *ListManagedObjectsRequest) (*ListManagedObjectsResponse, error)
|
||||
ListManagedObjects(ctx context.Context, req *resourcepb.ListManagedObjectsRequest) (*resourcepb.ListManagedObjectsResponse, error)
|
||||
|
||||
// Counts the values in a repo
|
||||
CountManagedObjects(ctx context.Context) ([]*CountManagedObjectsResponse_ResourceCount, error)
|
||||
CountManagedObjects(ctx context.Context) ([]*resourcepb.CountManagedObjectsResponse_ResourceCount, error)
|
||||
|
||||
// Get the number of documents in the index
|
||||
DocCount(ctx context.Context, folder string) (int64, error)
|
||||
@@ -122,8 +124,8 @@ type searchSupport struct {
|
||||
}
|
||||
|
||||
var (
|
||||
_ ResourceIndexServer = (*searchSupport)(nil)
|
||||
_ ManagedObjectIndexServer = (*searchSupport)(nil)
|
||||
_ resourcepb.ResourceIndexServer = (*searchSupport)(nil)
|
||||
_ resourcepb.ManagedObjectIndexServer = (*searchSupport)(nil)
|
||||
)
|
||||
|
||||
func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.AccessClient, blob BlobSupport, tracer trace.Tracer, indexMetrics *BleveIndexMetrics) (support *searchSupport, err error) {
|
||||
@@ -166,14 +168,14 @@ func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.A
|
||||
return support, err
|
||||
}
|
||||
|
||||
func (s *searchSupport) ListManagedObjects(ctx context.Context, req *ListManagedObjectsRequest) (*ListManagedObjectsResponse, error) {
|
||||
func (s *searchSupport) ListManagedObjects(ctx context.Context, req *resourcepb.ListManagedObjectsRequest) (*resourcepb.ListManagedObjectsResponse, error) {
|
||||
if req.NextPageToken != "" {
|
||||
return &ListManagedObjectsResponse{
|
||||
return &resourcepb.ListManagedObjectsResponse{
|
||||
Error: NewBadRequestError("multiple pages not yet supported"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
rsp := &ListManagedObjectsResponse{}
|
||||
rsp := &resourcepb.ListManagedObjectsResponse{}
|
||||
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0)
|
||||
if err != nil {
|
||||
rsp.Error = AsErrorResult(err)
|
||||
@@ -197,7 +199,7 @@ func (s *searchSupport) ListManagedObjects(ctx context.Context, req *ListManaged
|
||||
return rsp, nil
|
||||
}
|
||||
if kind.NextPageToken != "" {
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Message: "Multiple pages are not yet supported",
|
||||
}
|
||||
return rsp, nil
|
||||
@@ -206,15 +208,15 @@ func (s *searchSupport) ListManagedObjects(ctx context.Context, req *ListManaged
|
||||
}
|
||||
|
||||
// Sort based on path
|
||||
slices.SortFunc(rsp.Items, func(a, b *ListManagedObjectsResponse_Item) int {
|
||||
slices.SortFunc(rsp.Items, func(a, b *resourcepb.ListManagedObjectsResponse_Item) int {
|
||||
return cmp.Compare(a.Path, b.Path)
|
||||
})
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *searchSupport) CountManagedObjects(ctx context.Context, req *CountManagedObjectsRequest) (*CountManagedObjectsResponse, error) {
|
||||
rsp := &CountManagedObjectsResponse{}
|
||||
func (s *searchSupport) CountManagedObjects(ctx context.Context, req *resourcepb.CountManagedObjectsRequest) (*resourcepb.CountManagedObjectsResponse, error) {
|
||||
rsp := &resourcepb.CountManagedObjectsResponse{}
|
||||
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0)
|
||||
if err != nil {
|
||||
rsp.Error = AsErrorResult(err)
|
||||
@@ -249,7 +251,7 @@ func (s *searchSupport) CountManagedObjects(ctx context.Context, req *CountManag
|
||||
}
|
||||
|
||||
// Sort based on manager/group/resource
|
||||
slices.SortFunc(rsp.Items, func(a, b *CountManagedObjectsResponse_ResourceCount) int {
|
||||
slices.SortFunc(rsp.Items, func(a, b *resourcepb.CountManagedObjectsResponse_ResourceCount) int {
|
||||
return cmp.Or(
|
||||
cmp.Compare(a.Kind, b.Kind),
|
||||
cmp.Compare(a.Id, b.Id),
|
||||
@@ -262,7 +264,7 @@ func (s *searchSupport) CountManagedObjects(ctx context.Context, req *CountManag
|
||||
}
|
||||
|
||||
// Search implements ResourceIndexServer.
|
||||
func (s *searchSupport) Search(ctx context.Context, req *ResourceSearchRequest) (*ResourceSearchResponse, error) {
|
||||
func (s *searchSupport) Search(ctx context.Context, req *resourcepb.ResourceSearchRequest) (*resourcepb.ResourceSearchResponse, error) {
|
||||
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"Search")
|
||||
defer span.End()
|
||||
|
||||
@@ -273,7 +275,7 @@ func (s *searchSupport) Search(ctx context.Context, req *ResourceSearchRequest)
|
||||
}
|
||||
idx, err := s.getOrCreateIndex(ctx, nsr)
|
||||
if err != nil {
|
||||
return &ResourceSearchResponse{
|
||||
return &resourcepb.ResourceSearchResponse{
|
||||
Error: AsErrorResult(err),
|
||||
}, nil
|
||||
}
|
||||
@@ -285,7 +287,7 @@ func (s *searchSupport) Search(ctx context.Context, req *ResourceSearchRequest)
|
||||
nsr.Resource = f.Resource
|
||||
federate[i], err = s.getOrCreateIndex(ctx, nsr)
|
||||
if err != nil {
|
||||
return &ResourceSearchResponse{
|
||||
return &resourcepb.ResourceSearchResponse{
|
||||
Error: AsErrorResult(err),
|
||||
}, nil
|
||||
}
|
||||
@@ -295,17 +297,17 @@ func (s *searchSupport) Search(ctx context.Context, req *ResourceSearchRequest)
|
||||
}
|
||||
|
||||
// GetStats implements ResourceServer.
|
||||
func (s *searchSupport) GetStats(ctx context.Context, req *ResourceStatsRequest) (*ResourceStatsResponse, error) {
|
||||
func (s *searchSupport) GetStats(ctx context.Context, req *resourcepb.ResourceStatsRequest) (*resourcepb.ResourceStatsResponse, error) {
|
||||
if req.Namespace == "" {
|
||||
return &ResourceStatsResponse{
|
||||
return &resourcepb.ResourceStatsResponse{
|
||||
Error: NewBadRequestError("missing namespace"),
|
||||
}, nil
|
||||
}
|
||||
rsp := &ResourceStatsResponse{}
|
||||
rsp := &resourcepb.ResourceStatsResponse{}
|
||||
|
||||
// Explicit list of kinds
|
||||
if len(req.Kinds) > 0 {
|
||||
rsp.Stats = make([]*ResourceStatsResponse_Stats, len(req.Kinds))
|
||||
rsp.Stats = make([]*resourcepb.ResourceStatsResponse_Stats, len(req.Kinds))
|
||||
for i, k := range req.Kinds {
|
||||
parts := strings.SplitN(k, "/", 2)
|
||||
index, err := s.getOrCreateIndex(ctx, NamespacedResource{
|
||||
@@ -322,7 +324,7 @@ func (s *searchSupport) GetStats(ctx context.Context, req *ResourceStatsRequest)
|
||||
rsp.Error = AsErrorResult(err)
|
||||
return rsp, nil
|
||||
}
|
||||
rsp.Stats[i] = &ResourceStatsResponse_Stats{
|
||||
rsp.Stats[i] = &resourcepb.ResourceStatsResponse_Stats{
|
||||
Group: parts[0],
|
||||
Resource: parts[1],
|
||||
Count: count,
|
||||
@@ -333,16 +335,16 @@ func (s *searchSupport) GetStats(ctx context.Context, req *ResourceStatsRequest)
|
||||
|
||||
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0)
|
||||
if err != nil {
|
||||
return &ResourceStatsResponse{
|
||||
return &resourcepb.ResourceStatsResponse{
|
||||
Error: AsErrorResult(err),
|
||||
}, nil
|
||||
}
|
||||
rsp.Stats = make([]*ResourceStatsResponse_Stats, len(stats))
|
||||
rsp.Stats = make([]*resourcepb.ResourceStatsResponse_Stats, len(stats))
|
||||
|
||||
// When not filtered by folder or repository, we can use the results directly
|
||||
if req.Folder == "" {
|
||||
for i, stat := range stats {
|
||||
rsp.Stats[i] = &ResourceStatsResponse_Stats{
|
||||
rsp.Stats[i] = &resourcepb.ResourceStatsResponse_Stats{
|
||||
Group: stat.Group,
|
||||
Resource: stat.Resource,
|
||||
Count: stat.Count,
|
||||
@@ -366,7 +368,7 @@ func (s *searchSupport) GetStats(ctx context.Context, req *ResourceStatsRequest)
|
||||
rsp.Error = AsErrorResult(err)
|
||||
return rsp, nil
|
||||
}
|
||||
rsp.Stats[i] = &ResourceStatsResponse_Stats{
|
||||
rsp.Stats[i] = &resourcepb.ResourceStatsResponse_Stats{
|
||||
Group: stat.Group,
|
||||
Resource: stat.Resource,
|
||||
Count: count,
|
||||
@@ -450,7 +452,7 @@ func (s *searchSupport) dispatchEvent(ctx context.Context, evt *WrittenEvent) {
|
||||
)
|
||||
|
||||
switch evt.Type {
|
||||
case WatchEvent_ADDED, WatchEvent_MODIFIED, WatchEvent_DELETED: // OK
|
||||
case resourcepb.WatchEvent_ADDED, resourcepb.WatchEvent_MODIFIED, resourcepb.WatchEvent_DELETED: // OK
|
||||
default:
|
||||
s.log.Info("ignoring watch event", "type", evt.Type)
|
||||
span.AddEvent("ignoring watch event", trace.WithAttributes(attribute.String("type", evt.Type.String())))
|
||||
@@ -547,10 +549,10 @@ func (s *searchSupport) build(ctx context.Context, nsr NamespacedResource, size
|
||||
s.log.Debug("Building index", "resource", nsr.Resource, "size", size, "rv", rv)
|
||||
|
||||
index, err := s.search.BuildIndex(ctx, nsr, size, rv, fields, func(index ResourceIndex) (int64, error) {
|
||||
rv, err = s.storage.ListIterator(ctx, &ListRequest{
|
||||
rv, err = s.storage.ListIterator(ctx, &resourcepb.ListRequest{
|
||||
Limit: 1000000000000, // big number
|
||||
Options: &ListOptions{
|
||||
Key: &ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: nsr.Group,
|
||||
Resource: nsr.Resource,
|
||||
Namespace: nsr.Namespace,
|
||||
@@ -566,7 +568,7 @@ func (s *searchSupport) build(ctx context.Context, nsr NamespacedResource, size
|
||||
}
|
||||
|
||||
// Update the key name
|
||||
key := &ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: nsr.Group,
|
||||
Resource: nsr.Resource,
|
||||
Namespace: nsr.Namespace,
|
||||
@@ -576,7 +578,7 @@ func (s *searchSupport) build(ctx context.Context, nsr NamespacedResource, size
|
||||
// Convert it to an indexable document
|
||||
doc, err := builder.BuildDocument(ctx, key, iter.ResourceVersion(), iter.Value())
|
||||
if err != nil {
|
||||
s.log.Error("error building search document", "key", key.SearchID(), "err", err)
|
||||
s.log.Error("error building search document", "key", SearchID(key), "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -703,19 +705,19 @@ func (s *builderCache) get(ctx context.Context, key NamespacedResource) (Documen
|
||||
}
|
||||
|
||||
// AsResourceKey converts the given namespace and type to a search key
|
||||
func AsResourceKey(ns string, t string) (*ResourceKey, error) {
|
||||
func AsResourceKey(ns string, t string) (*resourcepb.ResourceKey, error) {
|
||||
if ns == "" {
|
||||
return nil, fmt.Errorf("missing namespace")
|
||||
}
|
||||
switch t {
|
||||
case "folders", "folder":
|
||||
return &ResourceKey{
|
||||
return &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: folders.GROUP,
|
||||
Resource: folders.RESOURCE,
|
||||
}, nil
|
||||
case "dashboards", "dashboard":
|
||||
return &ResourceKey{
|
||||
return &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: dashboardv1.GROUP,
|
||||
Resource: dashboardv1.DASHBOARD_RESOURCE,
|
||||
@@ -723,7 +725,7 @@ func AsResourceKey(ns string, t string) (*ResourceKey, error) {
|
||||
|
||||
// NOT really supported in the dashboard search UI, but useful for manual testing
|
||||
case "playlist", "playlists":
|
||||
return &ResourceKey{
|
||||
return &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "playlist.grafana.app",
|
||||
Resource: "playlists",
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
type DualWriter interface {
|
||||
@@ -12,7 +14,8 @@ type DualWriter interface {
|
||||
ReadFromUnified(context.Context, schema.GroupResource) (bool, error)
|
||||
}
|
||||
|
||||
func NewSearchClient(dual DualWriter, gr schema.GroupResource, unifiedClient ResourceIndexClient, legacyClient ResourceIndexClient) ResourceIndexClient {
|
||||
func NewSearchClient(dual DualWriter, gr schema.GroupResource, unifiedClient resourcepb.ResourceIndexClient,
|
||||
legacyClient resourcepb.ResourceIndexClient) resourcepb.ResourceIndexClient {
|
||||
if dual.IsEnabled(gr) {
|
||||
return &searchWrapper{
|
||||
dual: dual,
|
||||
@@ -32,11 +35,12 @@ type searchWrapper struct {
|
||||
dual DualWriter
|
||||
groupResource schema.GroupResource
|
||||
|
||||
unifiedClient ResourceIndexClient
|
||||
legacyClient ResourceIndexClient
|
||||
unifiedClient resourcepb.ResourceIndexClient
|
||||
legacyClient resourcepb.ResourceIndexClient
|
||||
}
|
||||
|
||||
func (s *searchWrapper) GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error) {
|
||||
func (s *searchWrapper) GetStats(ctx context.Context, in *resourcepb.ResourceStatsRequest,
|
||||
opts ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) {
|
||||
client := s.legacyClient
|
||||
unified, err := s.dual.ReadFromUnified(ctx, s.groupResource)
|
||||
if err != nil {
|
||||
@@ -48,7 +52,8 @@ func (s *searchWrapper) GetStats(ctx context.Context, in *ResourceStatsRequest,
|
||||
return client.GetStats(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (s *searchWrapper) Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error) {
|
||||
func (s *searchWrapper) Search(ctx context.Context, in *resourcepb.ResourceSearchRequest,
|
||||
opts ...grpc.CallOption) (*resourcepb.ResourceSearchResponse, error) {
|
||||
client := s.legacyClient
|
||||
unified, err := s.dual.ReadFromUnified(ctx, s.groupResource)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// indexQueueProcessor manages queue-based operations for a specific index
|
||||
@@ -108,7 +110,7 @@ func (b *indexQueueProcessor) process(batch []*WrittenEvent) {
|
||||
resp = append(resp, result)
|
||||
|
||||
item := &BulkIndexItem{}
|
||||
if evt.Type == WatchEvent_DELETED {
|
||||
if evt.Type == resourcepb.WatchEvent_DELETED {
|
||||
item.Action = ActionDelete
|
||||
item.Key = evt.Key
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestNewIndexQueueProcessor(t *testing.T) {
|
||||
@@ -32,11 +34,11 @@ func TestIndexQueueProcessor_SingleEvent(t *testing.T) {
|
||||
processor := newIndexQueueProcessor(mockIndex, nsr, 10, mockBuilder, resChan)
|
||||
|
||||
// Test data
|
||||
key := ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"}
|
||||
key := resourcepb.ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"}
|
||||
evt := &WrittenEvent{
|
||||
Key: &key,
|
||||
ResourceVersion: time.Now().UnixMicro(),
|
||||
Type: WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Value: []byte(`{"test": "data"}`),
|
||||
}
|
||||
|
||||
@@ -70,15 +72,15 @@ func TestIndexQueueProcessor_BatchProcessing(t *testing.T) {
|
||||
// Test data for two events
|
||||
events := []*WrittenEvent{
|
||||
{
|
||||
Key: &ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"},
|
||||
Key: &resourcepb.ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"},
|
||||
ResourceVersion: time.Now().UnixMicro(),
|
||||
Type: WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Value: []byte(`{"test": "data1"}`),
|
||||
},
|
||||
{
|
||||
Key: &ResourceKey{Resource: "test", Name: "obj2", Namespace: "default"},
|
||||
Key: &resourcepb.ResourceKey{Resource: "test", Name: "obj2", Namespace: "default"},
|
||||
ResourceVersion: time.Now().UnixMicro(),
|
||||
Type: WatchEvent_DELETED,
|
||||
Type: resourcepb.WatchEvent_DELETED,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -117,9 +119,9 @@ func TestIndexQueueProcessor_BuildDocumentError(t *testing.T) {
|
||||
processor := newIndexQueueProcessor(mockIndex, nsr, 10, mockBuilder, resChan)
|
||||
|
||||
evt := &WrittenEvent{
|
||||
Key: &ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"},
|
||||
Key: &resourcepb.ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"},
|
||||
ResourceVersion: time.Now().UnixMicro(),
|
||||
Type: WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Value: []byte(`invalid json`),
|
||||
}
|
||||
|
||||
@@ -151,9 +153,9 @@ func TestIndexQueueProcessor_BulkIndexError(t *testing.T) {
|
||||
processor := newIndexQueueProcessor(mockIndex, nsr, 10, mockBuilder, resChan)
|
||||
|
||||
evt := &WrittenEvent{
|
||||
Key: &ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"},
|
||||
Key: &resourcepb.ResourceKey{Resource: "test", Name: "obj1", Namespace: "default"},
|
||||
ResourceVersion: time.Now().UnixMicro(),
|
||||
Type: WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Value: []byte(`{"test": "data"}`),
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var _ ResourceIndex = &MockResourceIndex{}
|
||||
@@ -19,14 +21,14 @@ func (m *MockResourceIndex) BulkIndex(req *BulkIndexRequest) error {
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockResourceIndex) Search(ctx context.Context, access types.AccessClient, req *ResourceSearchRequest, federate []ResourceIndex) (*ResourceSearchResponse, error) {
|
||||
func (m *MockResourceIndex) Search(ctx context.Context, access types.AccessClient, req *resourcepb.ResourceSearchRequest, federate []ResourceIndex) (*resourcepb.ResourceSearchResponse, error) {
|
||||
args := m.Called(ctx, access, req, federate)
|
||||
return args.Get(0).(*ResourceSearchResponse), args.Error(1)
|
||||
return args.Get(0).(*resourcepb.ResourceSearchResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockResourceIndex) CountManagedObjects(ctx context.Context) ([]*CountManagedObjectsResponse_ResourceCount, error) {
|
||||
func (m *MockResourceIndex) CountManagedObjects(ctx context.Context) ([]*resourcepb.CountManagedObjectsResponse_ResourceCount, error) {
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).([]*CountManagedObjectsResponse_ResourceCount), args.Error(1)
|
||||
return args.Get(0).([]*resourcepb.CountManagedObjectsResponse_ResourceCount), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockResourceIndex) DocCount(ctx context.Context, folder string) (int64, error) {
|
||||
@@ -34,9 +36,9 @@ func (m *MockResourceIndex) DocCount(ctx context.Context, folder string) (int64,
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockResourceIndex) ListManagedObjects(ctx context.Context, req *ListManagedObjectsRequest) (*ListManagedObjectsResponse, error) {
|
||||
func (m *MockResourceIndex) ListManagedObjects(ctx context.Context, req *resourcepb.ListManagedObjectsRequest) (*resourcepb.ListManagedObjectsResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*ListManagedObjectsResponse), args.Error(1)
|
||||
return args.Get(0).(*resourcepb.ListManagedObjectsResponse), args.Error(1)
|
||||
}
|
||||
|
||||
var _ DocumentBuilder = &MockDocumentBuilder{}
|
||||
@@ -45,7 +47,7 @@ type MockDocumentBuilder struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockDocumentBuilder) BuildDocument(ctx context.Context, key *ResourceKey, resourceVersion int64, value []byte) (*IndexableDocument, error) {
|
||||
func (m *MockDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, resourceVersion int64, value []byte) (*IndexableDocument, error) {
|
||||
args := m.Called(ctx, key, resourceVersion, value)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
|
||||
@@ -24,17 +24,19 @@ import (
|
||||
"github.com/grafana/dskit/ring"
|
||||
ringclient "github.com/grafana/dskit/ring/client"
|
||||
userutils "github.com/grafana/dskit/user"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// ResourceServer implements all gRPC services
|
||||
type ResourceServer interface {
|
||||
ResourceStoreServer
|
||||
BulkStoreServer
|
||||
ResourceIndexServer
|
||||
ManagedObjectIndexServer
|
||||
BlobStoreServer
|
||||
DiagnosticsServer
|
||||
resourcepb.ResourceStoreServer
|
||||
resourcepb.BulkStoreServer
|
||||
resourcepb.ResourceIndexServer
|
||||
resourcepb.ManagedObjectIndexServer
|
||||
resourcepb.BlobStoreServer
|
||||
resourcepb.DiagnosticsServer
|
||||
}
|
||||
|
||||
type ListIterator interface {
|
||||
@@ -73,7 +75,7 @@ type ListIterator interface {
|
||||
|
||||
type BackendReadResponse struct {
|
||||
// Metadata
|
||||
Key *ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
Folder string
|
||||
|
||||
// GUID that is used internally
|
||||
@@ -83,7 +85,7 @@ type BackendReadResponse struct {
|
||||
// The properties
|
||||
Value []byte
|
||||
// Error details
|
||||
Error *ErrorResult
|
||||
Error *resourcepb.ErrorResult
|
||||
}
|
||||
|
||||
// The StorageBackend is an internal abstraction that supports interacting with
|
||||
@@ -96,14 +98,14 @@ type StorageBackend interface {
|
||||
WriteEvent(context.Context, WriteEvent) (int64, error)
|
||||
|
||||
// Read a resource from storage optionally at an explicit version
|
||||
ReadResource(context.Context, *ReadRequest) *BackendReadResponse
|
||||
ReadResource(context.Context, *resourcepb.ReadRequest) *BackendReadResponse
|
||||
|
||||
// When the ResourceServer executes a List request, this iterator will
|
||||
// query the backend for potential results. All results will be
|
||||
// checked against the kubernetes requirements before finally returning
|
||||
// results. The list options can be used to improve performance
|
||||
// but are the the final answer.
|
||||
ListIterator(context.Context, *ListRequest, func(ListIterator) error) (int64, error)
|
||||
ListIterator(context.Context, *resourcepb.ListRequest, func(ListIterator) error) (int64, error)
|
||||
|
||||
// Get all events from the store
|
||||
// For HA setups, this will be more events than the local WriteEvent above!
|
||||
@@ -128,11 +130,11 @@ type BlobSupport interface {
|
||||
|
||||
// Get the raw blob bytes and metadata -- limited to protobuf message size
|
||||
// For larger payloads, we should use presigned URLs to upload from the client
|
||||
PutResourceBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error)
|
||||
PutResourceBlob(context.Context, *resourcepb.PutBlobRequest) (*resourcepb.PutBlobResponse, error)
|
||||
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
GetResourceBlob(ctx context.Context, resource *ResourceKey, info *utils.BlobInfo, mustProxy bool) (*GetBlobResponse, error)
|
||||
GetResourceBlob(ctx context.Context, resource *resourcepb.ResourceKey, info *utils.BlobInfo, mustProxy bool) (*resourcepb.GetBlobResponse, error)
|
||||
|
||||
// TODO? List+Delete? This is for admin access
|
||||
}
|
||||
@@ -177,7 +179,7 @@ type ResourceServerOptions struct {
|
||||
Search SearchOptions
|
||||
|
||||
// Diagnostics
|
||||
Diagnostics DiagnosticsServer
|
||||
Diagnostics resourcepb.DiagnosticsServer
|
||||
|
||||
// Check if a user has access to write folders
|
||||
// When this is nil, no resources can have folders configured
|
||||
@@ -298,7 +300,7 @@ type server struct {
|
||||
backend StorageBackend
|
||||
blob BlobSupport
|
||||
search *searchSupport
|
||||
diagnostics DiagnosticsServer
|
||||
diagnostics resourcepb.DiagnosticsServer
|
||||
access claims.AccessClient
|
||||
writeHooks WriteAccessHooks
|
||||
lifecycle LifecycleHooks
|
||||
@@ -431,7 +433,7 @@ func (s *server) Stop(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Old value indicates an update -- otherwise a create
|
||||
func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *ResourceKey, value, oldValue []byte) (*WriteEvent, *ErrorResult) {
|
||||
func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *resourcepb.ResourceKey, value, oldValue []byte) (*WriteEvent, *resourcepb.ErrorResult) {
|
||||
tmp := &unstructured.Unstructured{}
|
||||
err := tmp.UnmarshalJSON(value)
|
||||
if err != nil {
|
||||
@@ -470,9 +472,9 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *Resour
|
||||
}
|
||||
|
||||
if oldValue == nil {
|
||||
event.Type = WatchEvent_ADDED
|
||||
event.Type = resourcepb.WatchEvent_ADDED
|
||||
} else {
|
||||
event.Type = WatchEvent_MODIFIED
|
||||
event.Type = resourcepb.WatchEvent_MODIFIED
|
||||
|
||||
temp := &unstructured.Unstructured{}
|
||||
err = temp.UnmarshalJSON(oldValue)
|
||||
@@ -530,7 +532,7 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *Resour
|
||||
Namespace: key.Namespace,
|
||||
}
|
||||
|
||||
if event.Type == WatchEvent_MODIFIED {
|
||||
if event.Type == resourcepb.WatchEvent_MODIFIED {
|
||||
check.Verb = utils.VerbUpdate
|
||||
check.Name = key.Name
|
||||
}
|
||||
@@ -541,7 +543,7 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *Resour
|
||||
return nil, AsErrorResult(err)
|
||||
}
|
||||
if !a.Allowed {
|
||||
return nil, &ErrorResult{
|
||||
return nil, &resourcepb.ErrorResult{
|
||||
Code: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
@@ -559,13 +561,13 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *Resour
|
||||
|
||||
// isFolderMove determines if an event represents a resource being moved between folders
|
||||
func (s *server) isFolderMove(event *WriteEvent) bool {
|
||||
return event.Type == WatchEvent_MODIFIED &&
|
||||
return event.Type == resourcepb.WatchEvent_MODIFIED &&
|
||||
event.ObjectOld != nil &&
|
||||
event.ObjectOld.GetFolder() != event.Object.GetFolder()
|
||||
}
|
||||
|
||||
// checkFolderMovePermissions handles permission checks when a resource is being moved between folders
|
||||
func (s *server) checkFolderMovePermissions(ctx context.Context, user claims.AuthInfo, key *ResourceKey, oldFolder, newFolder string) *ErrorResult {
|
||||
func (s *server) checkFolderMovePermissions(ctx context.Context, user claims.AuthInfo, key *resourcepb.ResourceKey, oldFolder, newFolder string) *resourcepb.ErrorResult {
|
||||
// First check if user can update the resource in the original folder
|
||||
updateCheck := claims.CheckRequest{
|
||||
Verb: utils.VerbUpdate,
|
||||
@@ -581,7 +583,7 @@ func (s *server) checkFolderMovePermissions(ctx context.Context, user claims.Aut
|
||||
return AsErrorResult(err)
|
||||
}
|
||||
if !a.Allowed {
|
||||
return &ErrorResult{
|
||||
return &resourcepb.ErrorResult{
|
||||
Code: http.StatusForbidden,
|
||||
Message: "not allowed to update resource in the source folder",
|
||||
}
|
||||
@@ -601,7 +603,7 @@ func (s *server) checkFolderMovePermissions(ctx context.Context, user claims.Aut
|
||||
return AsErrorResult(err)
|
||||
}
|
||||
if !a.Allowed {
|
||||
return &ErrorResult{
|
||||
return &resourcepb.ErrorResult{
|
||||
Code: http.StatusForbidden,
|
||||
Message: "not allowed to create resource in the destination folder",
|
||||
}
|
||||
@@ -610,14 +612,14 @@ func (s *server) checkFolderMovePermissions(ctx context.Context, user claims.Aut
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) Create(ctx context.Context, req *CreateRequest) (*CreateResponse, error) {
|
||||
func (s *server) Create(ctx context.Context, req *resourcepb.CreateRequest) (*resourcepb.CreateResponse, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "storage_server.Create")
|
||||
defer span.End()
|
||||
|
||||
rsp := &CreateResponse{}
|
||||
rsp := &resourcepb.CreateResponse{}
|
||||
user, ok := claims.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
}
|
||||
@@ -641,14 +643,14 @@ func (s *server) Create(ctx context.Context, req *CreateRequest) (*CreateRespons
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateResponse, error) {
|
||||
func (s *server) Update(ctx context.Context, req *resourcepb.UpdateRequest) (*resourcepb.UpdateResponse, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "storage_server.Update")
|
||||
defer span.End()
|
||||
|
||||
rsp := &UpdateResponse{}
|
||||
rsp := &resourcepb.UpdateResponse{}
|
||||
user, ok := claims.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
}
|
||||
@@ -659,7 +661,7 @@ func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateRespons
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
latest := s.backend.ReadResource(ctx, &ReadRequest{
|
||||
latest := s.backend.ReadResource(ctx, &resourcepb.ReadRequest{
|
||||
Key: req.Key,
|
||||
})
|
||||
if latest.Error != nil {
|
||||
@@ -680,7 +682,7 @@ func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateRespons
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
event.Type = WatchEvent_MODIFIED
|
||||
event.Type = resourcepb.WatchEvent_MODIFIED
|
||||
event.PreviousRV = latest.ResourceVersion
|
||||
|
||||
var err error
|
||||
@@ -691,24 +693,24 @@ func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateRespons
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *server) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) {
|
||||
func (s *server) Delete(ctx context.Context, req *resourcepb.DeleteRequest) (*resourcepb.DeleteResponse, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "storage_server.Delete")
|
||||
defer span.End()
|
||||
|
||||
rsp := &DeleteResponse{}
|
||||
rsp := &resourcepb.DeleteResponse{}
|
||||
if req.ResourceVersion < 0 {
|
||||
return nil, apierrors.NewBadRequest("update must include the previous version")
|
||||
}
|
||||
user, ok := claims.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
}
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
latest := s.backend.ReadResource(ctx, &ReadRequest{
|
||||
latest := s.backend.ReadResource(ctx, &resourcepb.ReadRequest{
|
||||
Key: req.Key,
|
||||
})
|
||||
if latest.Error != nil {
|
||||
@@ -733,7 +735,7 @@ func (s *server) Delete(ctx context.Context, req *DeleteRequest) (*DeleteRespons
|
||||
return rsp, nil
|
||||
}
|
||||
if !access.Allowed {
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Code: http.StatusForbidden,
|
||||
}
|
||||
return rsp, nil
|
||||
@@ -742,7 +744,7 @@ func (s *server) Delete(ctx context.Context, req *DeleteRequest) (*DeleteRespons
|
||||
now := metav1.NewTime(time.UnixMilli(s.now()))
|
||||
event := WriteEvent{
|
||||
Key: req.Key,
|
||||
Type: WatchEvent_DELETED,
|
||||
Type: resourcepb.WatchEvent_DELETED,
|
||||
PreviousRV: latest.ResourceVersion,
|
||||
}
|
||||
requester, ok := claims.AuthInfoFrom(ctx)
|
||||
@@ -779,11 +781,11 @@ func (s *server) Delete(ctx context.Context, req *DeleteRequest) (*DeleteRespons
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *server) Read(ctx context.Context, req *ReadRequest) (*ReadResponse, error) {
|
||||
func (s *server) Read(ctx context.Context, req *resourcepb.ReadRequest) (*resourcepb.ReadResponse, error) {
|
||||
user, ok := claims.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
return &ReadResponse{
|
||||
Error: &ErrorResult{
|
||||
return &resourcepb.ReadResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
}}, nil
|
||||
@@ -794,12 +796,12 @@ func (s *server) Read(ctx context.Context, req *ReadRequest) (*ReadResponse, err
|
||||
// return &ReadResponse{Status: status}, nil
|
||||
// }
|
||||
if req.Key.Resource == "" {
|
||||
return &ReadResponse{Error: NewBadRequestError("missing resource")}, nil
|
||||
return &resourcepb.ReadResponse{Error: NewBadRequestError("missing resource")}, nil
|
||||
}
|
||||
|
||||
rsp := s.backend.ReadResource(ctx, req)
|
||||
if rsp.Error != nil && rsp.Error.Code == http.StatusNotFound {
|
||||
return &ReadResponse{Error: rsp.Error}, nil
|
||||
return &resourcepb.ReadResponse{Error: rsp.Error}, nil
|
||||
}
|
||||
|
||||
a, err := s.access.Check(ctx, user, claims.CheckRequest{
|
||||
@@ -811,29 +813,29 @@ func (s *server) Read(ctx context.Context, req *ReadRequest) (*ReadResponse, err
|
||||
Folder: rsp.Folder,
|
||||
})
|
||||
if err != nil {
|
||||
return &ReadResponse{Error: AsErrorResult(err)}, nil
|
||||
return &resourcepb.ReadResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
if !a.Allowed {
|
||||
return &ReadResponse{
|
||||
Error: &ErrorResult{
|
||||
return &resourcepb.ReadResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Code: http.StatusForbidden,
|
||||
}}, nil
|
||||
}
|
||||
return &ReadResponse{
|
||||
return &resourcepb.ReadResponse{
|
||||
ResourceVersion: rsp.ResourceVersion,
|
||||
Value: rsp.Value,
|
||||
Error: rsp.Error,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, error) {
|
||||
func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resourcepb.ListResponse, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "storage_server.List")
|
||||
defer span.End()
|
||||
|
||||
// The history + trash queries do not yet support additional filters
|
||||
if req.Source != ListRequest_STORE {
|
||||
if req.Source != resourcepb.ListRequest_STORE {
|
||||
if len(req.Options.Fields) > 0 || len(req.Options.Labels) > 0 {
|
||||
return &ListResponse{
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("unexpected field/label selector for history query"),
|
||||
}, nil
|
||||
}
|
||||
@@ -841,8 +843,8 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
|
||||
user, ok := claims.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
return &ListResponse{
|
||||
Error: &ErrorResult{
|
||||
return &resourcepb.ListResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
}}, nil
|
||||
@@ -851,7 +853,7 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
// Do not allow label query for trash/history
|
||||
for _, v := range req.Options.Labels {
|
||||
if v.Key == utils.LabelKeyGetHistory || v.Key == utils.LabelKeyGetTrash {
|
||||
return &ListResponse{Error: NewBadRequestError("history and trash must be requested as source")}, nil
|
||||
return &resourcepb.ListResponse{Error: NewBadRequestError("history and trash must be requested as source")}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,7 +862,7 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
}
|
||||
maxPageBytes := 1024 * 1024 * 2 // 2mb/page
|
||||
pageBytes := 0
|
||||
rsp := &ListResponse{}
|
||||
rsp := &resourcepb.ListResponse{}
|
||||
|
||||
key := req.Options.Key
|
||||
checker, err := s.access.Compile(ctx, user, claims.ListRequest{
|
||||
@@ -870,10 +872,10 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
Verb: utils.VerbGet,
|
||||
})
|
||||
if err != nil {
|
||||
return &ListResponse{Error: AsErrorResult(err)}, nil
|
||||
return &resourcepb.ListResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
if checker == nil {
|
||||
return &ListResponse{Error: &ErrorResult{
|
||||
return &resourcepb.ListResponse{Error: &resourcepb.ErrorResult{
|
||||
Code: http.StatusForbidden,
|
||||
}}, nil
|
||||
}
|
||||
@@ -884,7 +886,7 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
return err
|
||||
}
|
||||
|
||||
item := &ResourceWrapper{
|
||||
item := &resourcepb.ResourceWrapper{
|
||||
ResourceVersion: iter.ResourceVersion(),
|
||||
Value: iter.Value(),
|
||||
}
|
||||
@@ -897,7 +899,7 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
rsp.Items = append(rsp.Items, item)
|
||||
if len(rsp.Items) >= int(req.Limit) || pageBytes >= maxPageBytes {
|
||||
t := iter.ContinueToken()
|
||||
if req.Source == ListRequest_HISTORY || req.Source == ListRequest_TRASH {
|
||||
if req.Source == resourcepb.ListRequest_HISTORY || req.Source == resourcepb.ListRequest_TRASH {
|
||||
// For history lists, we need to use the current RV in the continue token
|
||||
// to ensure consistent pagination. The order depends on VersionMatch:
|
||||
// - NotOlderThan: ascending order (oldest to newest)
|
||||
@@ -918,7 +920,7 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err
|
||||
}
|
||||
|
||||
if rv < 1 {
|
||||
rsp.Error = &ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: fmt.Sprintf("invalid resource version for list: %v", rv),
|
||||
}
|
||||
@@ -957,7 +959,7 @@ func (s *server) initWatcher() error {
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
func (s *server) Watch(req *resourcepb.WatchRequest, srv resourcepb.ResourceStore_WatchServer) error {
|
||||
ctx := srv.Context()
|
||||
|
||||
user, ok := claims.AuthInfoFrom(ctx)
|
||||
@@ -999,14 +1001,14 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
var initialEventsRV int64 // resource version coming from the initial events
|
||||
if req.SendInitialEvents {
|
||||
// Backfill the stream by adding every existing entities.
|
||||
initialEventsRV, err = s.backend.ListIterator(ctx, &ListRequest{Options: req.Options}, func(iter ListIterator) error {
|
||||
initialEventsRV, err = s.backend.ListIterator(ctx, &resourcepb.ListRequest{Options: req.Options}, func(iter ListIterator) error {
|
||||
for iter.Next() {
|
||||
if err := iter.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.Send(&WatchEvent{
|
||||
Type: WatchEvent_ADDED,
|
||||
Resource: &WatchEvent_Resource{
|
||||
if err := srv.Send(&resourcepb.WatchEvent{
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Resource: &resourcepb.WatchEvent_Resource{
|
||||
Value: iter.Value(),
|
||||
Version: iter.ResourceVersion(),
|
||||
},
|
||||
@@ -1021,9 +1023,9 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
}
|
||||
}
|
||||
if req.SendInitialEvents && req.AllowWatchBookmarks {
|
||||
if err := srv.Send(&WatchEvent{
|
||||
Type: WatchEvent_BOOKMARK,
|
||||
Resource: &WatchEvent_Resource{
|
||||
if err := srv.Send(&resourcepb.WatchEvent{
|
||||
Type: resourcepb.WatchEvent_BOOKMARK,
|
||||
Resource: &resourcepb.WatchEvent_Resource{
|
||||
Version: initialEventsRV,
|
||||
},
|
||||
}); err != nil {
|
||||
@@ -1058,19 +1060,19 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
|
||||
value := event.Value
|
||||
// remove the delete marker stored in the value for deleted objects
|
||||
if event.Type == WatchEvent_DELETED {
|
||||
if event.Type == resourcepb.WatchEvent_DELETED {
|
||||
value = []byte{}
|
||||
}
|
||||
resp := &WatchEvent{
|
||||
resp := &resourcepb.WatchEvent{
|
||||
Timestamp: event.Timestamp,
|
||||
Type: event.Type,
|
||||
Resource: &WatchEvent_Resource{
|
||||
Resource: &resourcepb.WatchEvent_Resource{
|
||||
Value: value,
|
||||
Version: event.ResourceVersion,
|
||||
},
|
||||
}
|
||||
if event.PreviousRV > 0 {
|
||||
prevObj, err := s.Read(ctx, &ReadRequest{Key: event.Key, ResourceVersion: event.PreviousRV})
|
||||
prevObj, err := s.Read(ctx, &resourcepb.ReadRequest{Key: event.Key, ResourceVersion: event.PreviousRV})
|
||||
if err != nil {
|
||||
// This scenario should never happen, but if it does, we should log it and continue
|
||||
// sending the event without the previous object. The client will decide what to do.
|
||||
@@ -1080,7 +1082,7 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
s.log.Error("resource version mismatch", "key", event.Key, "resource_version", event.PreviousRV, "actual", prevObj.ResourceVersion)
|
||||
return fmt.Errorf("resource version mismatch")
|
||||
}
|
||||
resp.Previous = &WatchEvent_Resource{
|
||||
resp.Previous = &resourcepb.WatchEvent_Resource{
|
||||
Value: prevObj.Value,
|
||||
Version: prevObj.ResourceVersion,
|
||||
}
|
||||
@@ -1102,7 +1104,7 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) Search(ctx context.Context, req *ResourceSearchRequest) (*ResourceSearchResponse, error) {
|
||||
func (s *server) Search(ctx context.Context, req *resourcepb.ResourceSearchRequest) (*resourcepb.ResourceSearchResponse, error) {
|
||||
if s.search == nil {
|
||||
return nil, fmt.Errorf("search index not configured")
|
||||
}
|
||||
@@ -1118,7 +1120,7 @@ func (s *server) Search(ctx context.Context, req *ResourceSearchRequest) (*Resou
|
||||
}
|
||||
|
||||
// GetStats implements ResourceServer.
|
||||
func (s *server) GetStats(ctx context.Context, req *ResourceStatsRequest) (*ResourceStatsResponse, error) {
|
||||
func (s *server) GetStats(ctx context.Context, req *resourcepb.ResourceStatsRequest) (*resourcepb.ResourceStatsResponse, error) {
|
||||
if err := s.Init(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1132,7 +1134,7 @@ func (s *server) GetStats(ctx context.Context, req *ResourceStatsRequest) (*Reso
|
||||
|
||||
if s.search == nil {
|
||||
// If the backend implements "GetStats", we can use it
|
||||
srv, ok := s.backend.(ResourceIndexServer)
|
||||
srv, ok := s.backend.(resourcepb.ResourceIndexServer)
|
||||
if ok {
|
||||
return srv.GetStats(ctx, req)
|
||||
}
|
||||
@@ -1141,23 +1143,23 @@ func (s *server) GetStats(ctx context.Context, req *ResourceStatsRequest) (*Reso
|
||||
return s.search.GetStats(ctx, req)
|
||||
}
|
||||
|
||||
func (s *server) ListManagedObjects(ctx context.Context, req *ListManagedObjectsRequest) (*ListManagedObjectsResponse, error) {
|
||||
func (s *server) ListManagedObjects(ctx context.Context, req *resourcepb.ListManagedObjectsRequest) (*resourcepb.ListManagedObjectsResponse, error) {
|
||||
return s.search.ListManagedObjects(ctx, req)
|
||||
}
|
||||
|
||||
func (s *server) CountManagedObjects(ctx context.Context, req *CountManagedObjectsRequest) (*CountManagedObjectsResponse, error) {
|
||||
func (s *server) CountManagedObjects(ctx context.Context, req *resourcepb.CountManagedObjectsRequest) (*resourcepb.CountManagedObjectsResponse, error) {
|
||||
return s.search.CountManagedObjects(ctx, req)
|
||||
}
|
||||
|
||||
// IsHealthy implements ResourceServer.
|
||||
func (s *server) IsHealthy(ctx context.Context, req *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||
func (s *server) IsHealthy(ctx context.Context, req *resourcepb.HealthCheckRequest) (*resourcepb.HealthCheckResponse, error) {
|
||||
return s.diagnostics.IsHealthy(ctx, req)
|
||||
}
|
||||
|
||||
// GetBlob implements BlobStore.
|
||||
func (s *server) PutBlob(ctx context.Context, req *PutBlobRequest) (*PutBlobResponse, error) {
|
||||
func (s *server) PutBlob(ctx context.Context, req *resourcepb.PutBlobRequest) (*resourcepb.PutBlobResponse, error) {
|
||||
if s.blob == nil {
|
||||
return &PutBlobResponse{Error: &ErrorResult{
|
||||
return &resourcepb.PutBlobResponse{Error: &resourcepb.ErrorResult{
|
||||
Message: "blob store not configured",
|
||||
Code: http.StatusNotImplemented,
|
||||
}}, nil
|
||||
@@ -1170,12 +1172,12 @@ func (s *server) PutBlob(ctx context.Context, req *PutBlobRequest) (*PutBlobResp
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (s *server) getPartialObject(ctx context.Context, key *ResourceKey, rv int64) (utils.GrafanaMetaAccessor, *ErrorResult) {
|
||||
func (s *server) getPartialObject(ctx context.Context, key *resourcepb.ResourceKey, rv int64) (utils.GrafanaMetaAccessor, *resourcepb.ErrorResult) {
|
||||
if r := verifyRequestKey(key); r != nil {
|
||||
return nil, r
|
||||
}
|
||||
|
||||
rsp := s.backend.ReadResource(ctx, &ReadRequest{
|
||||
rsp := s.backend.ReadResource(ctx, &resourcepb.ReadRequest{
|
||||
Key: key,
|
||||
ResourceVersion: rv,
|
||||
})
|
||||
@@ -1196,9 +1198,9 @@ func (s *server) getPartialObject(ctx context.Context, key *ResourceKey, rv int6
|
||||
}
|
||||
|
||||
// GetBlob implements BlobStore.
|
||||
func (s *server) GetBlob(ctx context.Context, req *GetBlobRequest) (*GetBlobResponse, error) {
|
||||
func (s *server) GetBlob(ctx context.Context, req *resourcepb.GetBlobRequest) (*resourcepb.GetBlobResponse, error) {
|
||||
if s.blob == nil {
|
||||
return &GetBlobResponse{Error: &ErrorResult{
|
||||
return &resourcepb.GetBlobResponse{Error: &resourcepb.ErrorResult{
|
||||
Message: "blob store not configured",
|
||||
Code: http.StatusNotImplemented,
|
||||
}}, nil
|
||||
@@ -1209,12 +1211,12 @@ func (s *server) GetBlob(ctx context.Context, req *GetBlobRequest) (*GetBlobResp
|
||||
// The linked blob is stored in the resource metadata attributes
|
||||
obj, status := s.getPartialObject(ctx, req.Resource, req.ResourceVersion)
|
||||
if status != nil {
|
||||
return &GetBlobResponse{Error: status}, nil
|
||||
return &resourcepb.GetBlobResponse{Error: status}, nil
|
||||
}
|
||||
|
||||
info = obj.GetBlob()
|
||||
if info == nil || info.UID == "" {
|
||||
return &GetBlobResponse{Error: &ErrorResult{
|
||||
return &resourcepb.GetBlobResponse{Error: &resourcepb.ErrorResult{
|
||||
Message: "Resource does not have a linked blob",
|
||||
Code: 404,
|
||||
}}, nil
|
||||
|
||||
@@ -15,8 +15,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestSimpleServer(t *testing.T) {
|
||||
@@ -78,7 +80,7 @@ func TestSimpleServer(t *testing.T) {
|
||||
}
|
||||
}`)
|
||||
|
||||
key := &ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "playlist.grafana.app",
|
||||
Resource: "rrrr", // can be anything :(
|
||||
Namespace: "default",
|
||||
@@ -86,8 +88,8 @@ func TestSimpleServer(t *testing.T) {
|
||||
}
|
||||
|
||||
// Should be empty when we start
|
||||
all, err := server.List(ctx, &ListRequest{Options: &ListOptions{
|
||||
Key: &ResourceKey{
|
||||
all, err := server.List(ctx, &resourcepb.ListRequest{Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: key.Group,
|
||||
Resource: key.Resource,
|
||||
},
|
||||
@@ -96,12 +98,12 @@ func TestSimpleServer(t *testing.T) {
|
||||
require.Len(t, all.Items, 0)
|
||||
|
||||
// should return 404 if not found
|
||||
found, err := server.Read(ctx, &ReadRequest{Key: key})
|
||||
found, err := server.Read(ctx, &resourcepb.ReadRequest{Key: key})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, found.Error)
|
||||
require.Equal(t, int32(http.StatusNotFound), found.Error.Code)
|
||||
|
||||
created, err := server.Create(ctx, &CreateRequest{
|
||||
created, err := server.Create(ctx, &resourcepb.CreateRequest{
|
||||
Value: raw,
|
||||
Key: key,
|
||||
})
|
||||
@@ -110,7 +112,7 @@ func TestSimpleServer(t *testing.T) {
|
||||
require.True(t, created.ResourceVersion > 0)
|
||||
|
||||
// The key does not include resource version
|
||||
found, err = server.Read(ctx, &ReadRequest{Key: key})
|
||||
found, err = server.Read(ctx, &resourcepb.ReadRequest{Key: key})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, found.Error)
|
||||
require.Equal(t, created.ResourceVersion, found.ResourceVersion)
|
||||
@@ -131,7 +133,7 @@ func TestSimpleServer(t *testing.T) {
|
||||
})
|
||||
raw, err = json.Marshal(tmp)
|
||||
require.NoError(t, err)
|
||||
updated, err := server.Update(ctx, &UpdateRequest{
|
||||
updated, err := server.Update(ctx, &resourcepb.UpdateRequest{
|
||||
Key: key,
|
||||
Value: raw,
|
||||
ResourceVersion: created.ResourceVersion})
|
||||
@@ -142,7 +144,7 @@ func TestSimpleServer(t *testing.T) {
|
||||
obj.SetLabels(nil)
|
||||
raw, err = json.Marshal(tmp)
|
||||
require.NoError(t, err)
|
||||
updated, err = server.Update(ctx, &UpdateRequest{
|
||||
updated, err = server.Update(ctx, &resourcepb.UpdateRequest{
|
||||
Key: key,
|
||||
Value: raw,
|
||||
ResourceVersion: created.ResourceVersion})
|
||||
@@ -151,13 +153,13 @@ func TestSimpleServer(t *testing.T) {
|
||||
require.True(t, updated.ResourceVersion > created.ResourceVersion)
|
||||
|
||||
// We should still get the latest
|
||||
found, err = server.Read(ctx, &ReadRequest{Key: key})
|
||||
found, err = server.Read(ctx, &resourcepb.ReadRequest{Key: key})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, found.Error)
|
||||
require.Equal(t, updated.ResourceVersion, found.ResourceVersion)
|
||||
|
||||
all, err = server.List(ctx, &ListRequest{Options: &ListOptions{
|
||||
Key: &ResourceKey{
|
||||
all, err = server.List(ctx, &resourcepb.ListRequest{Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: key.Group,
|
||||
Resource: key.Resource,
|
||||
},
|
||||
@@ -166,19 +168,19 @@ func TestSimpleServer(t *testing.T) {
|
||||
require.Len(t, all.Items, 1)
|
||||
require.Equal(t, updated.ResourceVersion, all.Items[0].ResourceVersion)
|
||||
|
||||
deleted, err := server.Delete(ctx, &DeleteRequest{Key: key, ResourceVersion: updated.ResourceVersion})
|
||||
deleted, err := server.Delete(ctx, &resourcepb.DeleteRequest{Key: key, ResourceVersion: updated.ResourceVersion})
|
||||
require.NoError(t, err)
|
||||
require.True(t, deleted.ResourceVersion > updated.ResourceVersion)
|
||||
|
||||
// We should get not found status when trying to read the latest value
|
||||
found, err = server.Read(ctx, &ReadRequest{Key: key})
|
||||
found, err = server.Read(ctx, &resourcepb.ReadRequest{Key: key})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, found.Error)
|
||||
require.Equal(t, int32(404), found.Error.Code)
|
||||
|
||||
// And the deleted value should not be in the results
|
||||
all, err = server.List(ctx, &ListRequest{Options: &ListOptions{
|
||||
Key: &ResourceKey{
|
||||
all, err = server.List(ctx, &resourcepb.ListRequest{Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: key.Group,
|
||||
Resource: key.Resource,
|
||||
},
|
||||
@@ -213,14 +215,14 @@ func TestSimpleServer(t *testing.T) {
|
||||
}
|
||||
}`)
|
||||
|
||||
key := &ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "playlist.grafana.app",
|
||||
Resource: "rrrr", // can be anything :(
|
||||
Namespace: "default",
|
||||
Name: "fdgsv37qslr0ga",
|
||||
}
|
||||
|
||||
created, err := server.Create(ctx, &CreateRequest{
|
||||
created, err := server.Create(ctx, &resourcepb.CreateRequest{
|
||||
Value: raw,
|
||||
Key: key,
|
||||
})
|
||||
@@ -228,13 +230,13 @@ func TestSimpleServer(t *testing.T) {
|
||||
|
||||
// Update should return an ErrOptimisticLockingFailed the second time
|
||||
|
||||
_, err = server.Update(ctx, &UpdateRequest{
|
||||
_, err = server.Update(ctx, &resourcepb.UpdateRequest{
|
||||
Key: key,
|
||||
Value: raw,
|
||||
ResourceVersion: created.ResourceVersion})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = server.Update(ctx, &UpdateRequest{
|
||||
_, err = server.Update(ctx, &resourcepb.UpdateRequest{
|
||||
Key: key,
|
||||
Value: raw,
|
||||
ResourceVersion: created.ResourceVersion})
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
reflect "reflect"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -20,10 +20,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data/utils/jsoniter"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
// Convert the the protobuf model into k8s (will decode each value)
|
||||
func (x *ResourceTable) ToK8s() (metav1.Table, error) {
|
||||
func toK8s(x *resourcepb.ResourceTable) (metav1.Table, error) {
|
||||
table := metav1.Table{
|
||||
ListMeta: metav1.ListMeta{
|
||||
Continue: x.NextPageToken,
|
||||
@@ -103,7 +105,7 @@ func (x *ResourceTable) ToK8s() (metav1.Table, error) {
|
||||
}
|
||||
|
||||
type TableBuilder struct {
|
||||
ResourceTable
|
||||
resourcepb.ResourceTable
|
||||
|
||||
lookup map[string]*resourceTableColumn
|
||||
|
||||
@@ -113,9 +115,9 @@ type TableBuilder struct {
|
||||
|
||||
type ResourceColumnEncoder = func(v any) ([]byte, error)
|
||||
|
||||
func NewTableBuilder(cols []*ResourceTableColumnDefinition) (*TableBuilder, error) {
|
||||
func NewTableBuilder(cols []*resourcepb.ResourceTableColumnDefinition) (*TableBuilder, error) {
|
||||
table := &TableBuilder{
|
||||
ResourceTable: ResourceTable{
|
||||
ResourceTable: resourcepb.ResourceTable{
|
||||
Columns: cols,
|
||||
},
|
||||
|
||||
@@ -147,8 +149,8 @@ func (x *TableBuilder) Encoders() []ResourceColumnEncoder {
|
||||
return encoders
|
||||
}
|
||||
|
||||
func (x *TableBuilder) AddRow(key *ResourceKey, rv int64, vals map[string]any) error {
|
||||
row := &ResourceTableRow{
|
||||
func (x *TableBuilder) AddRow(key *resourcepb.ResourceKey, rv int64, vals map[string]any) error {
|
||||
row := &resourcepb.ResourceTableRow{
|
||||
Key: key,
|
||||
ResourceVersion: rv,
|
||||
Cells: make([][]byte, len(x.Columns)),
|
||||
@@ -171,7 +173,7 @@ func (x *TableBuilder) AddRow(key *ResourceKey, rv int64, vals map[string]any) e
|
||||
}
|
||||
|
||||
type resourceTableColumn struct {
|
||||
def *ResourceTableColumnDefinition
|
||||
def *resourcepb.ResourceTableColumnDefinition
|
||||
index int
|
||||
|
||||
// Used for array indexing
|
||||
@@ -183,7 +185,7 @@ type resourceTableColumn struct {
|
||||
}
|
||||
|
||||
// helper to decode a cell value
|
||||
func DecodeCell(columnDef *ResourceTableColumnDefinition, index int, cellVal []byte) (any, error) {
|
||||
func DecodeCell(columnDef *resourcepb.ResourceTableColumnDefinition, index int, cellVal []byte) (any, error) {
|
||||
col, err := newResourceTableColumn(columnDef, index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -196,48 +198,48 @@ func DecodeCell(columnDef *ResourceTableColumnDefinition, index int, cellVal []b
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func newResourceTableColumn(def *ResourceTableColumnDefinition, index int) (*resourceTableColumn, error) {
|
||||
func newResourceTableColumn(def *resourcepb.ResourceTableColumnDefinition, index int) (*resourceTableColumn, error) {
|
||||
col := &resourceTableColumn{def: def, index: index}
|
||||
|
||||
// Initially ignore the array property, we wil wrap that at the end
|
||||
switch def.Type {
|
||||
case ResourceTableColumnDefinition_UNKNOWN_TYPE:
|
||||
case resourcepb.ResourceTableColumnDefinition_UNKNOWN_TYPE:
|
||||
return nil, fmt.Errorf("unknown column type")
|
||||
|
||||
case ResourceTableColumnDefinition_STRING:
|
||||
case resourcepb.ResourceTableColumnDefinition_STRING:
|
||||
col.OpenAPIType = "string"
|
||||
col.reader = func(iter *jsoniter.Iterator) (any, error) {
|
||||
return iter.ReadString()
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_BOOLEAN:
|
||||
case resourcepb.ResourceTableColumnDefinition_BOOLEAN:
|
||||
col.OpenAPIType = "boolean"
|
||||
col.reader = func(iter *jsoniter.Iterator) (any, error) {
|
||||
return iter.ReadBool()
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_INT32:
|
||||
case resourcepb.ResourceTableColumnDefinition_INT32:
|
||||
col.OpenAPIType = "number"
|
||||
col.OpenAPIFormat = "int32"
|
||||
col.reader = func(iter *jsoniter.Iterator) (any, error) {
|
||||
return iter.ReadInt32()
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_INT64:
|
||||
case resourcepb.ResourceTableColumnDefinition_INT64:
|
||||
col.OpenAPIType = "number"
|
||||
col.OpenAPIFormat = "int64"
|
||||
col.reader = func(iter *jsoniter.Iterator) (any, error) {
|
||||
return iter.ReadInt64()
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_DOUBLE:
|
||||
case resourcepb.ResourceTableColumnDefinition_DOUBLE:
|
||||
col.OpenAPIType = "number"
|
||||
col.OpenAPIFormat = "double"
|
||||
col.reader = func(iter *jsoniter.Iterator) (any, error) {
|
||||
return iter.ReadFloat64()
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_FLOAT:
|
||||
case resourcepb.ResourceTableColumnDefinition_FLOAT:
|
||||
col.OpenAPIType = "number"
|
||||
col.OpenAPIFormat = "float"
|
||||
col.reader = func(iter *jsoniter.Iterator) (any, error) {
|
||||
@@ -245,10 +247,10 @@ func newResourceTableColumn(def *ResourceTableColumnDefinition, index int) (*res
|
||||
}
|
||||
|
||||
// Encode everything we can -- the lower conversion can happen later?
|
||||
case ResourceTableColumnDefinition_DATE, ResourceTableColumnDefinition_DATE_TIME:
|
||||
case resourcepb.ResourceTableColumnDefinition_DATE, resourcepb.ResourceTableColumnDefinition_DATE_TIME:
|
||||
col.OpenAPIType = "string"
|
||||
col.OpenAPIFormat = "date"
|
||||
if def.Type == ResourceTableColumnDefinition_DATE_TIME {
|
||||
if def.Type == resourcepb.ResourceTableColumnDefinition_DATE_TIME {
|
||||
col.OpenAPIFormat = "date_time"
|
||||
}
|
||||
col.writer = func(v any, stream *jsoniter.Stream) error {
|
||||
@@ -287,7 +289,7 @@ func newResourceTableColumn(def *ResourceTableColumnDefinition, index int) (*res
|
||||
}
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_BINARY:
|
||||
case resourcepb.ResourceTableColumnDefinition_BINARY:
|
||||
col.OpenAPIType = "binary"
|
||||
col.writer = func(v any, stream *jsoniter.Stream) error {
|
||||
b, ok := v.([]byte)
|
||||
@@ -306,7 +308,7 @@ func newResourceTableColumn(def *ResourceTableColumnDefinition, index int) (*res
|
||||
return base64.StdEncoding.DecodeString(str)
|
||||
}
|
||||
|
||||
case ResourceTableColumnDefinition_OBJECT:
|
||||
case resourcepb.ResourceTableColumnDefinition_OBJECT:
|
||||
col.OpenAPIType = "string"
|
||||
col.OpenAPIFormat = "json"
|
||||
|
||||
@@ -337,7 +339,7 @@ func (x *resourceTableColumn) Encode(v any) ([]byte, error) {
|
||||
// Arrays will always use JSON formatting
|
||||
if !x.def.IsArray {
|
||||
switch x.def.Type {
|
||||
case ResourceTableColumnDefinition_STRING:
|
||||
case resourcepb.ResourceTableColumnDefinition_STRING:
|
||||
{
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
@@ -345,7 +347,7 @@ func (x *resourceTableColumn) Encode(v any) ([]byte, error) {
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
case ResourceTableColumnDefinition_BINARY:
|
||||
case resourcepb.ResourceTableColumnDefinition_BINARY:
|
||||
{
|
||||
s, ok := v.([]byte)
|
||||
if !ok {
|
||||
@@ -353,7 +355,7 @@ func (x *resourceTableColumn) Encode(v any) ([]byte, error) {
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
case ResourceTableColumnDefinition_BOOLEAN:
|
||||
case resourcepb.ResourceTableColumnDefinition_BOOLEAN:
|
||||
{
|
||||
b, ok := v.(bool)
|
||||
if !ok {
|
||||
@@ -375,7 +377,7 @@ func (x *resourceTableColumn) Encode(v any) ([]byte, error) {
|
||||
}
|
||||
return []byte{0}, nil
|
||||
}
|
||||
case ResourceTableColumnDefinition_DATE_TIME, ResourceTableColumnDefinition_DATE:
|
||||
case resourcepb.ResourceTableColumnDefinition_DATE_TIME, resourcepb.ResourceTableColumnDefinition_DATE:
|
||||
{
|
||||
f, ok := v.(time.Time)
|
||||
if !ok {
|
||||
@@ -397,7 +399,7 @@ func (x *resourceTableColumn) Encode(v any) ([]byte, error) {
|
||||
err := binary.Write(&buf, binary.BigEndian, ts)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
case ResourceTableColumnDefinition_DOUBLE:
|
||||
case resourcepb.ResourceTableColumnDefinition_DOUBLE:
|
||||
{
|
||||
f, ok := v.(float64)
|
||||
if !ok {
|
||||
@@ -420,7 +422,7 @@ func (x *resourceTableColumn) Encode(v any) ([]byte, error) {
|
||||
err := binary.Write(&buf, binary.BigEndian, f)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
case ResourceTableColumnDefinition_INT64:
|
||||
case resourcepb.ResourceTableColumnDefinition_INT64:
|
||||
{
|
||||
f, ok := v.(int64)
|
||||
if !ok {
|
||||
@@ -516,15 +518,15 @@ func (x *resourceTableColumn) Decode(buff []byte) (any, error) {
|
||||
}
|
||||
if !x.def.IsArray {
|
||||
switch x.def.Type {
|
||||
case ResourceTableColumnDefinition_STRING:
|
||||
case resourcepb.ResourceTableColumnDefinition_STRING:
|
||||
return string(buff), nil
|
||||
case ResourceTableColumnDefinition_BINARY:
|
||||
case resourcepb.ResourceTableColumnDefinition_BINARY:
|
||||
return buff, nil
|
||||
case ResourceTableColumnDefinition_BOOLEAN:
|
||||
case resourcepb.ResourceTableColumnDefinition_BOOLEAN:
|
||||
if len(buff) == 1 {
|
||||
return buff[0] != 0, nil
|
||||
}
|
||||
case ResourceTableColumnDefinition_DOUBLE:
|
||||
case resourcepb.ResourceTableColumnDefinition_DOUBLE:
|
||||
{
|
||||
var f float64
|
||||
count, err := binary.Decode(buff, binary.BigEndian, &f)
|
||||
@@ -532,7 +534,7 @@ func (x *resourceTableColumn) Decode(buff []byte) (any, error) {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
case ResourceTableColumnDefinition_INT64:
|
||||
case resourcepb.ResourceTableColumnDefinition_INT64:
|
||||
{
|
||||
var f int64
|
||||
count, err := binary.Decode(buff, binary.BigEndian, &f)
|
||||
@@ -540,7 +542,7 @@ func (x *resourceTableColumn) Decode(buff []byte) (any, error) {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
case ResourceTableColumnDefinition_DATE_TIME, ResourceTableColumnDefinition_DATE:
|
||||
case resourcepb.ResourceTableColumnDefinition_DATE_TIME, resourcepb.ResourceTableColumnDefinition_DATE:
|
||||
{
|
||||
var f int64
|
||||
count, err := binary.Decode(buff, binary.BigEndian, &f)
|
||||
@@ -587,10 +589,10 @@ func (x *resourceTableColumn) Decode(buff []byte) (any, error) {
|
||||
}
|
||||
|
||||
// AssertTableSnapshot will match a ResourceTable vs the saved value
|
||||
func AssertTableSnapshot(t *testing.T, path string, table *ResourceTable) {
|
||||
func AssertTableSnapshot(t *testing.T, path string, table *resourcepb.ResourceTable) {
|
||||
t.Helper()
|
||||
|
||||
k8sTable, err := table.ToK8s()
|
||||
k8sTable, err := toK8s(table)
|
||||
require.NoError(t, err, "unable to create table response", path)
|
||||
actual, err := json.MarshalIndent(k8sTable, "", " ")
|
||||
require.NoError(t, err, "unable to write table json", path)
|
||||
|
||||
@@ -10,27 +10,29 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestTableFormat(t *testing.T) {
|
||||
columns := []*ResourceTableColumnDefinition{
|
||||
columns := []*resourcepb.ResourceTableColumnDefinition{
|
||||
{
|
||||
Name: "title",
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
},
|
||||
{
|
||||
Name: "stats.count",
|
||||
Type: ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
},
|
||||
{
|
||||
Name: "number",
|
||||
Type: ResourceTableColumnDefinition_DOUBLE,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DOUBLE,
|
||||
|
||||
Description: "float64 value",
|
||||
},
|
||||
{
|
||||
Name: "tags",
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
IsArray: true,
|
||||
},
|
||||
}
|
||||
@@ -39,7 +41,7 @@ func TestTableFormat(t *testing.T) {
|
||||
builder, err := NewTableBuilder(columns)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = builder.AddRow(&ResourceKey{
|
||||
err = builder.AddRow(&resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "ggg",
|
||||
Resource: "xyz", // does not have a home in table!
|
||||
@@ -51,7 +53,7 @@ func TestTableFormat(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = builder.AddRow(&ResourceKey{
|
||||
err = builder.AddRow(&resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "ggg",
|
||||
Resource: "xyz", // does not have a home in table!
|
||||
@@ -70,7 +72,7 @@ func TestTableFormat(t *testing.T) {
|
||||
func TestColumnEncoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
// The table definition
|
||||
def *ResourceTableColumnDefinition
|
||||
def *resourcepb.ResourceTableColumnDefinition
|
||||
|
||||
// Passed to the encode function
|
||||
input any
|
||||
@@ -88,101 +90,101 @@ func TestColumnEncoding(t *testing.T) {
|
||||
output_err error
|
||||
}{
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
},
|
||||
input: "aaa", // expects output to match
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_STRING,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
IsArray: true,
|
||||
},
|
||||
input: "bbb",
|
||||
output: []any{"bbb"},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_INT64,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
},
|
||||
input: 12345,
|
||||
output: int64(12345),
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_INT64,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
IsArray: true,
|
||||
},
|
||||
input: 12345,
|
||||
output: []any{int64(12345)},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_DOUBLE,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DOUBLE,
|
||||
},
|
||||
input: 12345,
|
||||
output: float64(12345),
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_DOUBLE,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DOUBLE,
|
||||
IsArray: true,
|
||||
},
|
||||
input: 12345,
|
||||
output: []any{float64(12345)},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_BOOLEAN,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_BOOLEAN,
|
||||
},
|
||||
input: true,
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_BOOLEAN,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_BOOLEAN,
|
||||
IsArray: true,
|
||||
},
|
||||
input: []any{true, false, true},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_FLOAT,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_FLOAT,
|
||||
},
|
||||
input: 23.4,
|
||||
output: float32(23.4),
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_FLOAT,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_FLOAT,
|
||||
IsArray: true,
|
||||
},
|
||||
input: 23.4,
|
||||
output: []any{float32(23.4)},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_INT32,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT32,
|
||||
},
|
||||
input: 56,
|
||||
output: int32(56),
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_INT32,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT32,
|
||||
IsArray: true,
|
||||
},
|
||||
input: 56,
|
||||
output: []any{int32(56)},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_DATE_TIME,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DATE_TIME,
|
||||
},
|
||||
input: time.UnixMilli(946674000000).UTC(),
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_DATE_TIME,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DATE_TIME,
|
||||
IsArray: true,
|
||||
},
|
||||
input: time.UnixMilli(946674000000).UTC(),
|
||||
@@ -191,14 +193,14 @@ func TestColumnEncoding(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_DATE,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DATE,
|
||||
},
|
||||
input: time.UnixMilli(946674000000).UTC(),
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_DATE,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_DATE,
|
||||
IsArray: true,
|
||||
},
|
||||
input: time.UnixMilli(946674000000).UTC(),
|
||||
@@ -207,14 +209,14 @@ func TestColumnEncoding(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_BINARY,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_BINARY,
|
||||
},
|
||||
input: []byte{1, 2, 3, 4},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_BINARY,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_BINARY,
|
||||
IsArray: true,
|
||||
},
|
||||
input: []any{
|
||||
@@ -222,16 +224,16 @@ func TestColumnEncoding(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_OBJECT,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_OBJECT,
|
||||
},
|
||||
input: map[string]any{
|
||||
"hello": "world",
|
||||
},
|
||||
},
|
||||
{
|
||||
def: &ResourceTableColumnDefinition{
|
||||
Type: ResourceTableColumnDefinition_OBJECT,
|
||||
def: &resourcepb.ResourceTableColumnDefinition{
|
||||
Type: resourcepb.ResourceTableColumnDefinition_OBJECT,
|
||||
IsArray: true,
|
||||
},
|
||||
input: map[string]any{
|
||||
@@ -246,8 +248,8 @@ func TestColumnEncoding(t *testing.T) {
|
||||
}
|
||||
|
||||
// Keep track of the types that have tests
|
||||
testedTypes := make(map[ResourceTableColumnDefinition_ColumnType]bool)
|
||||
testedArrays := make(map[ResourceTableColumnDefinition_ColumnType]bool)
|
||||
testedTypes := make(map[resourcepb.ResourceTableColumnDefinition_ColumnType]bool)
|
||||
testedArrays := make(map[resourcepb.ResourceTableColumnDefinition_ColumnType]bool)
|
||||
for _, test := range tests {
|
||||
var sb strings.Builder
|
||||
if test.def.IsArray {
|
||||
@@ -301,7 +303,7 @@ func TestColumnEncoding(t *testing.T) {
|
||||
missingArrays := []string{}
|
||||
|
||||
// Make sure we have at least one test for each type
|
||||
for i := ResourceTableColumnDefinition_STRING; i <= ResourceTableColumnDefinition_OBJECT; i++ {
|
||||
for i := resourcepb.ResourceTableColumnDefinition_STRING; i <= resourcepb.ResourceTableColumnDefinition_OBJECT; i++ {
|
||||
if !testedTypes[i] {
|
||||
missingTypes = append(missingTypes, i.String())
|
||||
}
|
||||
@@ -316,7 +318,7 @@ func TestColumnEncoding(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecodeCell(t *testing.T) {
|
||||
colDef := &ResourceTableColumnDefinition{Type: ResourceTableColumnDefinition_INT64}
|
||||
colDef := &resourcepb.ResourceTableColumnDefinition{Type: resourcepb.ResourceTableColumnDefinition_INT64}
|
||||
var buf bytes.Buffer
|
||||
err := binary.Write(&buf, binary.BigEndian, int64(123))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -2,12 +2,14 @@ package resource
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
var validNameCharPattern = `a-zA-Z0-9:\-\_\.`
|
||||
var validNamePattern = regexp.MustCompile(`^[` + validNameCharPattern + `]*$`).MatchString
|
||||
|
||||
func validateName(name string) *ErrorResult {
|
||||
func validateName(name string) *resourcepb.ErrorResult {
|
||||
if len(name) == 0 {
|
||||
return NewBadRequestError("name is too short")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,520 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc (unknown)
|
||||
// source: blob.proto
|
||||
|
||||
package resourcepb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type PutBlobRequest_Method int32
|
||||
|
||||
const (
|
||||
// Use the inline raw []byte
|
||||
PutBlobRequest_GRPC PutBlobRequest_Method = 0
|
||||
// Get a signed URL and PUT the value
|
||||
PutBlobRequest_HTTP PutBlobRequest_Method = 1
|
||||
)
|
||||
|
||||
// Enum value maps for PutBlobRequest_Method.
|
||||
var (
|
||||
PutBlobRequest_Method_name = map[int32]string{
|
||||
0: "GRPC",
|
||||
1: "HTTP",
|
||||
}
|
||||
PutBlobRequest_Method_value = map[string]int32{
|
||||
"GRPC": 0,
|
||||
"HTTP": 1,
|
||||
}
|
||||
)
|
||||
|
||||
func (x PutBlobRequest_Method) Enum() *PutBlobRequest_Method {
|
||||
p := new(PutBlobRequest_Method)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x PutBlobRequest_Method) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (PutBlobRequest_Method) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_blob_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (PutBlobRequest_Method) Type() protoreflect.EnumType {
|
||||
return &file_blob_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x PutBlobRequest_Method) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PutBlobRequest_Method.Descriptor instead.
|
||||
func (PutBlobRequest_Method) EnumDescriptor() ([]byte, []int) {
|
||||
return file_blob_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
type PutBlobRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The resource that will use this blob
|
||||
// NOTE: the name may not yet exist, but group+resource are required
|
||||
Resource *ResourceKey `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"`
|
||||
// How to upload
|
||||
Method PutBlobRequest_Method `protobuf:"varint,2,opt,name=method,proto3,enum=resource.PutBlobRequest_Method" json:"method,omitempty"`
|
||||
// Content type header
|
||||
ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
// Raw value to write
|
||||
// Not valid when method == HTTP
|
||||
Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PutBlobRequest) Reset() {
|
||||
*x = PutBlobRequest{}
|
||||
mi := &file_blob_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PutBlobRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PutBlobRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PutBlobRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_blob_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PutBlobRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PutBlobRequest) Descriptor() ([]byte, []int) {
|
||||
return file_blob_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *PutBlobRequest) GetResource() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Resource
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PutBlobRequest) GetMethod() PutBlobRequest_Method {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return PutBlobRequest_GRPC
|
||||
}
|
||||
|
||||
func (x *PutBlobRequest) GetContentType() string {
|
||||
if x != nil {
|
||||
return x.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PutBlobRequest) GetValue() []byte {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PutBlobResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Error details
|
||||
Error *ErrorResult `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
|
||||
// The blob uid. This must be saved into the resource to support access
|
||||
Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"`
|
||||
// The URL where this value can be PUT
|
||||
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
|
||||
// Size of the uploaded blob
|
||||
Size int64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"`
|
||||
// Content hash used for an etag
|
||||
Hash string `protobuf:"bytes,5,opt,name=hash,proto3" json:"hash,omitempty"`
|
||||
// Validated mimetype (from content_type)
|
||||
MimeType string `protobuf:"bytes,6,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"`
|
||||
// Validated charset (from content_type)
|
||||
Charset string `protobuf:"bytes,7,opt,name=charset,proto3" json:"charset,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) Reset() {
|
||||
*x = PutBlobResponse{}
|
||||
mi := &file_blob_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PutBlobResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PutBlobResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_blob_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PutBlobResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PutBlobResponse) Descriptor() ([]byte, []int) {
|
||||
return file_blob_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetError() *ErrorResult {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetUid() string {
|
||||
if x != nil {
|
||||
return x.Uid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetSize() int64 {
|
||||
if x != nil {
|
||||
return x.Size
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetHash() string {
|
||||
if x != nil {
|
||||
return x.Hash
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetMimeType() string {
|
||||
if x != nil {
|
||||
return x.MimeType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PutBlobResponse) GetCharset() string {
|
||||
if x != nil {
|
||||
return x.Charset
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetBlobRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Resource *ResourceKey `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"`
|
||||
// The new resource version
|
||||
ResourceVersion int64 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"`
|
||||
// Do not return a pre-signed URL (when possible)
|
||||
MustProxyBytes bool `protobuf:"varint,3,opt,name=must_proxy_bytes,json=mustProxyBytes,proto3" json:"must_proxy_bytes,omitempty"`
|
||||
// The blob UID -- when empty, the value is loaded from annotations in the matching resource
|
||||
Uid string `protobuf:"bytes,4,opt,name=uid,proto3" json:"uid,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetBlobRequest) Reset() {
|
||||
*x = GetBlobRequest{}
|
||||
mi := &file_blob_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetBlobRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetBlobRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetBlobRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_blob_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetBlobRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetBlobRequest) Descriptor() ([]byte, []int) {
|
||||
return file_blob_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GetBlobRequest) GetResource() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Resource
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GetBlobRequest) GetResourceVersion() int64 {
|
||||
if x != nil {
|
||||
return x.ResourceVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetBlobRequest) GetMustProxyBytes() bool {
|
||||
if x != nil {
|
||||
return x.MustProxyBytes
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *GetBlobRequest) GetUid() string {
|
||||
if x != nil {
|
||||
return x.Uid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetBlobResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Error details
|
||||
Error *ErrorResult `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
|
||||
// (optional) When possible, the system will return a presigned URL
|
||||
// that can be used to actually read the full blob+metadata
|
||||
// When this is set, neither info nor value will be set
|
||||
Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
|
||||
// Content type
|
||||
ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
// The raw object value
|
||||
Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetBlobResponse) Reset() {
|
||||
*x = GetBlobResponse{}
|
||||
mi := &file_blob_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetBlobResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetBlobResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetBlobResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_blob_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetBlobResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetBlobResponse) Descriptor() ([]byte, []int) {
|
||||
return file_blob_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GetBlobResponse) GetError() *ErrorResult {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GetBlobResponse) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetBlobResponse) GetContentType() string {
|
||||
if x != nil {
|
||||
return x.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetBlobResponse) GetValue() []byte {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_blob_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_blob_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd3, 0x01, 0x0a, 0x0e, 0x50, 0x75, 0x74, 0x42, 0x6c,
|
||||
0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b,
|
||||
0x65, 0x79, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x06,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x06, 0x6d,
|
||||
0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1c,
|
||||
0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43,
|
||||
0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x01, 0x22, 0xc1, 0x01, 0x0a,
|
||||
0x0f, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72,
|
||||
0x6c, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||
0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d,
|
||||
0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69,
|
||||
0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
|
||||
0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74,
|
||||
0x22, 0xaa, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
|
||||
0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x75, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f,
|
||||
0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x73,
|
||||
0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75,
|
||||
0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0x89, 0x01,
|
||||
0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x8b, 0x01, 0x0a, 0x09, 0x42, 0x6c,
|
||||
0x6f, 0x62, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x50, 0x75, 0x74, 0x42, 0x6c,
|
||||
0x6f, 0x62, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75,
|
||||
0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c,
|
||||
0x6f, 0x62, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72,
|
||||
0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
file_blob_proto_rawDescOnce sync.Once
|
||||
file_blob_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_blob_proto_rawDescGZIP() []byte {
|
||||
file_blob_proto_rawDescOnce.Do(func() {
|
||||
file_blob_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_blob_proto_rawDesc), len(file_blob_proto_rawDesc)))
|
||||
})
|
||||
return file_blob_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_blob_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_blob_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_blob_proto_goTypes = []any{
|
||||
(PutBlobRequest_Method)(0), // 0: resource.PutBlobRequest.Method
|
||||
(*PutBlobRequest)(nil), // 1: resource.PutBlobRequest
|
||||
(*PutBlobResponse)(nil), // 2: resource.PutBlobResponse
|
||||
(*GetBlobRequest)(nil), // 3: resource.GetBlobRequest
|
||||
(*GetBlobResponse)(nil), // 4: resource.GetBlobResponse
|
||||
(*ResourceKey)(nil), // 5: resource.ResourceKey
|
||||
(*ErrorResult)(nil), // 6: resource.ErrorResult
|
||||
}
|
||||
var file_blob_proto_depIdxs = []int32{
|
||||
5, // 0: resource.PutBlobRequest.resource:type_name -> resource.ResourceKey
|
||||
0, // 1: resource.PutBlobRequest.method:type_name -> resource.PutBlobRequest.Method
|
||||
6, // 2: resource.PutBlobResponse.error:type_name -> resource.ErrorResult
|
||||
5, // 3: resource.GetBlobRequest.resource:type_name -> resource.ResourceKey
|
||||
6, // 4: resource.GetBlobResponse.error:type_name -> resource.ErrorResult
|
||||
1, // 5: resource.BlobStore.PutBlob:input_type -> resource.PutBlobRequest
|
||||
3, // 6: resource.BlobStore.GetBlob:input_type -> resource.GetBlobRequest
|
||||
2, // 7: resource.BlobStore.PutBlob:output_type -> resource.PutBlobResponse
|
||||
4, // 8: resource.BlobStore.GetBlob:output_type -> resource.GetBlobResponse
|
||||
7, // [7:9] is the sub-list for method output_type
|
||||
5, // [5:7] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_blob_proto_init() }
|
||||
func file_blob_proto_init() {
|
||||
if File_blob_proto != nil {
|
||||
return
|
||||
}
|
||||
file_resource_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_blob_proto_rawDesc), len(file_blob_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_blob_proto_goTypes,
|
||||
DependencyIndexes: file_blob_proto_depIdxs,
|
||||
EnumInfos: file_blob_proto_enumTypes,
|
||||
MessageInfos: file_blob_proto_msgTypes,
|
||||
}.Build()
|
||||
File_blob_proto = out.File
|
||||
file_blob_proto_goTypes = nil
|
||||
file_blob_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.4.0
|
||||
// - protoc (unknown)
|
||||
// source: blob.proto
|
||||
|
||||
package resourcepb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.62.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion8
|
||||
|
||||
const (
|
||||
BlobStore_PutBlob_FullMethodName = "/resource.BlobStore/PutBlob"
|
||||
BlobStore_GetBlob_FullMethodName = "/resource.BlobStore/GetBlob"
|
||||
)
|
||||
|
||||
// BlobStoreClient is the client API for BlobStore service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type BlobStoreClient interface {
|
||||
// Upload a blob that will be saved in a resource
|
||||
PutBlob(ctx context.Context, in *PutBlobRequest, opts ...grpc.CallOption) (*PutBlobResponse, error)
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error)
|
||||
}
|
||||
|
||||
type blobStoreClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewBlobStoreClient(cc grpc.ClientConnInterface) BlobStoreClient {
|
||||
return &blobStoreClient{cc}
|
||||
}
|
||||
|
||||
func (c *blobStoreClient) PutBlob(ctx context.Context, in *PutBlobRequest, opts ...grpc.CallOption) (*PutBlobResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(PutBlobResponse)
|
||||
err := c.cc.Invoke(ctx, BlobStore_PutBlob_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *blobStoreClient) GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetBlobResponse)
|
||||
err := c.cc.Invoke(ctx, BlobStore_GetBlob_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// BlobStoreServer is the server API for BlobStore service.
|
||||
// All implementations should embed UnimplementedBlobStoreServer
|
||||
// for forward compatibility
|
||||
type BlobStoreServer interface {
|
||||
// Upload a blob that will be saved in a resource
|
||||
PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error)
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedBlobStoreServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedBlobStoreServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedBlobStoreServer) PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PutBlob not implemented")
|
||||
}
|
||||
func (UnimplementedBlobStoreServer) GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBlob not implemented")
|
||||
}
|
||||
|
||||
// UnsafeBlobStoreServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to BlobStoreServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeBlobStoreServer interface {
|
||||
mustEmbedUnimplementedBlobStoreServer()
|
||||
}
|
||||
|
||||
func RegisterBlobStoreServer(s grpc.ServiceRegistrar, srv BlobStoreServer) {
|
||||
s.RegisterService(&BlobStore_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _BlobStore_PutBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PutBlobRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BlobStoreServer).PutBlob(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: BlobStore_PutBlob_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BlobStoreServer).PutBlob(ctx, req.(*PutBlobRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _BlobStore_GetBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetBlobRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BlobStoreServer).GetBlob(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: BlobStore_GetBlob_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BlobStoreServer).GetBlob(ctx, req.(*GetBlobRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// BlobStore_ServiceDesc is the grpc.ServiceDesc for BlobStore service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var BlobStore_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "resource.BlobStore",
|
||||
HandlerType: (*BlobStoreServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "PutBlob",
|
||||
Handler: _BlobStore_PutBlob_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetBlob",
|
||||
Handler: _BlobStore_GetBlob_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "blob.proto",
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module github.com/grafana/grafana/pkg/storage/unified/resourcepb
|
||||
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
google.golang.org/grpc v1.72.1
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
+337
-1565
File diff suppressed because it is too large
Load Diff
+1
-269
@@ -4,7 +4,7 @@
|
||||
// - protoc (unknown)
|
||||
// source: resource.proto
|
||||
|
||||
package resource
|
||||
package resourcepb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
@@ -476,141 +476,6 @@ var BulkStore_ServiceDesc = grpc.ServiceDesc{
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
ResourceIndex_Search_FullMethodName = "/resource.ResourceIndex/Search"
|
||||
ResourceIndex_GetStats_FullMethodName = "/resource.ResourceIndex/GetStats"
|
||||
)
|
||||
|
||||
// ResourceIndexClient is the client API for ResourceIndex service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
type ResourceIndexClient interface {
|
||||
Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error)
|
||||
// Get the resource stats
|
||||
GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error)
|
||||
}
|
||||
|
||||
type resourceIndexClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewResourceIndexClient(cc grpc.ClientConnInterface) ResourceIndexClient {
|
||||
return &resourceIndexClient{cc}
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResourceSearchResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_Search_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResourceStatsResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_GetStats_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ResourceIndexServer is the server API for ResourceIndex service.
|
||||
// All implementations should embed UnimplementedResourceIndexServer
|
||||
// for forward compatibility
|
||||
//
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
type ResourceIndexServer interface {
|
||||
Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error)
|
||||
// Get the resource stats
|
||||
GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedResourceIndexServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedResourceIndexServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedResourceIndexServer) Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Search not implemented")
|
||||
}
|
||||
func (UnimplementedResourceIndexServer) GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented")
|
||||
}
|
||||
|
||||
// UnsafeResourceIndexServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ResourceIndexServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeResourceIndexServer interface {
|
||||
mustEmbedUnimplementedResourceIndexServer()
|
||||
}
|
||||
|
||||
func RegisterResourceIndexServer(s grpc.ServiceRegistrar, srv ResourceIndexServer) {
|
||||
s.RegisterService(&ResourceIndex_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ResourceIndex_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResourceSearchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).Search(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_Search_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).Search(ctx, req.(*ResourceSearchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ResourceIndex_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResourceStatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).GetStats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_GetStats_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).GetStats(ctx, req.(*ResourceStatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ResourceIndex_ServiceDesc is the grpc.ServiceDesc for ResourceIndex service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ResourceIndex_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "resource.ResourceIndex",
|
||||
HandlerType: (*ResourceIndexServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Search",
|
||||
Handler: _ResourceIndex_Search_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStats",
|
||||
Handler: _ResourceIndex_GetStats_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
ManagedObjectIndex_CountManagedObjects_FullMethodName = "/resource.ManagedObjectIndex/CountManagedObjects"
|
||||
ManagedObjectIndex_ListManagedObjects_FullMethodName = "/resource.ManagedObjectIndex/ListManagedObjects"
|
||||
@@ -748,139 +613,6 @@ var ManagedObjectIndex_ServiceDesc = grpc.ServiceDesc{
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
BlobStore_PutBlob_FullMethodName = "/resource.BlobStore/PutBlob"
|
||||
BlobStore_GetBlob_FullMethodName = "/resource.BlobStore/GetBlob"
|
||||
)
|
||||
|
||||
// BlobStoreClient is the client API for BlobStore service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type BlobStoreClient interface {
|
||||
// Upload a blob that will be saved in a resource
|
||||
PutBlob(ctx context.Context, in *PutBlobRequest, opts ...grpc.CallOption) (*PutBlobResponse, error)
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error)
|
||||
}
|
||||
|
||||
type blobStoreClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewBlobStoreClient(cc grpc.ClientConnInterface) BlobStoreClient {
|
||||
return &blobStoreClient{cc}
|
||||
}
|
||||
|
||||
func (c *blobStoreClient) PutBlob(ctx context.Context, in *PutBlobRequest, opts ...grpc.CallOption) (*PutBlobResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(PutBlobResponse)
|
||||
err := c.cc.Invoke(ctx, BlobStore_PutBlob_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *blobStoreClient) GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetBlobResponse)
|
||||
err := c.cc.Invoke(ctx, BlobStore_GetBlob_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// BlobStoreServer is the server API for BlobStore service.
|
||||
// All implementations should embed UnimplementedBlobStoreServer
|
||||
// for forward compatibility
|
||||
type BlobStoreServer interface {
|
||||
// Upload a blob that will be saved in a resource
|
||||
PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error)
|
||||
// Get blob contents. When possible, this will return a signed URL
|
||||
// For large payloads, signed URLs are required to avoid protobuf message size limits
|
||||
GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedBlobStoreServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedBlobStoreServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedBlobStoreServer) PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PutBlob not implemented")
|
||||
}
|
||||
func (UnimplementedBlobStoreServer) GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBlob not implemented")
|
||||
}
|
||||
|
||||
// UnsafeBlobStoreServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to BlobStoreServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeBlobStoreServer interface {
|
||||
mustEmbedUnimplementedBlobStoreServer()
|
||||
}
|
||||
|
||||
func RegisterBlobStoreServer(s grpc.ServiceRegistrar, srv BlobStoreServer) {
|
||||
s.RegisterService(&BlobStore_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _BlobStore_PutBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PutBlobRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BlobStoreServer).PutBlob(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: BlobStore_PutBlob_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BlobStoreServer).PutBlob(ctx, req.(*PutBlobRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _BlobStore_GetBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetBlobRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BlobStoreServer).GetBlob(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: BlobStore_GetBlob_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BlobStoreServer).GetBlob(ctx, req.(*GetBlobRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// BlobStore_ServiceDesc is the grpc.ServiceDesc for BlobStore service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var BlobStore_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "resource.BlobStore",
|
||||
HandlerType: (*BlobStoreServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "PutBlob",
|
||||
Handler: _BlobStore_PutBlob_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetBlob",
|
||||
Handler: _BlobStore_GetBlob_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
Diagnostics_IsHealthy_FullMethodName = "/resource.Diagnostics/IsHealthy"
|
||||
)
|
||||
@@ -0,0 +1,879 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.5
|
||||
// protoc (unknown)
|
||||
// source: search.proto
|
||||
|
||||
package resourcepb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// Get statistics across multiple resources
|
||||
// For these queries, we do not need authorization to see the actual values
|
||||
type ResourceStatsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Namespace (tenant)
|
||||
Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"`
|
||||
// An optional list of group/resource identifiers
|
||||
// when empty, we assume searching across everything
|
||||
// NOTE, this query may need to federate across a few storage instances
|
||||
Kinds []string `protobuf:"bytes,2,rep,name=kinds,proto3" json:"kinds,omitempty"`
|
||||
// Limit the stats within a folder (not recursive!)
|
||||
Folder string `protobuf:"bytes,3,opt,name=folder,proto3" json:"folder,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceStatsRequest) Reset() {
|
||||
*x = ResourceStatsRequest{}
|
||||
mi := &file_search_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceStatsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceStatsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceStatsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceStatsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceStatsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ResourceStatsRequest) GetNamespace() string {
|
||||
if x != nil {
|
||||
return x.Namespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceStatsRequest) GetKinds() []string {
|
||||
if x != nil {
|
||||
return x.Kinds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceStatsRequest) GetFolder() string {
|
||||
if x != nil {
|
||||
return x.Folder
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ResourceStatsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Error details
|
||||
Error *ErrorResult `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
|
||||
// All results exist within this key
|
||||
Stats []*ResourceStatsResponse_Stats `protobuf:"bytes,2,rep,name=stats,proto3" json:"stats,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse) Reset() {
|
||||
*x = ResourceStatsResponse{}
|
||||
mi := &file_search_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceStatsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceStatsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceStatsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceStatsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse) GetError() *ErrorResult {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse) GetStats() []*ResourceStatsResponse_Stats {
|
||||
if x != nil {
|
||||
return x.Stats
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search within a single resource
|
||||
type ResourceSearchRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The key must include namespace + group + resource
|
||||
Options *ListOptions `protobuf:"bytes,1,opt,name=options,proto3" json:"options,omitempty"`
|
||||
// To search additional resource types, add additional keys to this list
|
||||
// NOTE: queries will only support federation across kinds with common fields
|
||||
Federated []*ResourceKey `protobuf:"bytes,2,rep,name=federated,proto3" json:"federated,omitempty"`
|
||||
// When a query exists, it is parsed and used to influence
|
||||
// query string for chosen implementation (currently just bleve)
|
||||
// The score is only relevant when a query exists
|
||||
Query string `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"`
|
||||
// max results
|
||||
Limit int64 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
// where to start the query (eg, From)
|
||||
Offset int64 `protobuf:"varint,5,opt,name=offset,proto3" json:"offset,omitempty"`
|
||||
// sorting
|
||||
SortBy []*ResourceSearchRequest_Sort `protobuf:"bytes,6,rep,name=sortBy,proto3" json:"sortBy,omitempty"`
|
||||
// calculate field statistics
|
||||
Facet map[string]*ResourceSearchRequest_Facet `protobuf:"bytes,7,rep,name=facet,proto3" json:"facet,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
// the return fields (empty will return everything)
|
||||
Fields []string `protobuf:"bytes,8,rep,name=fields,proto3" json:"fields,omitempty"`
|
||||
// explain each result (added to the each row)
|
||||
Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"`
|
||||
IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"`
|
||||
Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"`
|
||||
Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) Reset() {
|
||||
*x = ResourceSearchRequest{}
|
||||
mi := &file_search_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceSearchRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceSearchRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceSearchRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceSearchRequest) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetOptions() *ListOptions {
|
||||
if x != nil {
|
||||
return x.Options
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetFederated() []*ResourceKey {
|
||||
if x != nil {
|
||||
return x.Federated
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetQuery() string {
|
||||
if x != nil {
|
||||
return x.Query
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetLimit() int64 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetOffset() int64 {
|
||||
if x != nil {
|
||||
return x.Offset
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetSortBy() []*ResourceSearchRequest_Sort {
|
||||
if x != nil {
|
||||
return x.SortBy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetFacet() map[string]*ResourceSearchRequest_Facet {
|
||||
if x != nil {
|
||||
return x.Facet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetFields() []string {
|
||||
if x != nil {
|
||||
return x.Fields
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetExplain() bool {
|
||||
if x != nil {
|
||||
return x.Explain
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetIsDeleted() bool {
|
||||
if x != nil {
|
||||
return x.IsDeleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetPage() int64 {
|
||||
if x != nil {
|
||||
return x.Page
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetPermission() int64 {
|
||||
if x != nil {
|
||||
return x.Permission
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ResourceSearchResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Error details
|
||||
Error *ErrorResult `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
|
||||
// All results exist within this key
|
||||
Key *ResourceKey `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Query results
|
||||
Results *ResourceTable `protobuf:"bytes,3,opt,name=results,proto3" json:"results,omitempty"`
|
||||
// The total hit count
|
||||
TotalHits int64 `protobuf:"varint,4,opt,name=total_hits,json=totalHits,proto3" json:"total_hits,omitempty"`
|
||||
// indicates how expensive was the query with respect to bytes read
|
||||
QueryCost float64 `protobuf:"fixed64,5,opt,name=query_cost,json=queryCost,proto3" json:"query_cost,omitempty"`
|
||||
// maximum score across all fields
|
||||
MaxScore float64 `protobuf:"fixed64,6,opt,name=max_score,json=maxScore,proto3" json:"max_score,omitempty"`
|
||||
// Facet results
|
||||
Facet map[string]*ResourceSearchResponse_Facet `protobuf:"bytes,7,rep,name=facet,proto3" json:"facet,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) Reset() {
|
||||
*x = ResourceSearchResponse{}
|
||||
mi := &file_search_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceSearchResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceSearchResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceSearchResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceSearchResponse) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetError() *ErrorResult {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetKey() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetResults() *ResourceTable {
|
||||
if x != nil {
|
||||
return x.Results
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetTotalHits() int64 {
|
||||
if x != nil {
|
||||
return x.TotalHits
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetQueryCost() float64 {
|
||||
if x != nil {
|
||||
return x.QueryCost
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetMaxScore() float64 {
|
||||
if x != nil {
|
||||
return x.MaxScore
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetFacet() map[string]*ResourceSearchResponse_Facet {
|
||||
if x != nil {
|
||||
return x.Facet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceStatsResponse_Stats struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Resource group
|
||||
Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"`
|
||||
// Resource name
|
||||
Resource string `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
|
||||
// Number of items
|
||||
Count int64 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse_Stats) Reset() {
|
||||
*x = ResourceStatsResponse_Stats{}
|
||||
mi := &file_search_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse_Stats) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceStatsResponse_Stats) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceStatsResponse_Stats) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceStatsResponse_Stats.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceStatsResponse_Stats) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{1, 0}
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse_Stats) GetGroup() string {
|
||||
if x != nil {
|
||||
return x.Group
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse_Stats) GetResource() string {
|
||||
if x != nil {
|
||||
return x.Resource
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceStatsResponse_Stats) GetCount() int64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ResourceSearchRequest_Sort struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
|
||||
Desc bool `protobuf:"varint,2,opt,name=desc,proto3" json:"desc,omitempty"` // defaults to ascending
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Sort) Reset() {
|
||||
*x = ResourceSearchRequest_Sort{}
|
||||
mi := &file_search_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Sort) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceSearchRequest_Sort) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceSearchRequest_Sort) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceSearchRequest_Sort.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceSearchRequest_Sort) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{2, 0}
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Sort) GetField() string {
|
||||
if x != nil {
|
||||
return x.Field
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Sort) GetDesc() bool {
|
||||
if x != nil {
|
||||
return x.Desc
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ResourceSearchRequest_Facet struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
|
||||
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Facet) Reset() {
|
||||
*x = ResourceSearchRequest_Facet{}
|
||||
mi := &file_search_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Facet) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceSearchRequest_Facet) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceSearchRequest_Facet) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceSearchRequest_Facet.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceSearchRequest_Facet) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{2, 1}
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Facet) GetField() string {
|
||||
if x != nil {
|
||||
return x.Field
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest_Facet) GetLimit() int64 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ResourceSearchResponse_Facet struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
|
||||
// The distinct terms
|
||||
Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"`
|
||||
// The number of documents that do *not* have this field
|
||||
Missing int64 `protobuf:"varint,3,opt,name=missing,proto3" json:"missing,omitempty"`
|
||||
// Top term stats
|
||||
Terms []*ResourceSearchResponse_TermFacet `protobuf:"bytes,4,rep,name=terms,proto3" json:"terms,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) Reset() {
|
||||
*x = ResourceSearchResponse_Facet{}
|
||||
mi := &file_search_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceSearchResponse_Facet) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceSearchResponse_Facet.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceSearchResponse_Facet) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{3, 0}
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) GetField() string {
|
||||
if x != nil {
|
||||
return x.Field
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) GetTotal() int64 {
|
||||
if x != nil {
|
||||
return x.Total
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) GetMissing() int64 {
|
||||
if x != nil {
|
||||
return x.Missing
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_Facet) GetTerms() []*ResourceSearchResponse_TermFacet {
|
||||
if x != nil {
|
||||
return x.Terms
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceSearchResponse_TermFacet struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Term string `protobuf:"bytes,1,opt,name=term,proto3" json:"term,omitempty"`
|
||||
Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_TermFacet) Reset() {
|
||||
*x = ResourceSearchResponse_TermFacet{}
|
||||
mi := &file_search_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_TermFacet) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceSearchResponse_TermFacet) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_search_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceSearchResponse_TermFacet.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceSearchResponse_TermFacet) Descriptor() ([]byte, []int) {
|
||||
return file_search_proto_rawDescGZIP(), []int{3, 1}
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_TermFacet) GetTerm() string {
|
||||
if x != nil {
|
||||
return x.Term
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse_TermFacet) GetCount() int64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_search_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_search_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x62, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6b,
|
||||
0x69, 0x6e, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x22, 0xd2, 0x01, 0x0a,
|
||||
0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73,
|
||||
0x1a, 0x4f, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12,
|
||||
0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x22, 0x8e, 0x05, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f,
|
||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09,
|
||||
0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x65,
|
||||
0x64, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f,
|
||||
0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x18,
|
||||
0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x52, 0x06, 0x73, 0x6f, 0x72,
|
||||
0x74, 0x42, 0x79, 0x12, 0x40, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05,
|
||||
0x66, 0x61, 0x63, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18,
|
||||
0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
|
||||
0x65, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44,
|
||||
0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65,
|
||||
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
|
||||
0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f,
|
||||
0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05,
|
||||
0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c,
|
||||
0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69,
|
||||
0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
||||
0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a,
|
||||
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73,
|
||||
0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x07, 0x72,
|
||||
0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
|
||||
0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61,
|
||||
0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x63,
|
||||
0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79,
|
||||
0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x53, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66,
|
||||
0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66,
|
||||
0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69,
|
||||
0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x73,
|
||||
0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x04, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52,
|
||||
0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61,
|
||||
0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x60, 0x0a,
|
||||
0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46,
|
||||
0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32,
|
||||
0xa9, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b,
|
||||
0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74,
|
||||
0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74,
|
||||
0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e,
|
||||
0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
file_search_proto_rawDescOnce sync.Once
|
||||
file_search_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_search_proto_rawDescGZIP() []byte {
|
||||
file_search_proto_rawDescOnce.Do(func() {
|
||||
file_search_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_search_proto_rawDesc), len(file_search_proto_rawDesc)))
|
||||
})
|
||||
return file_search_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_search_proto_goTypes = []any{
|
||||
(*ResourceStatsRequest)(nil), // 0: resource.ResourceStatsRequest
|
||||
(*ResourceStatsResponse)(nil), // 1: resource.ResourceStatsResponse
|
||||
(*ResourceSearchRequest)(nil), // 2: resource.ResourceSearchRequest
|
||||
(*ResourceSearchResponse)(nil), // 3: resource.ResourceSearchResponse
|
||||
(*ResourceStatsResponse_Stats)(nil), // 4: resource.ResourceStatsResponse.Stats
|
||||
(*ResourceSearchRequest_Sort)(nil), // 5: resource.ResourceSearchRequest.Sort
|
||||
(*ResourceSearchRequest_Facet)(nil), // 6: resource.ResourceSearchRequest.Facet
|
||||
nil, // 7: resource.ResourceSearchRequest.FacetEntry
|
||||
(*ResourceSearchResponse_Facet)(nil), // 8: resource.ResourceSearchResponse.Facet
|
||||
(*ResourceSearchResponse_TermFacet)(nil), // 9: resource.ResourceSearchResponse.TermFacet
|
||||
nil, // 10: resource.ResourceSearchResponse.FacetEntry
|
||||
(*ErrorResult)(nil), // 11: resource.ErrorResult
|
||||
(*ListOptions)(nil), // 12: resource.ListOptions
|
||||
(*ResourceKey)(nil), // 13: resource.ResourceKey
|
||||
(*ResourceTable)(nil), // 14: resource.ResourceTable
|
||||
}
|
||||
var file_search_proto_depIdxs = []int32{
|
||||
11, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult
|
||||
4, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats
|
||||
12, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions
|
||||
13, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey
|
||||
5, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort
|
||||
7, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry
|
||||
11, // 6: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult
|
||||
13, // 7: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey
|
||||
14, // 8: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable
|
||||
10, // 9: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry
|
||||
6, // 10: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet
|
||||
9, // 11: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet
|
||||
8, // 12: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet
|
||||
2, // 13: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest
|
||||
0, // 14: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest
|
||||
3, // 15: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse
|
||||
1, // 16: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse
|
||||
15, // [15:17] is the sub-list for method output_type
|
||||
13, // [13:15] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_search_proto_init() }
|
||||
func file_search_proto_init() {
|
||||
if File_search_proto != nil {
|
||||
return
|
||||
}
|
||||
file_resource_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_search_proto_rawDesc), len(file_search_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 11,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_search_proto_goTypes,
|
||||
DependencyIndexes: file_search_proto_depIdxs,
|
||||
MessageInfos: file_search_proto_msgTypes,
|
||||
}.Build()
|
||||
File_search_proto = out.File
|
||||
file_search_proto_goTypes = nil
|
||||
file_search_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.4.0
|
||||
// - protoc (unknown)
|
||||
// source: search.proto
|
||||
|
||||
package resourcepb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.62.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion8
|
||||
|
||||
const (
|
||||
ResourceIndex_Search_FullMethodName = "/resource.ResourceIndex/Search"
|
||||
ResourceIndex_GetStats_FullMethodName = "/resource.ResourceIndex/GetStats"
|
||||
)
|
||||
|
||||
// ResourceIndexClient is the client API for ResourceIndex service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
type ResourceIndexClient interface {
|
||||
Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error)
|
||||
// Get the resource stats
|
||||
GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error)
|
||||
}
|
||||
|
||||
type resourceIndexClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewResourceIndexClient(cc grpc.ClientConnInterface) ResourceIndexClient {
|
||||
return &resourceIndexClient{cc}
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResourceSearchResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_Search_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResourceStatsResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_GetStats_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ResourceIndexServer is the server API for ResourceIndex service.
|
||||
// All implementations should embed UnimplementedResourceIndexServer
|
||||
// for forward compatibility
|
||||
//
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
type ResourceIndexServer interface {
|
||||
Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error)
|
||||
// Get the resource stats
|
||||
GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedResourceIndexServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedResourceIndexServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedResourceIndexServer) Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Search not implemented")
|
||||
}
|
||||
func (UnimplementedResourceIndexServer) GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented")
|
||||
}
|
||||
|
||||
// UnsafeResourceIndexServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ResourceIndexServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeResourceIndexServer interface {
|
||||
mustEmbedUnimplementedResourceIndexServer()
|
||||
}
|
||||
|
||||
func RegisterResourceIndexServer(s grpc.ServiceRegistrar, srv ResourceIndexServer) {
|
||||
s.RegisterService(&ResourceIndex_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ResourceIndex_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResourceSearchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).Search(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_Search_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).Search(ctx, req.(*ResourceSearchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ResourceIndex_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResourceStatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).GetStats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_GetStats_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).GetStats(ctx, req.(*ResourceStatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ResourceIndex_ServiceDesc is the grpc.ServiceDesc for ResourceIndex service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ResourceIndex_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "resource.ResourceIndex",
|
||||
HandlerType: (*ResourceIndexServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Search",
|
||||
Handler: _ResourceIndex_Search_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStats",
|
||||
Handler: _ResourceIndex_GetStats_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "search.proto",
|
||||
}
|
||||
@@ -27,8 +27,10 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
@@ -309,7 +311,7 @@ type bleveIndex struct {
|
||||
fields resource.SearchableDocumentFields
|
||||
|
||||
// The values returned with all
|
||||
allFields []*resource.ResourceTableColumnDefinition
|
||||
allFields []*resourcepb.ResourceTableColumnDefinition
|
||||
features featuremgmt.FeatureToggles
|
||||
tracing trace.Tracer
|
||||
}
|
||||
@@ -330,29 +332,29 @@ func (b *bleveIndex) BulkIndex(req *resource.BulkIndexRequest) error {
|
||||
doc := item.Doc.UpdateCopyFields()
|
||||
doc.References = nil // remove references (for now!)
|
||||
|
||||
err := batch.Index(doc.Key.SearchID(), doc)
|
||||
err := batch.Index(resource.SearchID(doc.Key), doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case resource.ActionDelete:
|
||||
batch.Delete(item.Key.SearchID())
|
||||
batch.Delete(resource.SearchID(item.Key))
|
||||
}
|
||||
}
|
||||
|
||||
return b.index.Batch(batch)
|
||||
}
|
||||
|
||||
func (b *bleveIndex) ListManagedObjects(ctx context.Context, req *resource.ListManagedObjectsRequest) (*resource.ListManagedObjectsResponse, error) {
|
||||
func (b *bleveIndex) ListManagedObjects(ctx context.Context, req *resourcepb.ListManagedObjectsRequest) (*resourcepb.ListManagedObjectsResponse, error) {
|
||||
if req.NextPageToken != "" {
|
||||
return nil, fmt.Errorf("next page not implemented yet")
|
||||
}
|
||||
if req.Kind == "" {
|
||||
return &resource.ListManagedObjectsResponse{
|
||||
return &resourcepb.ListManagedObjectsResponse{
|
||||
Error: resource.NewBadRequestError("empty manager kind"),
|
||||
}, nil
|
||||
}
|
||||
if req.Id == "" {
|
||||
return &resource.ListManagedObjectsResponse{
|
||||
return &resourcepb.ListManagedObjectsResponse{
|
||||
Error: resource.NewBadRequestError("empty manager id"),
|
||||
}, nil
|
||||
}
|
||||
@@ -423,17 +425,17 @@ func (b *bleveIndex) ListManagedObjects(ctx context.Context, req *resource.ListM
|
||||
return 0
|
||||
}
|
||||
|
||||
rsp := &resource.ListManagedObjectsResponse{}
|
||||
rsp := &resourcepb.ListManagedObjectsResponse{}
|
||||
for _, hit := range found.Hits {
|
||||
item := &resource.ListManagedObjectsResponse_Item{
|
||||
Object: &resource.ResourceKey{},
|
||||
item := &resourcepb.ListManagedObjectsResponse_Item{
|
||||
Object: &resourcepb.ResourceKey{},
|
||||
Hash: asString(hit.Fields[resource.SEARCH_FIELD_SOURCE_CHECKSUM]),
|
||||
Path: asString(hit.Fields[resource.SEARCH_FIELD_SOURCE_PATH]),
|
||||
Time: asTime(hit.Fields[resource.SEARCH_FIELD_SOURCE_TIME]),
|
||||
Title: asString(hit.Fields[resource.SEARCH_FIELD_TITLE]),
|
||||
Folder: asString(hit.Fields[resource.SEARCH_FIELD_FOLDER]),
|
||||
}
|
||||
err := item.Object.ReadSearchID(hit.ID)
|
||||
err := resource.ReadSearchID(item.Object, hit.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -442,7 +444,7 @@ func (b *bleveIndex) ListManagedObjects(ctx context.Context, req *resource.ListM
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (b *bleveIndex) CountManagedObjects(ctx context.Context) ([]*resource.CountManagedObjectsResponse_ResourceCount, error) {
|
||||
func (b *bleveIndex) CountManagedObjects(ctx context.Context) ([]*resourcepb.CountManagedObjectsResponse_ResourceCount, error) {
|
||||
found, err := b.index.SearchInContext(ctx, &bleve.SearchRequest{
|
||||
Query: bleve.NewMatchAllQuery(),
|
||||
Size: 0,
|
||||
@@ -453,14 +455,14 @@ func (b *bleveIndex) CountManagedObjects(ctx context.Context) ([]*resource.Count
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals := make([]*resource.CountManagedObjectsResponse_ResourceCount, 0)
|
||||
vals := make([]*resourcepb.CountManagedObjectsResponse_ResourceCount, 0)
|
||||
f, ok := found.Facets["count"]
|
||||
if ok && f.Terms != nil {
|
||||
for _, v := range f.Terms.Terms() {
|
||||
val := v.Term
|
||||
idx := strings.Index(val, ":")
|
||||
if idx > 0 {
|
||||
vals = append(vals, &resource.CountManagedObjectsResponse_ResourceCount{
|
||||
vals = append(vals, &resourcepb.CountManagedObjectsResponse_ResourceCount{
|
||||
Kind: val[0:idx],
|
||||
Id: val[idx+1:],
|
||||
Group: b.key.Group,
|
||||
@@ -477,19 +479,19 @@ func (b *bleveIndex) CountManagedObjects(ctx context.Context) ([]*resource.Count
|
||||
func (b *bleveIndex) Search(
|
||||
ctx context.Context,
|
||||
access authlib.AccessClient,
|
||||
req *resource.ResourceSearchRequest,
|
||||
req *resourcepb.ResourceSearchRequest,
|
||||
federate []resource.ResourceIndex, // For federated queries, these will match the values in req.federate
|
||||
) (*resource.ResourceSearchResponse, error) {
|
||||
) (*resourcepb.ResourceSearchResponse, error) {
|
||||
ctx, span := b.tracing.Start(ctx, tracingPrexfixBleve+"Search")
|
||||
defer span.End()
|
||||
|
||||
if req.Options == nil || req.Options.Key == nil {
|
||||
return &resource.ResourceSearchResponse{
|
||||
return &resourcepb.ResourceSearchResponse{
|
||||
Error: resource.NewBadRequestError("missing query key"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
response := &resource.ResourceSearchResponse{
|
||||
response := &resourcepb.ResourceSearchResponse{
|
||||
Error: b.verifyKey(req.Options.Key),
|
||||
}
|
||||
if response.Error != nil {
|
||||
@@ -545,7 +547,7 @@ func (b *bleveIndex) Search(
|
||||
for k, v := range res.Facets {
|
||||
f := newResponseFacet(v)
|
||||
if response.Facet == nil {
|
||||
response.Facet = make(map[string]*resource.ResourceSearchResponse_Facet)
|
||||
response.Facet = make(map[string]*resourcepb.ResourceSearchResponse_Facet)
|
||||
}
|
||||
response.Facet[k] = f
|
||||
}
|
||||
@@ -577,7 +579,7 @@ func (b *bleveIndex) DocCount(ctx context.Context, folder string) (int64, error)
|
||||
}
|
||||
|
||||
// make sure the request key matches the index
|
||||
func (b *bleveIndex) verifyKey(key *resource.ResourceKey) *resource.ErrorResult {
|
||||
func (b *bleveIndex) verifyKey(key *resourcepb.ResourceKey) *resourcepb.ErrorResult {
|
||||
if key.Namespace != b.key.Namespace {
|
||||
return resource.NewBadRequestError("namespace mismatch (expected " + b.key.Namespace + ")")
|
||||
}
|
||||
@@ -592,7 +594,7 @@ func (b *bleveIndex) verifyKey(key *resource.ResourceKey) *resource.ErrorResult
|
||||
|
||||
func (b *bleveIndex) getIndex(
|
||||
ctx context.Context,
|
||||
req *resource.ResourceSearchRequest,
|
||||
req *resourcepb.ResourceSearchRequest,
|
||||
federate []resource.ResourceIndex,
|
||||
) (bleve.Index, error) {
|
||||
_, span := b.tracing.Start(ctx, tracingPrexfixBleve+"getIndex")
|
||||
@@ -621,7 +623,7 @@ func (b *bleveIndex) getIndex(
|
||||
return b.index, nil
|
||||
}
|
||||
|
||||
func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resource.ResourceSearchRequest, access authlib.AccessClient) (*bleve.SearchRequest, *resource.ErrorResult) {
|
||||
func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.ResourceSearchRequest, access authlib.AccessClient) (*bleve.SearchRequest, *resourcepb.ErrorResult) {
|
||||
ctx, span := b.tracing.Start(ctx, tracingPrexfixBleve+"toBleveSearchRequest")
|
||||
defer span.End()
|
||||
|
||||
@@ -793,7 +795,7 @@ func safeInt64ToInt(i64 int64) (int, error) {
|
||||
return int(i64), nil
|
||||
}
|
||||
|
||||
func getSortFields(req *resource.ResourceSearchRequest) []string {
|
||||
func getSortFields(req *resourcepb.ResourceSearchRequest) []string {
|
||||
sorting := []string{}
|
||||
for _, sort := range req.SortBy {
|
||||
input := sort.Field
|
||||
@@ -826,7 +828,7 @@ var termFields = []string{
|
||||
}
|
||||
|
||||
// Convert a "requirement" into a bleve query
|
||||
func requirementQuery(req *resource.Requirement, prefix string) (query.Query, *resource.ErrorResult) {
|
||||
func requirementQuery(req *resourcepb.Requirement, prefix string) (query.Query, *resourcepb.ErrorResult) {
|
||||
switch selection.Operator(req.Operator) {
|
||||
case selection.Equals, selection.DoubleEquals:
|
||||
if len(req.Values) == 0 {
|
||||
@@ -946,11 +948,11 @@ func filterValue(field string, v string) string {
|
||||
return v
|
||||
}
|
||||
|
||||
func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hits search.DocumentMatchCollection, explain bool) (*resource.ResourceTable, error) {
|
||||
func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hits search.DocumentMatchCollection, explain bool) (*resourcepb.ResourceTable, error) {
|
||||
_, span := b.tracing.Start(ctx, tracingPrexfixBleve+"hitsToTable")
|
||||
defer span.End()
|
||||
|
||||
fields := []*resource.ResourceTableColumnDefinition{}
|
||||
fields := []*resourcepb.ResourceTableColumnDefinition{}
|
||||
for _, name := range selectFields {
|
||||
if name == "_all" {
|
||||
fields = b.allFields
|
||||
@@ -964,9 +966,9 @@ func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hit
|
||||
if f == nil {
|
||||
// Labels as a string
|
||||
if strings.HasPrefix(name, "labels.") {
|
||||
f = &resource.ResourceTableColumnDefinition{
|
||||
f = &resourcepb.ResourceTableColumnDefinition{
|
||||
Name: name,
|
||||
Type: resource.ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,18 +989,18 @@ func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hit
|
||||
}
|
||||
encoders := builder.Encoders()
|
||||
|
||||
table := &resource.ResourceTable{
|
||||
table := &resourcepb.ResourceTable{
|
||||
Columns: fields,
|
||||
Rows: make([]*resource.ResourceTableRow, hits.Len()),
|
||||
Rows: make([]*resourcepb.ResourceTableRow, hits.Len()),
|
||||
}
|
||||
for rowID, match := range hits {
|
||||
row := &resource.ResourceTableRow{
|
||||
Key: &resource.ResourceKey{},
|
||||
row := &resourcepb.ResourceTableRow{
|
||||
Key: &resourcepb.ResourceKey{},
|
||||
Cells: make([][]byte, len(fields)),
|
||||
}
|
||||
table.Rows[rowID] = row
|
||||
|
||||
err := row.Key.ReadSearchID(match.ID)
|
||||
err := resource.ReadSearchID(row.Key, match.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1048,8 +1050,8 @@ func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hit
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func getAllFields(standard resource.SearchableDocumentFields, custom resource.SearchableDocumentFields) ([]*resource.ResourceTableColumnDefinition, error) {
|
||||
fields := []*resource.ResourceTableColumnDefinition{
|
||||
func getAllFields(standard resource.SearchableDocumentFields, custom resource.SearchableDocumentFields) ([]*resourcepb.ResourceTableColumnDefinition, error) {
|
||||
fields := []*resourcepb.ResourceTableColumnDefinition{
|
||||
standard.Field(resource.SEARCH_FIELD_ID),
|
||||
standard.Field(resource.SEARCH_FIELD_TITLE),
|
||||
standard.Field(resource.SEARCH_FIELD_TAGS),
|
||||
@@ -1077,15 +1079,15 @@ func getAllFields(standard resource.SearchableDocumentFields, custom resource.Se
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func newResponseFacet(v *search.FacetResult) *resource.ResourceSearchResponse_Facet {
|
||||
f := &resource.ResourceSearchResponse_Facet{
|
||||
func newResponseFacet(v *search.FacetResult) *resourcepb.ResourceSearchResponse_Facet {
|
||||
f := &resourcepb.ResourceSearchResponse_Facet{
|
||||
Field: v.Field,
|
||||
Total: int64(v.Total),
|
||||
Missing: int64(v.Missing),
|
||||
}
|
||||
if v.Terms != nil {
|
||||
for _, t := range v.Terms.Terms() {
|
||||
f.Terms = append(f.Terms, &resource.ResourceSearchResponse_TermFacet{
|
||||
f.Terms = append(f.Terms, &resourcepb.ResourceSearchResponse_TermFacet{
|
||||
Term: t.Term,
|
||||
Count: int64(t.Count),
|
||||
})
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -85,7 +87,7 @@ func BenchmarkBleveQuery(b *testing.B) {
|
||||
}
|
||||
|
||||
func newTestWriter(size int, batchSize int) IndexWriter {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
@@ -105,7 +107,7 @@ func newTestWriter(size int, batchSize int) IndexWriter {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: int64(i),
|
||||
Name: name,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: name,
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
|
||||
@@ -16,13 +16,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/dashboard"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/search"
|
||||
)
|
||||
|
||||
const threshold = 9999
|
||||
|
||||
func TestCanSearchByTitle(t *testing.T) {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
@@ -37,7 +38,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name1",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -51,7 +52,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name2",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name2",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -81,7 +82,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name1",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -95,7 +96,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name2",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name2",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -125,7 +126,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name1",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -139,7 +140,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name2",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name2",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -168,7 +169,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name1",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -182,7 +183,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name2",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name2",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -212,7 +213,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "aaa",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -253,7 +254,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "aaa",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -267,7 +268,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 2,
|
||||
Name: "name2",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name2",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -300,7 +301,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "aaa",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -365,7 +366,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name1",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -379,7 +380,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name2",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name2",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -393,7 +394,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name3",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "name3",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -434,7 +435,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "name1",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "aaa",
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -455,7 +456,7 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: int64(i),
|
||||
Name: fmt.Sprintf("name%d", i),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: fmt.Sprintf("name%d", i),
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
@@ -491,10 +492,10 @@ func TestCanSearchByTitle(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func newTestQuery(query string) *resource.ResourceSearchRequest {
|
||||
return &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
func newTestQuery(query string) *resourcepb.ResourceSearchRequest {
|
||||
return &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
@@ -505,22 +506,22 @@ func newTestQuery(query string) *resource.ResourceSearchRequest {
|
||||
}
|
||||
}
|
||||
|
||||
func newQueryByTitle(query string) *resource.ResourceSearchRequest {
|
||||
return &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
func newQueryByTitle(query string) *resourcepb.ResourceSearchRequest {
|
||||
return &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
},
|
||||
Fields: []*resource.Requirement{{Key: "title", Operator: "=", Values: []string{query}}},
|
||||
Fields: []*resourcepb.Requirement{{Key: "title", Operator: "=", Values: []string{query}}},
|
||||
},
|
||||
Limit: 100000,
|
||||
}
|
||||
}
|
||||
|
||||
func newTestDashboardsIndex(t TB, threshold int64, size int64, batchSize int64, writer IndexWriter) (resource.ResourceIndex, string) {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@@ -21,15 +22,16 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/dashboard"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func TestBleveBackend(t *testing.T) {
|
||||
dashboardskey := &resource.ResourceKey{
|
||||
dashboardskey := &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
}
|
||||
folderKey := &resource.ResourceKey{
|
||||
folderKey := &resourcepb.ResourceKey{
|
||||
Namespace: dashboardskey.Namespace,
|
||||
Group: "folder.grafana.app",
|
||||
Resource: "folders",
|
||||
@@ -72,7 +74,7 @@ func TestBleveBackend(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Name: "aaa",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "aaa",
|
||||
Namespace: "ns",
|
||||
Group: "dashboard.grafana.app",
|
||||
@@ -105,7 +107,7 @@ func TestBleveBackend(t *testing.T) {
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 2,
|
||||
Name: "bbb",
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "bbb",
|
||||
Namespace: "ns",
|
||||
Group: "dashboard.grafana.app",
|
||||
@@ -138,7 +140,7 @@ func TestBleveBackend(t *testing.T) {
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 3,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "ccc",
|
||||
Namespace: "ns",
|
||||
Group: "dashboard.grafana.app",
|
||||
@@ -172,15 +174,15 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.NotNil(t, index)
|
||||
dashboardsIndex = index
|
||||
|
||||
rsp, err := index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err := index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: key,
|
||||
},
|
||||
Limit: 100000,
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: resource.SEARCH_FIELD_TITLE, Desc: true}, // ccc,bbb,aaa
|
||||
},
|
||||
Facet: map[string]*resource.ResourceSearchRequest_Facet{
|
||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||
"tags": {
|
||||
Field: "tags",
|
||||
Limit: 100,
|
||||
@@ -221,10 +223,10 @@ func TestBleveBackend(t *testing.T) {
|
||||
count, _ = index.DocCount(ctx, "zzz")
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
rsp, err = index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err = index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: key,
|
||||
Labels: []*resource.Requirement{{
|
||||
Labels: []*resourcepb.Requirement{{
|
||||
Key: utils.LabelKeyDeprecatedInternalID, // nolint:staticcheck
|
||||
Operator: "in",
|
||||
Values: []string{"10", "11"},
|
||||
@@ -240,13 +242,13 @@ func TestBleveBackend(t *testing.T) {
|
||||
})
|
||||
|
||||
// can get sprinkles fields and sort by them
|
||||
rsp, err = index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err = index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: key,
|
||||
},
|
||||
Limit: 100000,
|
||||
Fields: []string{DASHBOARD_ERRORS_TODAY, DASHBOARD_VIEWS_LAST_1_DAYS, "fieldThatDoesntExist"},
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "fields." + DASHBOARD_VIEWS_LAST_1_DAYS, Desc: true},
|
||||
},
|
||||
}, nil)
|
||||
@@ -260,13 +262,13 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.Equal(t, int64(100), val)
|
||||
|
||||
// check auth will exclude results we don't have access to
|
||||
rsp, err = index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": false}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err = index.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": false}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: key,
|
||||
},
|
||||
Limit: 100000,
|
||||
Fields: []string{DASHBOARD_ERRORS_TODAY, DASHBOARD_VIEWS_LAST_1_DAYS, "fieldThatDoesntExist"},
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "fields." + DASHBOARD_VIEWS_LAST_1_DAYS, Desc: true},
|
||||
},
|
||||
}, nil)
|
||||
@@ -274,7 +276,7 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.Equal(t, 0, len(rsp.Results.Rows))
|
||||
|
||||
// Now look for repositories
|
||||
found, err := index.ListManagedObjects(ctx, &resource.ListManagedObjectsRequest{
|
||||
found, err := index.ListManagedObjects(ctx, &resourcepb.ListManagedObjectsRequest{
|
||||
Kind: "repo",
|
||||
Id: "repo-1",
|
||||
})
|
||||
@@ -352,7 +354,7 @@ func TestBleveBackend(t *testing.T) {
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 1,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "zzz",
|
||||
Namespace: "ns",
|
||||
Group: "folder.grafana.app",
|
||||
@@ -377,7 +379,7 @@ func TestBleveBackend(t *testing.T) {
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
RV: 2,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "yyy",
|
||||
Namespace: "ns",
|
||||
Group: "folder.grafana.app",
|
||||
@@ -401,8 +403,8 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.NotNil(t, index)
|
||||
foldersIndex = index
|
||||
|
||||
rsp, err := index.Search(ctx, NewStubAccessClient(map[string]bool{"folders": true}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err := index.Search(ctx, NewStubAccessClient(map[string]bool{"folders": true}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: key,
|
||||
},
|
||||
Limit: 100000,
|
||||
@@ -421,21 +423,21 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.NotNil(t, foldersIndex)
|
||||
|
||||
// Use a federated query to get both results together, sorted by title
|
||||
rsp, err := dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true, "folders": true}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err := dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true, "folders": true}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: dashboardskey,
|
||||
},
|
||||
Fields: []string{
|
||||
"title", "_id",
|
||||
},
|
||||
Federated: []*resource.ResourceKey{
|
||||
Federated: []*resourcepb.ResourceKey{
|
||||
folderKey, // This will join in the
|
||||
},
|
||||
Limit: 100000,
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "title", Desc: false},
|
||||
},
|
||||
Facet: map[string]*resource.ResourceSearchRequest_Facet{
|
||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||
"region": {
|
||||
Field: "labels.region",
|
||||
Limit: 100,
|
||||
@@ -485,21 +487,21 @@ func TestBleveBackend(t *testing.T) {
|
||||
}`, string(disp))
|
||||
|
||||
// now only when we have permissions to see dashboards
|
||||
rsp, err = dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true, "folders": false}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err = dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": true, "folders": false}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: dashboardskey,
|
||||
},
|
||||
Fields: []string{
|
||||
"title", "_id",
|
||||
},
|
||||
Federated: []*resource.ResourceKey{
|
||||
Federated: []*resourcepb.ResourceKey{
|
||||
folderKey, // This will join in the
|
||||
},
|
||||
Limit: 100000,
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "title", Desc: false},
|
||||
},
|
||||
Facet: map[string]*resource.ResourceSearchRequest_Facet{
|
||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||
"region": {
|
||||
Field: "labels.region",
|
||||
Limit: 100,
|
||||
@@ -514,21 +516,21 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.Equal(t, "dashboards", rsp.Results.Rows[2].Key.Resource)
|
||||
|
||||
// now only when we have permissions to see folders
|
||||
rsp, err = dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": false, "folders": true}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err = dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": false, "folders": true}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: dashboardskey,
|
||||
},
|
||||
Fields: []string{
|
||||
"title", "_id",
|
||||
},
|
||||
Federated: []*resource.ResourceKey{
|
||||
Federated: []*resourcepb.ResourceKey{
|
||||
folderKey, // This will join in the
|
||||
},
|
||||
Limit: 100000,
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "title", Desc: false},
|
||||
},
|
||||
Facet: map[string]*resource.ResourceSearchRequest_Facet{
|
||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||
"region": {
|
||||
Field: "labels.region",
|
||||
Limit: 100,
|
||||
@@ -542,21 +544,21 @@ func TestBleveBackend(t *testing.T) {
|
||||
require.Equal(t, "folders", rsp.Results.Rows[1].Key.Resource)
|
||||
|
||||
// now when we have permissions to see nothing
|
||||
rsp, err = dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": false, "folders": false}), &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
rsp, err = dashboardsIndex.Search(ctx, NewStubAccessClient(map[string]bool{"dashboards": false, "folders": false}), &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: dashboardskey,
|
||||
},
|
||||
Fields: []string{
|
||||
"title", "_id",
|
||||
},
|
||||
Federated: []*resource.ResourceKey{
|
||||
Federated: []*resourcepb.ResourceKey{
|
||||
folderKey, // This will join in the
|
||||
},
|
||||
Limit: 100000,
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "title", Desc: false},
|
||||
},
|
||||
Facet: map[string]*resource.ResourceSearchRequest_Facet{
|
||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||
"region": {
|
||||
Field: "labels.region",
|
||||
Limit: 100,
|
||||
@@ -571,8 +573,8 @@ func TestBleveBackend(t *testing.T) {
|
||||
|
||||
func TestGetSortFields(t *testing.T) {
|
||||
t.Run("will prepend 'fields.' to sort fields when they are dashboard fields", func(t *testing.T) {
|
||||
searchReq := &resource.ResourceSearchRequest{
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
searchReq := &resourcepb.ResourceSearchRequest{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "views_total", Desc: false},
|
||||
},
|
||||
}
|
||||
@@ -580,8 +582,8 @@ func TestGetSortFields(t *testing.T) {
|
||||
assert.Equal(t, []string{"fields.views_total"}, sortFields)
|
||||
})
|
||||
t.Run("will prepend sort fields with a '-' when sort is Desc", func(t *testing.T) {
|
||||
searchReq := &resource.ResourceSearchRequest{
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
searchReq := &resourcepb.ResourceSearchRequest{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "views_total", Desc: true},
|
||||
},
|
||||
}
|
||||
@@ -589,8 +591,8 @@ func TestGetSortFields(t *testing.T) {
|
||||
assert.Equal(t, []string{"-fields.views_total"}, sortFields)
|
||||
})
|
||||
t.Run("will not prepend 'fields.' to common fields", func(t *testing.T) {
|
||||
searchReq := &resource.ResourceSearchRequest{
|
||||
SortBy: []*resource.ResourceSearchRequest_Sort{
|
||||
searchReq := &resourcepb.ResourceSearchRequest{
|
||||
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
||||
{Field: "description", Desc: false},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/dashboard"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------
|
||||
@@ -45,146 +46,146 @@ const DASHBOARD_ERRORS_TOTAL = "errors_total"
|
||||
const DASHBOARD_ERRORS_TODAY = "errors_today"
|
||||
|
||||
func DashboardBuilder(namespaced resource.NamespacedDocumentSupplier) (resource.DocumentBuilderInfo, error) {
|
||||
fields, err := resource.NewSearchableDocumentFields([]*resource.ResourceTableColumnDefinition{
|
||||
fields, err := resource.NewSearchableDocumentFields([]*resourcepb.ResourceTableColumnDefinition{
|
||||
{
|
||||
Name: DASHBOARD_SCHEMA_VERSION,
|
||||
Type: resource.ResourceTableColumnDefinition_INT32,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT32,
|
||||
Description: "Numeric version saying when the schema was saved",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
NotNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_LINK_COUNT,
|
||||
Type: resource.ResourceTableColumnDefinition_INT32,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT32,
|
||||
Description: "How many links appear on the page",
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_PANEL_TYPES,
|
||||
Type: resource.ResourceTableColumnDefinition_STRING,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
IsArray: true,
|
||||
Description: "How many links appear on the page",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_ERRORS_TODAY,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of errors that occurred today",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_ERRORS_LAST_1_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of errors that occurred in the last 1 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_ERRORS_LAST_7_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of errors that occurred in the last 7 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_ERRORS_LAST_30_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of errors that occurred in the last 30 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_ERRORS_TOTAL,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Total number of errors",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_QUERIES_TODAY,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of queries that occurred today",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_QUERIES_LAST_1_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of queries that occurred in the last 1 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_QUERIES_LAST_7_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of queries that occurred in the last 7 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_QUERIES_LAST_30_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of queries that occurred in the last 30 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_QUERIES_TOTAL,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Total number of queries",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_VIEWS_TODAY,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of views that occurred today",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_VIEWS_LAST_1_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of views that occurred in the last 1 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_VIEWS_LAST_7_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of views that occurred in the last 7 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_VIEWS_LAST_30_DAYS,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Number of views that occurred in the last 30 days",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: DASHBOARD_VIEWS_TOTAL,
|
||||
Type: resource.ResourceTableColumnDefinition_INT64,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
||||
Description: "Total number of views",
|
||||
Properties: &resource.ResourceTableColumnDefinition_Properties{
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
@@ -231,7 +232,7 @@ type DashboardStatsLookup = func(ctx context.Context, uid string) map[string]int
|
||||
|
||||
var _ resource.DocumentBuilder = &DashboardDocumentBuilder{}
|
||||
|
||||
func (s *DashboardDocumentBuilder) BuildDocument(ctx context.Context, key *resource.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
|
||||
func (s *DashboardDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
|
||||
if s.Namespace != "" && s.Namespace != key.Namespace {
|
||||
return nil, fmt.Errorf("invalid namespace")
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/dashboard"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/search"
|
||||
)
|
||||
|
||||
func doSnapshotTests(t *testing.T, builder resource.DocumentBuilder, kind string, key *resource.ResourceKey, names []string) {
|
||||
func doSnapshotTests(t *testing.T, builder resource.DocumentBuilder, kind string, key *resourcepb.ResourceKey, names []string) {
|
||||
t.Helper()
|
||||
|
||||
for _, name := range names {
|
||||
@@ -47,7 +48,7 @@ func doSnapshotTests(t *testing.T, builder resource.DocumentBuilder, kind string
|
||||
}
|
||||
|
||||
func TestDashboardDocumentBuilder(t *testing.T) {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
@@ -82,7 +83,7 @@ func TestDashboardDocumentBuilder(t *testing.T) {
|
||||
|
||||
// Standard
|
||||
builder = resource.StandardDocumentBuilder()
|
||||
doSnapshotTests(t, builder, "folder", &resource.ResourceKey{
|
||||
doSnapshotTests(t, builder, "folder", &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "folder.grafana.app",
|
||||
Resource: "folders",
|
||||
@@ -90,14 +91,14 @@ func TestDashboardDocumentBuilder(t *testing.T) {
|
||||
"aaa",
|
||||
"bbb",
|
||||
})
|
||||
doSnapshotTests(t, builder, "playlist", &resource.ResourceKey{
|
||||
doSnapshotTests(t, builder, "playlist", &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "playlist.grafana.app",
|
||||
Resource: "playlists",
|
||||
}, []string{
|
||||
"aaa",
|
||||
})
|
||||
doSnapshotTests(t, builder, "report", &resource.ResourceKey{
|
||||
doSnapshotTests(t, builder, "report", &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "reporting.grafana.app",
|
||||
Resource: "reports",
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/dbutil"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
@@ -36,7 +37,7 @@ const defaultPrunerHistoryLimit = 20
|
||||
|
||||
type Backend interface {
|
||||
resource.StorageBackend
|
||||
resource.DiagnosticsServer
|
||||
resourcepb.DiagnosticsServer
|
||||
resource.LifecycleHooks
|
||||
}
|
||||
|
||||
@@ -218,7 +219,7 @@ func (b *backend) initPruner(ctx context.Context) error {
|
||||
res, err := dbutil.Exec(ctx, tx, sqlResourceHistoryPrune, &sqlPruneHistoryRequest{
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
HistoryLimit: defaultPrunerHistoryLimit,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: key.namespace,
|
||||
Group: key.group,
|
||||
Resource: key.resource,
|
||||
@@ -260,14 +261,14 @@ func (b *backend) initPruner(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *backend) IsHealthy(ctx context.Context, r *resource.HealthCheckRequest) (*resource.HealthCheckResponse, error) {
|
||||
func (b *backend) IsHealthy(ctx context.Context, _ *resourcepb.HealthCheckRequest) (*resourcepb.HealthCheckResponse, error) {
|
||||
// ctxLogger := s.log.FromContext(log.WithContextualAttributes(ctx, []any{"method", "isHealthy"}))
|
||||
|
||||
if err := b.db.PingContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resource.HealthCheckResponse{Status: resource.HealthCheckResponse_SERVING}, nil
|
||||
return &resourcepb.HealthCheckResponse{Status: resourcepb.HealthCheckResponse_SERVING}, nil
|
||||
}
|
||||
|
||||
func (b *backend) Stop(_ context.Context) error {
|
||||
@@ -313,11 +314,11 @@ func (b *backend) WriteEvent(ctx context.Context, event resource.WriteEvent) (in
|
||||
defer span.End()
|
||||
// TODO: validate key ?
|
||||
switch event.Type {
|
||||
case resource.WatchEvent_ADDED:
|
||||
case resourcepb.WatchEvent_ADDED:
|
||||
return b.create(ctx, event)
|
||||
case resource.WatchEvent_MODIFIED:
|
||||
case resourcepb.WatchEvent_MODIFIED:
|
||||
return b.update(ctx, event)
|
||||
case resource.WatchEvent_DELETED:
|
||||
case resourcepb.WatchEvent_DELETED:
|
||||
return b.delete(ctx, event)
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported event type")
|
||||
@@ -525,7 +526,7 @@ func (b *backend) delete(ctx context.Context, event resource.WriteEvent) (int64,
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func (b *backend) ReadResource(ctx context.Context, req *resource.ReadRequest) *resource.BackendReadResponse {
|
||||
func (b *backend) ReadResource(ctx context.Context, req *resourcepb.ReadRequest) *resource.BackendReadResponse {
|
||||
_, span := b.tracer.Start(ctx, tracePrefix+".Read")
|
||||
defer span.End()
|
||||
|
||||
@@ -558,7 +559,7 @@ func (b *backend) ReadResource(ctx context.Context, req *resource.ReadRequest) *
|
||||
return res
|
||||
}
|
||||
|
||||
func (b *backend) ListIterator(ctx context.Context, req *resource.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
func (b *backend) ListIterator(ctx context.Context, req *resourcepb.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"List")
|
||||
defer span.End()
|
||||
|
||||
@@ -570,7 +571,7 @@ func (b *backend) ListIterator(ctx context.Context, req *resource.ListRequest, c
|
||||
return 0, fmt.Errorf("missing group or resource")
|
||||
}
|
||||
|
||||
if req.Source != resource.ListRequest_STORE {
|
||||
if req.Source != resourcepb.ListRequest_STORE {
|
||||
return b.getHistory(ctx, req, cb)
|
||||
}
|
||||
|
||||
@@ -652,7 +653,7 @@ func (l *listIter) Next() bool {
|
||||
var _ resource.ListIterator = (*listIter)(nil)
|
||||
|
||||
// listLatest fetches the resources from the resource table.
|
||||
func (b *backend) listLatest(ctx context.Context, req *resource.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
func (b *backend) listLatest(ctx context.Context, req *resourcepb.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"listLatest")
|
||||
defer span.End()
|
||||
|
||||
@@ -673,9 +674,9 @@ func (b *backend) listLatest(ctx context.Context, req *resource.ListRequest, cb
|
||||
|
||||
listReq := sqlResourceListRequest{
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
Request: new(resource.ListRequest),
|
||||
Request: new(resourcepb.ListRequest),
|
||||
}
|
||||
listReq.Request = proto.Clone(req).(*resource.ListRequest)
|
||||
listReq.Request = proto.Clone(req).(*resourcepb.ListRequest)
|
||||
|
||||
rows, err := dbutil.QueryRows(ctx, tx, sqlResourceList, listReq)
|
||||
if rows != nil {
|
||||
@@ -696,7 +697,7 @@ func (b *backend) listLatest(ctx context.Context, req *resource.ListRequest, cb
|
||||
}
|
||||
|
||||
// listAtRevision fetches the resources from the resource_history table at a specific revision.
|
||||
func (b *backend) listAtRevision(ctx context.Context, req *resource.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
func (b *backend) listAtRevision(ctx context.Context, req *resourcepb.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"listAtRevision")
|
||||
defer span.End()
|
||||
|
||||
@@ -756,7 +757,7 @@ func (b *backend) listAtRevision(ctx context.Context, req *resource.ListRequest,
|
||||
}
|
||||
|
||||
// readHistory fetches the resource history from the resource_history table.
|
||||
func (b *backend) readHistory(ctx context.Context, key *resource.ResourceKey, rv int64) *resource.BackendReadResponse {
|
||||
func (b *backend) readHistory(ctx context.Context, key *resourcepb.ResourceKey, rv int64) *resource.BackendReadResponse {
|
||||
_, span := b.tracer.Start(ctx, tracePrefix+".ReadHistory")
|
||||
defer span.End()
|
||||
|
||||
@@ -787,19 +788,19 @@ func (b *backend) readHistory(ctx context.Context, key *resource.ResourceKey, rv
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func (b *backend) getHistory(ctx context.Context, req *resourcepb.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"getHistory")
|
||||
defer span.End()
|
||||
listReq := sqlGetHistoryRequest{
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
Key: req.Options.Key,
|
||||
Trash: req.Source == resource.ListRequest_TRASH,
|
||||
Trash: req.Source == resourcepb.ListRequest_TRASH,
|
||||
}
|
||||
|
||||
// We are assuming that users want history in ascending order
|
||||
// when they are using NotOlderThan matching, and descending order
|
||||
// for Unset (default) and Exact matching.
|
||||
listReq.SortAscending = req.GetVersionMatchV2() == resource.ResourceVersionMatchV2_NotOlderThan
|
||||
listReq.SortAscending = req.GetVersionMatchV2() == resourcepb.ResourceVersionMatchV2_NotOlderThan
|
||||
|
||||
iter := &listIter{}
|
||||
if req.NextPageToken != "" {
|
||||
@@ -813,7 +814,7 @@ func (b *backend) getHistory(ctx context.Context, req *resource.ListRequest, cb
|
||||
iter.sortAsc = listReq.SortAscending
|
||||
|
||||
// Set ExactRV when using Exact matching
|
||||
if req.VersionMatchV2 == resource.ResourceVersionMatchV2_Exact {
|
||||
if req.VersionMatchV2 == resourcepb.ResourceVersionMatchV2_Exact {
|
||||
if req.ResourceVersion <= 0 {
|
||||
return 0, fmt.Errorf("expecting an explicit resource version query when using Exact matching")
|
||||
}
|
||||
@@ -821,12 +822,12 @@ func (b *backend) getHistory(ctx context.Context, req *resource.ListRequest, cb
|
||||
}
|
||||
|
||||
// Set MinRV when using NotOlderThan matching to filter at the database level
|
||||
if req.ResourceVersion > 0 && req.VersionMatchV2 == resource.ResourceVersionMatchV2_NotOlderThan {
|
||||
if req.ResourceVersion > 0 && req.VersionMatchV2 == resourcepb.ResourceVersionMatchV2_NotOlderThan {
|
||||
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
|
||||
useLatestDeletionAsMinRV := listReq.MinRV == 0 && !listReq.Trash && req.VersionMatchV2 != resourcepb.ResourceVersionMatchV2_Exact
|
||||
|
||||
err := b.db.WithTx(ctx, ReadCommittedRO, func(ctx context.Context, tx db.Tx) error {
|
||||
var err error
|
||||
@@ -836,7 +837,7 @@ func (b *backend) getHistory(ctx context.Context, req *resource.ListRequest, cb
|
||||
}
|
||||
|
||||
if useLatestDeletionAsMinRV {
|
||||
latestDeletedRV, err := b.fetchLatestHistoryRV(ctx, tx, b.dialect, req.Options.Key, resource.WatchEvent_DELETED)
|
||||
latestDeletedRV, err := b.fetchLatestHistoryRV(ctx, tx, b.dialect, req.Options.Key, resourcepb.WatchEvent_DELETED)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -914,7 +915,7 @@ func (b *backend) fetchLatestRV(ctx context.Context, x db.ContextExecer, d sqlte
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func (b *backend) fetchLatestHistoryRV(ctx context.Context, x db.ContextExecer, d sqltemplate.Dialect, key *resourcepb.ResourceKey, eventType resourcepb.WatchEvent_Type) (int64, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"fetchLatestHistoryRV")
|
||||
defer span.End()
|
||||
res, err := dbutil.QueryRow(ctx, x, sqlResourceHistoryReadLatestRV, sqlResourceHistoryReadLatestRVRequest{
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"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"
|
||||
@@ -22,7 +23,7 @@ import (
|
||||
|
||||
var (
|
||||
errTest = errors.New("things happened")
|
||||
resKey = &resource.ResourceKey{
|
||||
resKey = &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gr",
|
||||
Resource: "rs",
|
||||
@@ -212,7 +213,7 @@ func TestBackend_create(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
event := resource.WriteEvent{
|
||||
Type: resource.WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Key: resKey,
|
||||
Object: meta,
|
||||
}
|
||||
@@ -292,7 +293,7 @@ func TestBackend_update(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
meta.SetFolder("folderuid")
|
||||
event := resource.WriteEvent{
|
||||
Type: resource.WatchEvent_MODIFIED,
|
||||
Type: resourcepb.WatchEvent_MODIFIED,
|
||||
Key: resKey,
|
||||
Object: meta,
|
||||
}
|
||||
@@ -350,7 +351,7 @@ func TestBackend_delete(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
event := resource.WriteEvent{
|
||||
Type: resource.WatchEvent_DELETED,
|
||||
Type: resourcepb.WatchEvent_DELETED,
|
||||
Key: resKey,
|
||||
Object: meta,
|
||||
}
|
||||
@@ -445,7 +446,7 @@ func TestBackend_ReadResource(t *testing.T) {
|
||||
))
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
req := &resourcepb.ReadRequest{
|
||||
Key: resKey,
|
||||
}
|
||||
rps := b.ReadResource(ctx, req)
|
||||
@@ -464,7 +465,7 @@ func TestBackend_ReadResource(t *testing.T) {
|
||||
WillReturnRows(sqlmock.NewRows([]string{}))
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
req := &resourcepb.ReadRequest{
|
||||
Key: resKey,
|
||||
}
|
||||
res := b.ReadResource(ctx, req)
|
||||
@@ -503,7 +504,7 @@ func TestBackend_ReadResource(t *testing.T) {
|
||||
))
|
||||
b.SQLMock.ExpectCommit()
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
req := &resourcepb.ReadRequest{
|
||||
Key: resKey,
|
||||
ResourceVersion: 300,
|
||||
}
|
||||
@@ -521,7 +522,7 @@ func TestBackend_ReadResource(t *testing.T) {
|
||||
b.SQLMock.ExpectQuery("SELECT .* FROM resource_history").
|
||||
WillReturnError(errTest)
|
||||
|
||||
req := &resource.ReadRequest{
|
||||
req := &resourcepb.ReadRequest{
|
||||
Key: resKey,
|
||||
}
|
||||
rps := b.ReadResource(ctx, req)
|
||||
@@ -533,7 +534,7 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Common setup
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gr",
|
||||
Resource: "rs",
|
||||
@@ -544,8 +545,8 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
source resource.ListRequest_Source
|
||||
versionMatch resource.ResourceVersionMatchV2
|
||||
source resourcepb.ListRequest_Source
|
||||
versionMatch resourcepb.ResourceVersionMatchV2
|
||||
resourceVersion int64
|
||||
expectedVersions []int64
|
||||
expectedListRv int64
|
||||
@@ -555,8 +556,8 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "with ResourceVersionMatch_NotOlderThan",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
source: resourcepb.ListRequest_HISTORY,
|
||||
versionMatch: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
resourceVersion: rv2,
|
||||
expectedVersions: []int64{rv2, rv3}, // Should be in ASC order due to NotOlderThan
|
||||
expectedListRv: rv3,
|
||||
@@ -564,8 +565,8 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with ResourceVersionMatch_NotOlderThan and ResourceVersion=0",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
source: resourcepb.ListRequest_HISTORY,
|
||||
versionMatch: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
resourceVersion: 0,
|
||||
expectedVersions: []int64{rv1, rv2, rv3}, // Should be in ASC order due to NotOlderThan
|
||||
expectedListRv: rv3,
|
||||
@@ -574,8 +575,8 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with ResourceVersionMatch_Exact",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_Exact,
|
||||
source: resourcepb.ListRequest_HISTORY,
|
||||
versionMatch: resourcepb.ResourceVersionMatchV2_Exact,
|
||||
resourceVersion: rv2,
|
||||
expectedVersions: []int64{rv2},
|
||||
expectedListRv: rv3,
|
||||
@@ -583,7 +584,7 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with ResourceVersionMatch_Unset (default)",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
source: resourcepb.ListRequest_HISTORY,
|
||||
expectedVersions: []int64{rv3, rv2, rv1}, // Should be in DESC order by default
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
@@ -591,14 +592,14 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "error with ResourceVersionMatch_Exact and ResourceVersion <= 0",
|
||||
source: resource.ListRequest_HISTORY,
|
||||
versionMatch: resource.ResourceVersionMatchV2_Exact,
|
||||
source: resourcepb.ListRequest_HISTORY,
|
||||
versionMatch: resourcepb.ResourceVersionMatchV2_Exact,
|
||||
resourceVersion: 0,
|
||||
expectedErr: "expecting an explicit resource version query when using Exact matching",
|
||||
},
|
||||
{
|
||||
name: "with ListRequest_TRASH",
|
||||
source: resource.ListRequest_TRASH,
|
||||
source: resourcepb.ListRequest_TRASH,
|
||||
expectedVersions: []int64{rv3, rv2, rv1}, // Should be in DESC order by default
|
||||
expectedListRv: rv3,
|
||||
expectedRowsCount: 3,
|
||||
@@ -612,8 +613,8 @@ func TestBackend_getHistory(t *testing.T) {
|
||||
b, ctx := setupBackendTest(t)
|
||||
|
||||
// Build request with appropriate matcher
|
||||
req := &resource.ListRequest{
|
||||
Options: &resource.ListOptions{Key: key},
|
||||
req := &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{Key: key},
|
||||
ResourceVersion: tc.resourceVersion,
|
||||
VersionMatchV2: tc.versionMatch,
|
||||
Source: tc.source,
|
||||
@@ -700,7 +701,7 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Common setup
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gr",
|
||||
Resource: "rs",
|
||||
@@ -749,11 +750,11 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
|
||||
// Test each page
|
||||
for _, page := range pages {
|
||||
req := &resource.ListRequest{
|
||||
Options: &resource.ListOptions{Key: key},
|
||||
req := &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{Key: key},
|
||||
ResourceVersion: initialRV,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Limit: 4,
|
||||
}
|
||||
if page.token != nil {
|
||||
@@ -790,11 +791,11 @@ func TestBackend_getHistoryPagination(t *testing.T) {
|
||||
t.Run("pagination with ResourceVersion=0 and NotOlderThan should return entries in ASC order", func(t *testing.T) {
|
||||
b, ctx := setupBackendTest(t)
|
||||
|
||||
req := &resource.ListRequest{
|
||||
Options: &resource.ListOptions{Key: key},
|
||||
req := &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{Key: key},
|
||||
ResourceVersion: 0,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Limit: 4,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/dbutil"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
@@ -25,12 +26,12 @@ func (b *backend) SupportsSignedURLs() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *backend) PutResourceBlob(ctx context.Context, req *resource.PutBlobRequest) (*resource.PutBlobResponse, error) {
|
||||
func (b *backend) PutResourceBlob(ctx context.Context, req *resourcepb.PutBlobRequest) (*resourcepb.PutBlobResponse, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"PutResourceBlob")
|
||||
defer span.End()
|
||||
|
||||
if req.Method == resource.PutBlobRequest_HTTP {
|
||||
return &resource.PutBlobResponse{
|
||||
if req.Method == resourcepb.PutBlobRequest_HTTP {
|
||||
return &resourcepb.PutBlobResponse{
|
||||
Error: resource.NewBadRequestError("signed url upload not supported"),
|
||||
}, nil
|
||||
}
|
||||
@@ -49,7 +50,7 @@ func (b *backend) PutResourceBlob(ctx context.Context, req *resource.PutBlobRequ
|
||||
info.SetContentType(req.ContentType)
|
||||
|
||||
if info.Size < 1 {
|
||||
return &resource.PutBlobResponse{
|
||||
return &resourcepb.PutBlobResponse{
|
||||
Error: resource.NewBadRequestError("empty content"),
|
||||
}, nil
|
||||
}
|
||||
@@ -68,11 +69,11 @@ func (b *backend) PutResourceBlob(ctx context.Context, req *resource.PutBlobRequ
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return &resource.PutBlobResponse{
|
||||
return &resourcepb.PutBlobResponse{
|
||||
Error: resource.AsErrorResult(err),
|
||||
}, nil
|
||||
}
|
||||
return &resource.PutBlobResponse{
|
||||
return &resourcepb.PutBlobResponse{
|
||||
Uid: info.UID,
|
||||
Size: info.Size,
|
||||
MimeType: info.MimeType,
|
||||
@@ -81,17 +82,17 @@ func (b *backend) PutResourceBlob(ctx context.Context, req *resource.PutBlobRequ
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *backend) GetResourceBlob(ctx context.Context, key *resource.ResourceKey, info *utils.BlobInfo, mustProxy bool) (*resource.GetBlobResponse, error) {
|
||||
func (b *backend) GetResourceBlob(ctx context.Context, key *resourcepb.ResourceKey, info *utils.BlobInfo, mustProxy bool) (*resourcepb.GetBlobResponse, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+"GetResourceBlob")
|
||||
defer span.End()
|
||||
|
||||
if info == nil {
|
||||
return &resource.GetBlobResponse{
|
||||
return &resourcepb.GetBlobResponse{
|
||||
Error: resource.NewBadRequestError("missing blob info"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
rsp := &resource.GetBlobResponse{}
|
||||
rsp := &resourcepb.GetBlobResponse{}
|
||||
err := b.db.WithTx(ctx, ReadCommitted, func(ctx context.Context, tx db.Tx) error {
|
||||
rows, err := dbutil.QueryRows(ctx, tx, sqlResourceBlobQuery, sqlResourceBlobQueryRequest{
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
@@ -109,7 +110,7 @@ func (b *backend) GetResourceBlob(ctx context.Context, key *resource.ResourceKey
|
||||
}
|
||||
return err
|
||||
}
|
||||
rsp.Error = &resource.ErrorResult{
|
||||
rsp.Error = &resourcepb.ErrorResult{
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -14,9 +14,11 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/parquet"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/dbutil"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
@@ -62,14 +64,14 @@ type bulkLock struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (x *bulkLock) Start(keys []*resource.ResourceKey) error {
|
||||
func (x *bulkLock) Start(keys []*resourcepb.ResourceKey) error {
|
||||
x.mu.Lock()
|
||||
defer x.mu.Unlock()
|
||||
|
||||
// First verify that it is not already running
|
||||
ids := make([]string, len(keys))
|
||||
for i, k := range keys {
|
||||
id := k.NSGR()
|
||||
id := resource.NSGR(k)
|
||||
if x.running[id] {
|
||||
return &apierrors.StatusError{ErrStatus: metav1.Status{
|
||||
Code: http.StatusPreconditionFailed,
|
||||
@@ -86,11 +88,11 @@ func (x *bulkLock) Start(keys []*resource.ResourceKey) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *bulkLock) Finish(keys []*resource.ResourceKey) {
|
||||
func (x *bulkLock) Finish(keys []*resourcepb.ResourceKey) {
|
||||
x.mu.Lock()
|
||||
defer x.mu.Unlock()
|
||||
for _, k := range keys {
|
||||
delete(x.running, k.NSGR())
|
||||
delete(x.running, resource.NSGR(k))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +102,10 @@ func (x *bulkLock) Active() bool {
|
||||
return len(x.running) > 0
|
||||
}
|
||||
|
||||
func (b *backend) ProcessBulk(ctx context.Context, setting resource.BulkSettings, iter resource.BulkRequestIterator) *resource.BulkResponse {
|
||||
func (b *backend) ProcessBulk(ctx context.Context, setting resource.BulkSettings, iter resource.BulkRequestIterator) *resourcepb.BulkResponse {
|
||||
err := b.bulkLock.Start(setting.Collection)
|
||||
if err != nil {
|
||||
return &resource.BulkResponse{
|
||||
return &resourcepb.BulkResponse{
|
||||
Error: resource.AsErrorResult(err),
|
||||
}
|
||||
}
|
||||
@@ -113,14 +115,14 @@ func (b *backend) ProcessBulk(ctx context.Context, setting resource.BulkSettings
|
||||
if b.dialect.DialectName() == "sqlite" {
|
||||
file, err := os.CreateTemp("", "grafana-bulk-export-*.parquet")
|
||||
if err != nil {
|
||||
return &resource.BulkResponse{
|
||||
return &resourcepb.BulkResponse{
|
||||
Error: resource.AsErrorResult(err),
|
||||
}
|
||||
}
|
||||
|
||||
writer, err := parquet.NewParquetWriter(file)
|
||||
if err != nil {
|
||||
return &resource.BulkResponse{
|
||||
return &resourcepb.BulkResponse{
|
||||
Error: resource.AsErrorResult(err),
|
||||
}
|
||||
}
|
||||
@@ -136,7 +138,7 @@ func (b *backend) ProcessBulk(ctx context.Context, setting resource.BulkSettings
|
||||
// Replace the iterator with one from parquet
|
||||
iter, err = parquet.NewParquetReader(file.Name(), 50)
|
||||
if err != nil {
|
||||
return &resource.BulkResponse{
|
||||
return &resourcepb.BulkResponse{
|
||||
Error: resource.AsErrorResult(err),
|
||||
}
|
||||
}
|
||||
@@ -146,8 +148,8 @@ func (b *backend) ProcessBulk(ctx context.Context, setting resource.BulkSettings
|
||||
}
|
||||
|
||||
// internal bulk process
|
||||
func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings, iter resource.BulkRequestIterator) *resource.BulkResponse {
|
||||
rsp := &resource.BulkResponse{}
|
||||
func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings, iter resource.BulkRequestIterator) *resourcepb.BulkResponse {
|
||||
rsp := &resourcepb.BulkResponse{}
|
||||
err := b.db.WithTx(ctx, ReadCommitted, func(ctx context.Context, tx db.Tx) error {
|
||||
rollbackWithError := func(err error) error {
|
||||
txerr := tx.Rollback()
|
||||
@@ -168,7 +170,7 @@ func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings
|
||||
// Calculate the RV based on incoming request timestamps
|
||||
rv := newBulkRV()
|
||||
|
||||
summaries := make(map[string]*resource.BulkResponse_Summary, len(setting.Collection)*4)
|
||||
summaries := make(map[string]*resourcepb.BulkResponse_Summary, len(setting.Collection)*4)
|
||||
|
||||
// First clear everything in the transaction
|
||||
if setting.RebuildCollection {
|
||||
@@ -177,7 +179,7 @@ func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings
|
||||
if err != nil {
|
||||
return rollbackWithError(err)
|
||||
}
|
||||
summaries[key.NSGR()] = summary
|
||||
summaries[resource.NSGR(key)] = summary
|
||||
rsp.Summary = append(rsp.Summary, summary)
|
||||
}
|
||||
}
|
||||
@@ -195,8 +197,8 @@ func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings
|
||||
}
|
||||
rsp.Processed++
|
||||
|
||||
if req.Action == resource.BulkRequest_UNKNOWN {
|
||||
rsp.Rejected = append(rsp.Rejected, &resource.BulkResponse_Rejected{
|
||||
if req.Action == resourcepb.BulkRequest_UNKNOWN {
|
||||
rsp.Rejected = append(rsp.Rejected, &resourcepb.BulkResponse_Rejected{
|
||||
Key: req.Key,
|
||||
Action: req.Action,
|
||||
Error: "unknown action",
|
||||
@@ -206,7 +208,7 @@ func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings
|
||||
|
||||
err := obj.UnmarshalJSON(req.Value)
|
||||
if err != nil {
|
||||
rsp.Rejected = append(rsp.Rejected, &resource.BulkResponse_Rejected{
|
||||
rsp.Rejected = append(rsp.Rejected, &resourcepb.BulkResponse_Rejected{
|
||||
Key: req.Key,
|
||||
Action: req.Action,
|
||||
Error: "unable to unmarshal json",
|
||||
@@ -219,7 +221,7 @@ func (b *backend) processBulk(ctx context.Context, setting resource.BulkSettings
|
||||
SQLTemplate: sqltemplate.New(b.dialect),
|
||||
WriteEvent: resource.WriteEvent{
|
||||
Key: req.Key,
|
||||
Type: resource.WatchEvent_Type(req.Action),
|
||||
Type: resourcepb.WatchEvent_Type(req.Action),
|
||||
Value: req.Value,
|
||||
PreviousRV: -1, // Used for WATCH, but we want to skip watch events
|
||||
},
|
||||
@@ -268,8 +270,8 @@ type bulkWroker struct {
|
||||
}
|
||||
|
||||
// This will remove everything from the `resource` and `resource_history` table for a given namespace/group/resource
|
||||
func (w *bulkWroker) deleteCollection(key *resource.ResourceKey) (*resource.BulkResponse_Summary, error) {
|
||||
summary := &resource.BulkResponse_Summary{
|
||||
func (w *bulkWroker) deleteCollection(key *resourcepb.ResourceKey) (*resourcepb.BulkResponse_Summary, error) {
|
||||
summary := &resourcepb.BulkResponse_Summary{
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
Resource: key.Resource,
|
||||
@@ -306,8 +308,8 @@ func (w *bulkWroker) deleteCollection(key *resource.ResourceKey) (*resource.Bulk
|
||||
}
|
||||
|
||||
// Copy the latest value from history into the active resource table
|
||||
func (w *bulkWroker) syncCollection(key *resource.ResourceKey, summary *resource.BulkResponse_Summary) error {
|
||||
w.logger.Info("synchronize collection", "key", key.NSGR())
|
||||
func (w *bulkWroker) syncCollection(key *resourcepb.ResourceKey, summary *resourcepb.BulkResponse_Summary) error {
|
||||
w.logger.Info("synchronize collection", "key", resource.NSGR(key))
|
||||
_, err := dbutil.Exec(w.ctx, w.tx, sqlResourceInsertFromHistory, &sqlResourceInsertFromHistoryRequest{
|
||||
SQLTemplate: sqltemplate.New(w.dialect),
|
||||
Key: key,
|
||||
@@ -316,7 +318,7 @@ func (w *bulkWroker) syncCollection(key *resource.ResourceKey, summary *resource
|
||||
return err
|
||||
}
|
||||
|
||||
w.logger.Info("get stats (still in transaction)", "key", key.NSGR())
|
||||
w.logger.Info("get stats (still in transaction)", "key", resource.NSGR(key))
|
||||
rows, err := dbutil.QueryRows(w.ctx, w.tx, sqlResourceStats, &sqlStatsRequest{
|
||||
SQLTemplate: sqltemplate.New(w.dialect),
|
||||
Namespace: key.Namespace,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
@@ -197,13 +198,13 @@ func (p *pollingNotifier) poll(ctx context.Context, grp string, res string, sinc
|
||||
}
|
||||
stream <- &resource.WrittenEvent{
|
||||
Value: rec.Value,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: rec.Key.Namespace,
|
||||
Group: rec.Key.Group,
|
||||
Resource: rec.Key.Resource,
|
||||
Name: rec.Key.Name,
|
||||
},
|
||||
Type: resource.WatchEvent_Type(rec.Action),
|
||||
Type: resourcepb.WatchEvent_Type(rec.Action),
|
||||
PreviousRV: *prevRV,
|
||||
Folder: rec.Folder,
|
||||
ResourceVersion: rec.ResourceVersion,
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
@@ -209,7 +209,7 @@ func TestPollingNotifier(t *testing.T) {
|
||||
defer close(done)
|
||||
|
||||
testEvent := &historyPollResponse{
|
||||
Key: resource.ResourceKey{
|
||||
Key: resourcepb.ResourceKey{
|
||||
Namespace: "test-ns",
|
||||
Group: "test-group",
|
||||
Resource: "test-resource",
|
||||
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -21,8 +24,8 @@ func TestChannelNotifier(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
testEvent := &resource.WrittenEvent{
|
||||
Type: resource.WatchEvent_ADDED,
|
||||
Key: &resource.ResourceKey{
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Name: "test1",
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
@@ -88,7 +89,7 @@ func (r sqlResourceRequest) Validate() error {
|
||||
|
||||
type sqlResourceInsertFromHistoryRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Key *resource.ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
}
|
||||
|
||||
func (r sqlResourceInsertFromHistoryRequest) Validate() error {
|
||||
@@ -115,7 +116,7 @@ func (r sqlStatsRequest) Validate() error {
|
||||
}
|
||||
|
||||
type historyPollResponse struct {
|
||||
Key resource.ResourceKey
|
||||
Key resourcepb.ResourceKey
|
||||
GUID string
|
||||
ResourceVersion int64
|
||||
PreviousRV *int64
|
||||
@@ -148,7 +149,7 @@ func (r *sqlResourceHistoryPollRequest) Results() (*historyPollResponse, error)
|
||||
prevRV = new(int64)
|
||||
}
|
||||
return &historyPollResponse{
|
||||
Key: resource.ResourceKey{
|
||||
Key: resourcepb.ResourceKey{
|
||||
Namespace: r.Response.Key.Namespace,
|
||||
Group: r.Response.Key.Group,
|
||||
Resource: r.Response.Key.Resource,
|
||||
@@ -165,13 +166,13 @@ func (r *sqlResourceHistoryPollRequest) Results() (*historyPollResponse, error)
|
||||
// sqlResourceReadRequest can be used to retrieve a row fromthe "resource" tables.
|
||||
func NewReadResponse() *resource.BackendReadResponse {
|
||||
return &resource.BackendReadResponse{
|
||||
Key: &resource.ResourceKey{},
|
||||
Key: &resourcepb.ResourceKey{},
|
||||
}
|
||||
}
|
||||
|
||||
type sqlResourceReadRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Request *resource.ReadRequest
|
||||
Request *resourcepb.ReadRequest
|
||||
Response *resource.BackendReadResponse
|
||||
}
|
||||
|
||||
@@ -186,7 +187,7 @@ func (r *sqlResourceReadRequest) Results() (*resource.BackendReadResponse, error
|
||||
// List
|
||||
type sqlResourceListRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Request *resource.ListRequest
|
||||
Request *resourcepb.ListRequest
|
||||
}
|
||||
|
||||
func (r sqlResourceListRequest) Validate() error {
|
||||
@@ -194,7 +195,7 @@ func (r sqlResourceListRequest) Validate() error {
|
||||
}
|
||||
|
||||
type historyReadRequest struct {
|
||||
Key *resource.ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
ResourceVersion int64
|
||||
}
|
||||
|
||||
@@ -213,8 +214,8 @@ func (r sqlResourceHistoryReadRequest) Results() (*resource.BackendReadResponse,
|
||||
}
|
||||
|
||||
type historyReadLatestRVRequest struct {
|
||||
Key *resource.ResourceKey
|
||||
EventType resource.WatchEvent_Type
|
||||
Key *resourcepb.ResourceKey
|
||||
EventType resourcepb.WatchEvent_Type
|
||||
}
|
||||
|
||||
type sqlResourceHistoryReadLatestRVRequest struct {
|
||||
@@ -242,25 +243,25 @@ func (r *resourceHistoryReadLatestRVResponse) Results() (*resourceHistoryReadLat
|
||||
type historyListRequest struct {
|
||||
ResourceVersion, Limit, Offset int64
|
||||
Folder string
|
||||
Options *resource.ListOptions
|
||||
Options *resourcepb.ListOptions
|
||||
}
|
||||
type sqlResourceHistoryListRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Request *historyListRequest
|
||||
Response *resource.ResourceWrapper
|
||||
Response *resourcepb.ResourceWrapper
|
||||
}
|
||||
|
||||
func (r sqlResourceHistoryListRequest) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func (r sqlResourceHistoryListRequest) Results() (*resource.ResourceWrapper, error) {
|
||||
func (r sqlResourceHistoryListRequest) Results() (*resourcepb.ResourceWrapper, error) {
|
||||
// sqlResourceHistoryListRequest is a set-returning query. As such, it
|
||||
// should not return its *Response, since that will be overwritten in the
|
||||
// next call to `Scan`, so it needs to return a copy of it. Note, though,
|
||||
// that it is safe to return the same `Response.Value` since `Scan`
|
||||
// allocates a new slice of bytes each time.
|
||||
return &resource.ResourceWrapper{
|
||||
return &resourcepb.ResourceWrapper{
|
||||
ResourceVersion: r.Response.ResourceVersion,
|
||||
Value: r.Response.Value,
|
||||
}, nil
|
||||
@@ -292,7 +293,7 @@ func (r *sqlResourceHistoryDeleteRequest) Validate() error {
|
||||
|
||||
type sqlGetHistoryRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Key *resource.ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
Trash bool // only deleted items
|
||||
StartRV int64 // from NextPageToken
|
||||
MinRV int64 // minimum resource version for NotOlderThan
|
||||
@@ -307,7 +308,7 @@ func (r sqlGetHistoryRequest) Validate() error {
|
||||
// prune resource history
|
||||
type sqlPruneHistoryRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Key *resource.ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
PartitionByGeneration bool // include generation in the partition
|
||||
HistoryLimit int64
|
||||
}
|
||||
@@ -335,7 +336,7 @@ type sqlResourceBlobInsertRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Now time.Time
|
||||
Info *utils.BlobInfo
|
||||
Key *resource.ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
Value []byte
|
||||
ContentType string
|
||||
}
|
||||
@@ -349,7 +350,7 @@ func (r sqlResourceBlobInsertRequest) Validate() error {
|
||||
|
||||
type sqlResourceBlobQueryRequest struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Key *resource.ResourceKey
|
||||
Key *resourcepb.ResourceKey
|
||||
UID string
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate/mocks"
|
||||
)
|
||||
|
||||
@@ -21,7 +22,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Data: &sqlResourceRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
WriteEvent: resource.WriteEvent{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -37,13 +38,13 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Data: &sqlResourceRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
WriteEvent: resource.WriteEvent{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
Name: "name",
|
||||
},
|
||||
Type: resource.WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
PreviousRV: 123,
|
||||
},
|
||||
Folder: "fldr",
|
||||
@@ -56,7 +57,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Data: &sqlResourceRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
WriteEvent: resource.WriteEvent{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -72,8 +73,8 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "without_resource_version",
|
||||
Data: &sqlResourceReadRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &resource.ReadRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
Request: &resourcepb.ReadRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -90,10 +91,10 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "filter_on_namespace",
|
||||
Data: &sqlResourceListRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &resource.ListRequest{
|
||||
Request: &resourcepb.ListRequest{
|
||||
Limit: 10,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
@@ -109,13 +110,13 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &historyListRequest{
|
||||
Limit: 10,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: new(resource.ResourceWrapper),
|
||||
Response: new(resourcepb.ResourceWrapper),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -152,7 +153,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &historyReadRequest{
|
||||
ResourceVersion: 123,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gp",
|
||||
Resource: "rs",
|
||||
@@ -170,7 +171,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Data: &sqlResourceHistoryReadLatestRVRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &historyReadLatestRVRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gp",
|
||||
Resource: "rs",
|
||||
@@ -185,13 +186,13 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Data: &sqlResourceHistoryReadLatestRVRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Request: &historyReadLatestRVRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "ns",
|
||||
Group: "gp",
|
||||
Resource: "rs",
|
||||
Name: "nm",
|
||||
},
|
||||
EventType: resource.WatchEvent_DELETED,
|
||||
EventType: resourcepb.WatchEvent_DELETED,
|
||||
},
|
||||
Response: new(resourceHistoryReadLatestRVResponse),
|
||||
},
|
||||
@@ -218,7 +219,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Generation: 789,
|
||||
WriteEvent: resource.WriteEvent{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -236,7 +237,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "read object history",
|
||||
Data: &sqlGetHistoryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -248,7 +249,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "read trash",
|
||||
Data: &sqlGetHistoryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -260,7 +261,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "read trash second page",
|
||||
Data: &sqlGetHistoryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "nn",
|
||||
Group: "gg",
|
||||
Resource: "rr",
|
||||
@@ -276,7 +277,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "max-versions",
|
||||
Data: &sqlPruneHistoryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "provisioning.grafana.app",
|
||||
Resource: "repositories",
|
||||
@@ -289,7 +290,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "collapse-generations",
|
||||
Data: &sqlPruneHistoryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "provisioning.grafana.app",
|
||||
Resource: "repositories",
|
||||
@@ -386,7 +387,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "basic",
|
||||
Data: &sqlResourceBlobInsertRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "x",
|
||||
Group: "g",
|
||||
Resource: "r",
|
||||
@@ -409,7 +410,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "basic",
|
||||
Data: &sqlResourceBlobQueryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "x",
|
||||
Group: "g",
|
||||
Resource: "r",
|
||||
@@ -422,7 +423,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "resource", // NOTE: this returns multiple values
|
||||
Data: &sqlResourceBlobQueryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "x",
|
||||
Group: "g",
|
||||
Resource: "r",
|
||||
@@ -454,7 +455,7 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
Name: "update",
|
||||
Data: &sqlResourceInsertFromHistoryRequest{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
|
||||
@@ -8,15 +8,16 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/dbutil"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/dbutil"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -82,9 +83,9 @@ type writeOpResult struct {
|
||||
|
||||
// writeOp is a write operation that is executed with an incremented resource version
|
||||
type writeOp struct {
|
||||
key *resource.ResourceKey // The key of the resource
|
||||
fn WriteEventFunc // The function to execute to write the event
|
||||
done chan writeOpResult // A channel informing the operation is done
|
||||
key *resourcepb.ResourceKey // The key of the resource
|
||||
fn WriteEventFunc // The function to execute to write the event
|
||||
done chan writeOpResult // A channel informing the operation is done
|
||||
}
|
||||
|
||||
// WriteEventFunc is a function that writes a resource to the database
|
||||
@@ -128,7 +129,7 @@ func NewResourceVersionManager(opts ResourceManagerOptions) (*resourceVersionMan
|
||||
}
|
||||
|
||||
// ExecWithRV executes the given function with an incremented resource version
|
||||
func (m *resourceVersionManager) ExecWithRV(ctx context.Context, key *resource.ResourceKey, fn WriteEventFunc) (rv int64, err error) {
|
||||
func (m *resourceVersionManager) ExecWithRV(ctx context.Context, key *resourcepb.ResourceKey, fn WriteEventFunc) (rv int64, err error) {
|
||||
rvmInflightWrites.WithLabelValues(key.Group, key.Resource).Inc()
|
||||
defer rvmInflightWrites.WithLabelValues(key.Group, key.Resource).Dec()
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@ package sql
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sqlmock "github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/test"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func expectSuccessfulResourceVersionLock(t *testing.T, dbp test.TestDBProvider, rv int64, timestamp int64) {
|
||||
@@ -44,7 +45,7 @@ func TestResourceVersionManager(t *testing.T) {
|
||||
require.NotNil(t, manager)
|
||||
|
||||
t.Run("should handle single operation", func(t *testing.T) {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "test-group",
|
||||
Resource: "test-resource",
|
||||
}
|
||||
|
||||
@@ -6,17 +6,18 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/dbutil"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
// Support using SQL as fallback when the indexer is not running
|
||||
var _ resource.ResourceIndexServer = &backend{}
|
||||
var _ resourcepb.ResourceIndexServer = &backend{}
|
||||
|
||||
// GetStats implements resource.ResourceIndexServer.
|
||||
// This will use the SQL index to count values
|
||||
func (b *backend) GetStats(ctx context.Context, req *resource.ResourceStatsRequest) (*resource.ResourceStatsResponse, error) {
|
||||
func (b *backend) GetStats(ctx context.Context, req *resourcepb.ResourceStatsRequest) (*resourcepb.ResourceStatsResponse, error) {
|
||||
ctx, span := b.tracer.Start(ctx, tracePrefix+".GetStats")
|
||||
defer span.End()
|
||||
|
||||
@@ -26,7 +27,7 @@ func (b *backend) GetStats(ctx context.Context, req *resource.ResourceStatsReque
|
||||
Folder: req.Folder,
|
||||
}
|
||||
|
||||
rsp := &resource.ResourceStatsResponse{}
|
||||
rsp := &resourcepb.ResourceStatsResponse{}
|
||||
err := b.db.WithTx(ctx, ReadCommittedRO, func(ctx context.Context, tx db.Tx) error {
|
||||
rows, err := dbutil.QueryRows(ctx, tx, sqlResourceStats, sreq)
|
||||
if err != nil {
|
||||
@@ -39,7 +40,7 @@ func (b *backend) GetStats(ctx context.Context, req *resource.ResourceStatsReque
|
||||
return err
|
||||
}
|
||||
|
||||
rsp.Stats = append(rsp.Stats, &resource.ResourceStatsResponse_Stats{
|
||||
rsp.Stats = append(rsp.Stats, &resourcepb.ResourceStatsResponse_Stats{
|
||||
Group: row.Group,
|
||||
Resource: row.Resource,
|
||||
Count: row.Count,
|
||||
@@ -53,18 +54,18 @@ func (b *backend) GetStats(ctx context.Context, req *resource.ResourceStatsReque
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (b *backend) RepositoryList(ctx context.Context, req *resource.ListManagedObjectsRequest) (*resource.ListManagedObjectsResponse, error) {
|
||||
func (b *backend) RepositoryList(ctx context.Context, req *resourcepb.ListManagedObjectsRequest) (*resourcepb.ListManagedObjectsResponse, error) {
|
||||
return nil, fmt.Errorf("SQL backend does not implement RepositoryList")
|
||||
}
|
||||
|
||||
func (b *backend) RepositoryStats(context.Context, *resource.CountManagedObjectsRequest) (*resource.CountManagedObjectsResponse, error) {
|
||||
func (b *backend) RepositoryStats(context.Context, *resourcepb.CountManagedObjectsRequest) (*resourcepb.CountManagedObjectsResponse, error) {
|
||||
return nil, fmt.Errorf("SQL backend does not implement RepositoryStats")
|
||||
}
|
||||
|
||||
// Search implements resource.ResourceIndexServer.
|
||||
func (b *backend) Search(context.Context, *resource.ResourceSearchRequest) (*resource.ResourceSearchResponse, error) {
|
||||
return &resource.ResourceSearchResponse{
|
||||
Error: &resource.ErrorResult{
|
||||
func (b *backend) Search(context.Context, *resourcepb.ResourceSearchRequest) (*resourcepb.ResourceSearchResponse, error) {
|
||||
return &resourcepb.ResourceSearchResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Code: http.StatusNotImplemented,
|
||||
Message: "SQL backend does not implement Search",
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/search"
|
||||
)
|
||||
|
||||
@@ -136,12 +137,12 @@ func (s *service) start(ctx context.Context) error {
|
||||
}
|
||||
|
||||
srv := s.handler.GetServer()
|
||||
resource.RegisterResourceStoreServer(srv, server)
|
||||
resource.RegisterBulkStoreServer(srv, server)
|
||||
resource.RegisterResourceIndexServer(srv, server)
|
||||
resource.RegisterManagedObjectIndexServer(srv, server)
|
||||
resource.RegisterBlobStoreServer(srv, server)
|
||||
resource.RegisterDiagnosticsServer(srv, server)
|
||||
resourcepb.RegisterResourceStoreServer(srv, server)
|
||||
resourcepb.RegisterBulkStoreServer(srv, server)
|
||||
resourcepb.RegisterResourceIndexServer(srv, server)
|
||||
resourcepb.RegisterManagedObjectIndexServer(srv, server)
|
||||
resourcepb.RegisterBlobStoreServer(srv, server)
|
||||
resourcepb.RegisterDiagnosticsServer(srv, server)
|
||||
grpc_health_v1.RegisterHealthServer(srv, healthService)
|
||||
|
||||
// register reflection service
|
||||
|
||||
@@ -13,12 +13,14 @@ import (
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/services"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/search"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/db/dbimpl"
|
||||
@@ -157,7 +159,7 @@ func TestClientServer(t *testing.T) {
|
||||
|
||||
svc, err := sql.ProvideUnifiedStorageGrpcService(cfg, features, dbstore, nil, prometheus.NewPedanticRegistry(), nil, nil, nil, nil)
|
||||
require.NoError(t, err)
|
||||
var client resource.ResourceStoreClient
|
||||
var client resourcepb.ResourceStoreClient
|
||||
|
||||
clientCtx := types.WithAuthInfo(context.Background(), authn.NewAccessTokenAuthInfo(authn.Claims[authn.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
@@ -193,7 +195,7 @@ func TestClientServer(t *testing.T) {
|
||||
},
|
||||
"spec": {}
|
||||
}`)
|
||||
resp, err := client.Create(clientCtx, &resource.CreateRequest{
|
||||
resp, err := client.Create(clientCtx, &resourcepb.CreateRequest{
|
||||
Key: resourceKey("item1"),
|
||||
Value: raw,
|
||||
})
|
||||
@@ -203,7 +205,7 @@ func TestClientServer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Read the resource", func(t *testing.T) {
|
||||
resp, err := client.Read(clientCtx, &resource.ReadRequest{
|
||||
resp, err := client.Read(clientCtx, &resourcepb.ReadRequest{
|
||||
Key: resourceKey("item1"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -217,8 +219,8 @@ func TestClientServer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func resourceKey(name string) *resource.ResourceKey {
|
||||
return &resource.ResourceKey{
|
||||
func resourceKey(name string) *resourcepb.ResourceKey {
|
||||
return &resourcepb.ResourceKey{
|
||||
Namespace: "namespace",
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -53,7 +55,7 @@ func initializeBackend(ctx context.Context, backend resource.StorageBackend, opt
|
||||
group := fmt.Sprintf("group-%d", g)
|
||||
for r := 0; r < opts.NumResourceTypes; r++ {
|
||||
resourceType := fmt.Sprintf("resource-%d", r)
|
||||
_, err := writeEvent(ctx, backend, "init", resource.WatchEvent_ADDED,
|
||||
_, err := writeEvent(ctx, backend, "init", resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(namespace),
|
||||
WithGroup(group),
|
||||
WithResource(resourceType),
|
||||
@@ -104,7 +106,7 @@ func runStorageBackendBenchmark(ctx context.Context, backend resource.StorageBac
|
||||
name := fmt.Sprintf("item-%d", uniqueID)
|
||||
|
||||
writeStart := time.Now()
|
||||
_, err := writeEvent(ctx, backend, name, resource.WatchEvent_ADDED,
|
||||
_, err := writeEvent(ctx, backend, name, resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(namespace),
|
||||
WithGroup(group),
|
||||
WithResource(resourceType),
|
||||
@@ -232,7 +234,7 @@ func runSearchBackendBenchmarkWriteThroughput(ctx context.Context, backend resou
|
||||
|
||||
for jobID := range jobs {
|
||||
doc := &resource.IndexableDocument{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: nr.Namespace,
|
||||
Group: nr.Group,
|
||||
Resource: nr.Resource,
|
||||
@@ -420,7 +422,7 @@ func BenchmarkIndexServer(tb testing.TB, ctx context.Context, backend resource.S
|
||||
// testDocumentBuilder implements DocumentBuilder for testing
|
||||
type testDocumentBuilder struct{}
|
||||
|
||||
func (b *testDocumentBuilder) BuildDocument(ctx context.Context, key *resource.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
|
||||
func (b *testDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
|
||||
// convert value to unstructured.Unstructured
|
||||
var u unstructured.Unstructured
|
||||
if err := u.UnmarshalJSON(value); err != nil {
|
||||
@@ -451,7 +453,7 @@ func (b *testDocumentBuilder) BuildDocument(ctx context.Context, key *resource.R
|
||||
}
|
||||
}
|
||||
return &resource.IndexableDocument{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: key.Namespace,
|
||||
Group: key.Group,
|
||||
Resource: key.Resource,
|
||||
|
||||
@@ -7,9 +7,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
@@ -48,7 +51,7 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
}
|
||||
|
||||
for _, doc := range initialResources {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix,
|
||||
@@ -80,7 +83,7 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
|
||||
// Create document
|
||||
rv, err := backend.WriteEvent(ctx, resource.WriteEvent{
|
||||
Type: resource.WatchEvent_ADDED,
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Key: key,
|
||||
Value: value,
|
||||
Object: meta,
|
||||
@@ -111,9 +114,9 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
|
||||
t.Run("Search for initial resources", func(t *testing.T) {
|
||||
// Test 1: Search for initial resources
|
||||
searchResp, err := server.Search(ctx, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix,
|
||||
@@ -152,7 +155,7 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
}
|
||||
|
||||
for _, doc := range testDocs {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix,
|
||||
@@ -180,7 +183,7 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create document
|
||||
createResp, err := server.Create(ctx, &resource.CreateRequest{
|
||||
createResp, err := server.Create(ctx, &resourcepb.CreateRequest{
|
||||
Key: key,
|
||||
Value: value,
|
||||
})
|
||||
@@ -194,9 +197,9 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
})
|
||||
|
||||
t.Run("Search for documents", func(t *testing.T) {
|
||||
searchResp, err := server.Search(ctx, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix,
|
||||
@@ -212,9 +215,9 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
})
|
||||
|
||||
t.Run("Search with tags", func(t *testing.T) {
|
||||
searchResp, err := server.Search(ctx, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix,
|
||||
@@ -230,9 +233,9 @@ func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource
|
||||
})
|
||||
|
||||
t.Run("Search with specific tag", func(t *testing.T) {
|
||||
searchResp, err := server.Search(ctx, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
@@ -74,7 +75,7 @@ func runTestSearchBackendBuildIndex(t *testing.T, backend resource.SearchBackend
|
||||
{
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
@@ -120,7 +121,7 @@ func runTestResourceIndex(t *testing.T, backend resource.SearchBackend, nsPrefix
|
||||
{
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
@@ -137,7 +138,7 @@ func runTestResourceIndex(t *testing.T, backend resource.SearchBackend, nsPrefix
|
||||
{
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
@@ -160,9 +161,9 @@ func runTestResourceIndex(t *testing.T, backend resource.SearchBackend, nsPrefix
|
||||
require.NotNil(t, index)
|
||||
|
||||
t.Run("Search", func(t *testing.T) {
|
||||
resp, err := index.Search(ctx, nil, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
resp, err := index.Search(ctx, nil, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
@@ -177,9 +178,9 @@ func runTestResourceIndex(t *testing.T, backend resource.SearchBackend, nsPrefix
|
||||
require.Equal(t, int64(1), resp.TotalHits) // Only doc3 should have tag3 now
|
||||
|
||||
// Search for Document
|
||||
resp, err = index.Search(ctx, nil, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
resp, err = index.Search(ctx, nil, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
@@ -201,7 +202,7 @@ func runTestResourceIndex(t *testing.T, backend resource.SearchBackend, nsPrefix
|
||||
{
|
||||
Action: resource.ActionIndex,
|
||||
Doc: &resource.IndexableDocument{
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
@@ -219,9 +220,9 @@ func runTestResourceIndex(t *testing.T, backend resource.SearchBackend, nsPrefix
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Search for Document
|
||||
resp, err := index.Search(ctx, nil, &resource.ResourceSearchRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
resp, err := index.Search(ctx, nil, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns.Namespace,
|
||||
Group: ns.Group,
|
||||
Resource: ns.Resource,
|
||||
|
||||
@@ -7,9 +7,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -137,7 +140,7 @@ func runTestResourcePermissionScenarios(t *testing.T, backend resource.StorageBa
|
||||
|
||||
ctx := types.WithAuthInfo(context.Background(), testUser)
|
||||
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Group: "test.grafana.app",
|
||||
Resource: "testresources",
|
||||
Namespace: nsPrefix + "-ns1",
|
||||
@@ -163,7 +166,7 @@ func runTestResourcePermissionScenarios(t *testing.T, backend resource.StorageBa
|
||||
}`, resourceName, resourceUID, nsPrefix+"-ns1", tc.initialFolder, i)
|
||||
|
||||
checksPerformed = []types.CheckRequest{}
|
||||
created, err := server.Create(ctx, &resource.CreateRequest{
|
||||
created, err := server.Create(ctx, &resourcepb.CreateRequest{
|
||||
Value: []byte(resourceJSON),
|
||||
Key: key,
|
||||
})
|
||||
@@ -202,7 +205,7 @@ func runTestResourcePermissionScenarios(t *testing.T, backend resource.StorageBa
|
||||
|
||||
// Override permissions for initial creation to always succeed
|
||||
mockAccess.allowed = true
|
||||
created, err := server.Create(ctx, &resource.CreateRequest{
|
||||
created, err := server.Create(ctx, &resourcepb.CreateRequest{
|
||||
Value: []byte(initialResourceJSON),
|
||||
Key: key,
|
||||
})
|
||||
@@ -229,7 +232,7 @@ func runTestResourcePermissionScenarios(t *testing.T, backend resource.StorageBa
|
||||
mockAccess.allowed = false // Reset to use the map
|
||||
checksPerformed = []types.CheckRequest{}
|
||||
|
||||
updated, err := server.Update(ctx, &resource.UpdateRequest{
|
||||
updated, err := server.Update(ctx, &resourcepb.UpdateRequest{
|
||||
Key: key,
|
||||
Value: []byte(targetResourceJSON),
|
||||
ResourceVersion: created.ResourceVersion,
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
@@ -108,34 +109,34 @@ func runTestIntegrationBackendHappyPath(t *testing.T, backend resource.StorageBa
|
||||
var rv1, rv2, rv3, rv4, rv5 int64
|
||||
|
||||
t.Run("Add 3 resources", func(t *testing.T) {
|
||||
rv1, err = writeEvent(ctx, backend, "item1", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv1, err = writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv1, int64(0))
|
||||
|
||||
rv2, err = writeEvent(ctx, backend, "item2", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv2, err = writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv2, rv1)
|
||||
|
||||
rv3, err = writeEvent(ctx, backend, "item3", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv3, err = writeEvent(ctx, backend, "item3", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv3, rv2)
|
||||
})
|
||||
|
||||
t.Run("Update item2", func(t *testing.T) {
|
||||
rv4, err = writeEvent(ctx, backend, "item2", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rv4, err = writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv4, rv3)
|
||||
})
|
||||
|
||||
t.Run("Delete item1", func(t *testing.T) {
|
||||
rv5, err = writeEvent(ctx, backend, "item1", resource.WatchEvent_DELETED, WithNamespace(ns))
|
||||
rv5, err = writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv5, rv4)
|
||||
})
|
||||
|
||||
t.Run("Read latest item 2", func(t *testing.T) {
|
||||
resp := backend.ReadResource(ctx, &resource.ReadRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
resp := backend.ReadResource(ctx, &resourcepb.ReadRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "item2",
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
@@ -149,8 +150,8 @@ func runTestIntegrationBackendHappyPath(t *testing.T, backend resource.StorageBa
|
||||
})
|
||||
|
||||
t.Run("Read early version of item2", func(t *testing.T) {
|
||||
resp := backend.ReadResource(ctx, &resource.ReadRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
resp := backend.ReadResource(ctx, &resourcepb.ReadRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Name: "item2",
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
@@ -164,9 +165,9 @@ func runTestIntegrationBackendHappyPath(t *testing.T, backend resource.StorageBa
|
||||
})
|
||||
|
||||
t.Run("List latest", func(t *testing.T) {
|
||||
resp, err := server.List(ctx, &resource.ListRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
resp, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -185,29 +186,29 @@ func runTestIntegrationBackendHappyPath(t *testing.T, backend resource.StorageBa
|
||||
event := <-stream
|
||||
require.Equal(t, "item1", event.Key.Name)
|
||||
require.Equal(t, rv1, event.ResourceVersion)
|
||||
require.Equal(t, resource.WatchEvent_ADDED, event.Type)
|
||||
require.Equal(t, resourcepb.WatchEvent_ADDED, event.Type)
|
||||
require.Equal(t, "folderuid", event.Folder)
|
||||
|
||||
event = <-stream
|
||||
require.Equal(t, "item2", event.Key.Name)
|
||||
require.Equal(t, rv2, event.ResourceVersion)
|
||||
require.Equal(t, resource.WatchEvent_ADDED, event.Type)
|
||||
require.Equal(t, resourcepb.WatchEvent_ADDED, event.Type)
|
||||
require.Equal(t, "folderuid", event.Folder)
|
||||
|
||||
event = <-stream
|
||||
require.Equal(t, "item3", event.Key.Name)
|
||||
require.Equal(t, rv3, event.ResourceVersion)
|
||||
require.Equal(t, resource.WatchEvent_ADDED, event.Type)
|
||||
require.Equal(t, resourcepb.WatchEvent_ADDED, event.Type)
|
||||
|
||||
event = <-stream
|
||||
require.Equal(t, "item2", event.Key.Name)
|
||||
require.Equal(t, rv4, event.ResourceVersion)
|
||||
require.Equal(t, resource.WatchEvent_MODIFIED, event.Type)
|
||||
require.Equal(t, resourcepb.WatchEvent_MODIFIED, event.Type)
|
||||
|
||||
event = <-stream
|
||||
require.Equal(t, "item1", event.Key.Name)
|
||||
require.Equal(t, rv5, event.ResourceVersion)
|
||||
require.Equal(t, resource.WatchEvent_DELETED, event.Type)
|
||||
require.Equal(t, resourcepb.WatchEvent_DELETED, event.Type)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -224,31 +225,31 @@ func runTestIntegrationBackendGetResourceStats(t *testing.T, backend resource.St
|
||||
return strings.Compare(a.Resource, b.Resource)
|
||||
}
|
||||
// Create resources across different namespaces/groups
|
||||
_, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_ADDED,
|
||||
_, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(nsPrefix+"-ns1"),
|
||||
WithGroup("group"),
|
||||
WithResource("resource1"))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = writeEvent(ctx, backend, "item2", resource.WatchEvent_ADDED,
|
||||
_, err = writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(nsPrefix+"-ns1"),
|
||||
WithGroup("group"),
|
||||
WithResource("resource1"))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = writeEvent(ctx, backend, "item3", resource.WatchEvent_ADDED,
|
||||
_, err = writeEvent(ctx, backend, "item3", resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(nsPrefix+"-ns1"),
|
||||
WithGroup("group"),
|
||||
WithResource("resource2"))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = writeEvent(ctx, backend, "item4", resource.WatchEvent_ADDED,
|
||||
_, err = writeEvent(ctx, backend, "item4", resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(nsPrefix+"-ns2"),
|
||||
WithGroup("group"),
|
||||
WithResource("resource1"))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = writeEvent(ctx, backend, "item5", resource.WatchEvent_ADDED,
|
||||
_, err = writeEvent(ctx, backend, "item5", resourcepb.WatchEvent_ADDED,
|
||||
WithNamespace(nsPrefix+"-ns2"),
|
||||
WithGroup("group"),
|
||||
WithResource("resource1"))
|
||||
@@ -311,7 +312,7 @@ func runTestIntegrationBackendWatchWriteEvents(t *testing.T, backend resource.St
|
||||
ctx := testutil.NewTestContext(t, time.Now().Add(5*time.Second))
|
||||
|
||||
// Create a few resources before initing the watch
|
||||
_, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_ADDED, WithNamespace(nsPrefix+"-ns1"))
|
||||
_, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED, WithNamespace(nsPrefix+"-ns1"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Start the watch
|
||||
@@ -319,7 +320,7 @@ func runTestIntegrationBackendWatchWriteEvents(t *testing.T, backend resource.St
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create one more event
|
||||
_, err = writeEvent(ctx, backend, "item2", resource.WatchEvent_ADDED, WithNamespace(nsPrefix+"-ns1"))
|
||||
_, err = writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(nsPrefix+"-ns1"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "item2", (<-stream).Key.Name)
|
||||
|
||||
@@ -335,35 +336,35 @@ func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend
|
||||
server := newServer(t, backend)
|
||||
ns := nsPrefix + "-ns1"
|
||||
// Create a few resources before starting the watch
|
||||
rv1, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv1, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv1, int64(0))
|
||||
rv2, err := writeEvent(ctx, backend, "item2", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv2, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv2, rv1)
|
||||
rv3, err := writeEvent(ctx, backend, "item3", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv3, err := writeEvent(ctx, backend, "item3", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv3, rv2)
|
||||
rv4, err := writeEvent(ctx, backend, "item4", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv4, err := writeEvent(ctx, backend, "item4", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv4, rv3)
|
||||
rv5, err := writeEvent(ctx, backend, "item5", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv5, err := writeEvent(ctx, backend, "item5", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv5, rv4)
|
||||
rv6, err := writeEvent(ctx, backend, "item2", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rv6, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv6, rv5)
|
||||
rv7, err := writeEvent(ctx, backend, "item3", resource.WatchEvent_DELETED, WithNamespace(ns))
|
||||
rv7, err := writeEvent(ctx, backend, "item3", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv7, rv6)
|
||||
rv8, err := writeEvent(ctx, backend, "item6", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv8, err := writeEvent(ctx, backend, "item6", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv8, rv7)
|
||||
|
||||
t.Run("fetch all latest", func(t *testing.T) {
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
},
|
||||
@@ -383,10 +384,10 @@ func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend
|
||||
})
|
||||
|
||||
t.Run("list latest first page ", func(t *testing.T) {
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
},
|
||||
@@ -404,10 +405,10 @@ func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend
|
||||
})
|
||||
|
||||
t.Run("list at revision", func(t *testing.T) {
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
ResourceVersion: rv4,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
},
|
||||
@@ -424,11 +425,11 @@ func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend
|
||||
})
|
||||
|
||||
t.Run("fetch first page at revision with limit", func(t *testing.T) {
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
ResourceVersion: rv7,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
},
|
||||
@@ -454,11 +455,11 @@ func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend
|
||||
StartOffset: 2,
|
||||
SortAscending: false,
|
||||
}
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
NextPageToken: continueToken.String(),
|
||||
Limit: 2,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
},
|
||||
@@ -482,28 +483,28 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
|
||||
server := newServer(t, backend)
|
||||
ns := nsPrefix + "-ns1"
|
||||
rv1, _ := writeEvent(ctx, backend, "item1", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv1, _ := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.Greater(t, rv1, int64(0))
|
||||
|
||||
// add 5 events for item1 - should be saved to history
|
||||
rvHistory1, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rvHistory1, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvHistory1, rv1)
|
||||
rvHistory2, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rvHistory2, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvHistory2, rvHistory1)
|
||||
rvHistory3, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rvHistory3, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvHistory3, rvHistory2)
|
||||
rvHistory4, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rvHistory4, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvHistory4, rvHistory3)
|
||||
rvHistory5, err := writeEvent(ctx, backend, "item1", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rvHistory5, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvHistory5, rvHistory4)
|
||||
|
||||
t.Run("fetch history with different version matching", func(t *testing.T) {
|
||||
baseKey := &resource.ResourceKey{
|
||||
baseKey := &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -512,7 +513,7 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request *resource.ListRequest
|
||||
request *resourcepb.ListRequest
|
||||
expectedVersions []int64
|
||||
expectedValues []string
|
||||
minExpectedHeadRV int64
|
||||
@@ -521,12 +522,12 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
}{
|
||||
{
|
||||
name: "NotOlderThan with rv1 (ASC order)",
|
||||
request: &resource.ListRequest{
|
||||
request: &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
ResourceVersion: rv1,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resource.ListOptions{
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: baseKey,
|
||||
},
|
||||
},
|
||||
@@ -538,12 +539,12 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
},
|
||||
{
|
||||
name: "NotOlderThan with rv=0 (ASC order)",
|
||||
request: &resource.ListRequest{
|
||||
request: &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
ResourceVersion: 0,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resource.ListOptions{
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: baseKey,
|
||||
},
|
||||
},
|
||||
@@ -555,10 +556,10 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
},
|
||||
{
|
||||
name: "ResourceVersionMatch_Unset (DESC order)",
|
||||
request: &resource.ListRequest{
|
||||
request: &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Options: &resource.ListOptions{
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: baseKey,
|
||||
},
|
||||
},
|
||||
@@ -597,12 +598,12 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
// Test pagination for NotOlderThan (second page)
|
||||
t.Run("second page with NotOlderThan", func(t *testing.T) {
|
||||
// Get first page
|
||||
firstRequest := &resource.ListRequest{
|
||||
firstRequest := &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
ResourceVersion: rv1,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resource.ListOptions{Key: baseKey},
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resourcepb.ListOptions{Key: baseKey},
|
||||
}
|
||||
firstPageRes, err := server.List(ctx, firstRequest)
|
||||
require.NoError(t, err)
|
||||
@@ -612,13 +613,13 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get second page
|
||||
secondPageRes, err := server.List(ctx, &resource.ListRequest{
|
||||
secondPageRes, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Limit: 3,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
ResourceVersion: rv1,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
NextPageToken: continueToken.String(),
|
||||
Options: &resource.ListOptions{Key: baseKey},
|
||||
Options: &resourcepb.ListOptions{Key: baseKey},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, secondPageRes.Error)
|
||||
@@ -639,12 +640,12 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
StartOffset: 2,
|
||||
SortAscending: false,
|
||||
}
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
NextPageToken: continueToken.String(),
|
||||
Limit: 2,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -665,7 +666,7 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
t.Run("paginated history with NotOlderThan returns items in ascending order", func(t *testing.T) {
|
||||
// Create 10 versions of a resource to test pagination
|
||||
ns2 := nsPrefix + "-ns2"
|
||||
resourceKey := &resource.ResourceKey{
|
||||
resourceKey := &resourcepb.ResourceKey{
|
||||
Namespace: ns2,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -675,13 +676,13 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
var resourceVersions []int64
|
||||
|
||||
// First create the initial resource
|
||||
initialRV, err := writeEvent(ctx, backend, "paged-item", resource.WatchEvent_ADDED, WithNamespace(ns2))
|
||||
initialRV, err := writeEvent(ctx, backend, "paged-item", resourcepb.WatchEvent_ADDED, WithNamespace(ns2))
|
||||
require.NoError(t, err)
|
||||
resourceVersions = append(resourceVersions, initialRV)
|
||||
|
||||
// Create 9 more versions with modifications
|
||||
for i := 0; i < 9; i++ {
|
||||
rv, err := writeEvent(ctx, backend, "paged-item", resource.WatchEvent_MODIFIED, WithNamespace(ns2))
|
||||
rv, err := writeEvent(ctx, backend, "paged-item", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns2))
|
||||
require.NoError(t, err)
|
||||
resourceVersions = append(resourceVersions, rv)
|
||||
}
|
||||
@@ -701,16 +702,16 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
{pageNumber: 4, pageSize: 1, startToken: ""}, // Will be set in the test - last page with remaining item
|
||||
}
|
||||
|
||||
var allItems []*resource.ResourceWrapper
|
||||
var allItems []*resourcepb.ResourceWrapper
|
||||
|
||||
// Request first page with NotOlderThan and ResourceVersion=0 (should start from oldest)
|
||||
for i, page := range pages {
|
||||
req := &resource.ListRequest{
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: int64(page.pageSize),
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
ResourceVersion: 0,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resource.ListOptions{
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: resourceKey,
|
||||
},
|
||||
}
|
||||
@@ -781,17 +782,17 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
|
||||
t.Run("fetch history with deleted item", func(t *testing.T) {
|
||||
// Create a resource and delete it
|
||||
rv, err := writeEvent(ctx, backend, "deleted-item", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv, err := writeEvent(ctx, backend, "deleted-item", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
rvDeleted, err := writeEvent(ctx, backend, "deleted-item", resource.WatchEvent_DELETED, WithNamespace(ns))
|
||||
rvDeleted, err := writeEvent(ctx, backend, "deleted-item", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDeleted, rv)
|
||||
|
||||
// Fetch history for the deleted item
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -806,25 +807,25 @@ func runTestIntegrationBackendListHistory(t *testing.T, backend resource.Storage
|
||||
|
||||
t.Run("fetch history with recreated item", func(t *testing.T) {
|
||||
// Create a resource and delete it
|
||||
rv, err := writeEvent(ctx, backend, "deleted-item", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv, err := writeEvent(ctx, backend, "deleted-item", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
rvDeleted, err := writeEvent(ctx, backend, "deleted-item", resource.WatchEvent_DELETED, WithNamespace(ns))
|
||||
rvDeleted, err := writeEvent(ctx, backend, "deleted-item", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDeleted, rv)
|
||||
|
||||
// Create a few more versions after deletion
|
||||
rv1, err := writeEvent(ctx, backend, "deleted-item", resource.WatchEvent_ADDED, WithNamespace(ns))
|
||||
rv1, err := writeEvent(ctx, backend, "deleted-item", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv1, rvDeleted)
|
||||
rv2, err := writeEvent(ctx, backend, "deleted-item", resource.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
rv2, err := writeEvent(ctx, backend, "deleted-item", resourcepb.WatchEvent_MODIFIED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv2, rv1)
|
||||
|
||||
// Fetch history for the deleted item
|
||||
res, err := server.List(ctx, &resource.ListRequest{
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
res, err := server.List(ctx, &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
@@ -854,26 +855,26 @@ func runTestIntegrationBackendListHistoryErrorReporting(t *testing.T, backend re
|
||||
)
|
||||
|
||||
start := time.Now()
|
||||
origRv, _ := writeEvent(ctx, backend, name, resource.WatchEvent_ADDED, WithNamespace(ns), WithGroup(group), WithResource(resourceName))
|
||||
origRv, _ := writeEvent(ctx, backend, name, resourcepb.WatchEvent_ADDED, WithNamespace(ns), WithGroup(group), WithResource(resourceName))
|
||||
require.Greater(t, origRv, int64(0))
|
||||
|
||||
const events = 500
|
||||
prevRv := origRv
|
||||
for i := 0; i < events; i++ {
|
||||
rv, err := writeEvent(ctx, backend, name, resource.WatchEvent_MODIFIED, WithNamespace(ns), WithGroup(group), WithResource(resourceName))
|
||||
rv, err := writeEvent(ctx, backend, name, resourcepb.WatchEvent_MODIFIED, WithNamespace(ns), WithGroup(group), WithResource(resourceName))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv, prevRv)
|
||||
prevRv = rv
|
||||
}
|
||||
t.Log("added events in ", time.Since(start))
|
||||
|
||||
req := &resource.ListRequest{
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: 2 * events,
|
||||
Source: resource.ListRequest_HISTORY,
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
ResourceVersion: origRv,
|
||||
VersionMatchV2: resource.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resource.ListOptions{
|
||||
Key: &resource.ResourceKey{
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: group,
|
||||
Resource: resourceName,
|
||||
@@ -903,16 +904,15 @@ func runTestIntegrationBlobSupport(t *testing.T, backend resource.StorageBackend
|
||||
ns := nsPrefix + "-ns1"
|
||||
|
||||
t.Run("put and fetch blob", func(t *testing.T) {
|
||||
key := &resource.ResourceKey{
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "g",
|
||||
Resource: "r",
|
||||
Name: "n",
|
||||
}
|
||||
|
||||
b1, err := server.PutBlob(ctx, &resource.PutBlobRequest{
|
||||
b1, err := server.PutBlob(ctx, &resourcepb.PutBlobRequest{
|
||||
Resource: key,
|
||||
Method: resource.PutBlobRequest_GRPC,
|
||||
Method: resourcepb.PutBlobRequest_GRPC,
|
||||
ContentType: "plain/text",
|
||||
Value: []byte("hello 11111"),
|
||||
})
|
||||
@@ -920,9 +920,9 @@ func runTestIntegrationBlobSupport(t *testing.T, backend resource.StorageBackend
|
||||
require.Nil(t, b1.Error)
|
||||
require.Equal(t, "c894ae57bd227b8f8c63f38a2ddf458b", b1.Hash)
|
||||
|
||||
b2, err := server.PutBlob(ctx, &resource.PutBlobRequest{
|
||||
b2, err := server.PutBlob(ctx, &resourcepb.PutBlobRequest{
|
||||
Resource: key,
|
||||
Method: resource.PutBlobRequest_GRPC,
|
||||
Method: resourcepb.PutBlobRequest_GRPC,
|
||||
ContentType: "plain/text",
|
||||
Value: []byte("hello 22222"), // the most recent
|
||||
})
|
||||
@@ -950,19 +950,19 @@ func runTestIntegrationBlobSupport(t *testing.T, backend resource.StorageBackend
|
||||
obj.SetKind("Test")
|
||||
val, err := obj.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
out, err := server.Create(ctx, &resource.CreateRequest{Key: key, Value: val})
|
||||
out, err := server.Create(ctx, &resourcepb.CreateRequest{Key: key, Value: val})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, out.Error)
|
||||
require.True(t, out.ResourceVersion > 0)
|
||||
|
||||
// The server (not store!) will lookup the saved annotation and return the correct payload
|
||||
res, err := server.GetBlob(ctx, &resource.GetBlobRequest{Resource: key})
|
||||
res, err := server.GetBlob(ctx, &resourcepb.GetBlobRequest{Resource: key})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, out.Error)
|
||||
require.Contains(t, string(res.Value), "hello 22222")
|
||||
|
||||
// But we can still get an older version with an explicit UID
|
||||
res, err = server.GetBlob(ctx, &resource.GetBlobRequest{Resource: key, Uid: b1.Uid})
|
||||
res, err = server.GetBlob(ctx, &resourcepb.GetBlobRequest{Resource: key, Uid: b1.Uid})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, out.Error)
|
||||
require.Contains(t, string(res.Value), "hello 11111")
|
||||
@@ -981,8 +981,8 @@ func runTestIntegrationBackendCreateNewResource(t *testing.T, backend resource.S
|
||||
ns := nsPrefix + "-create-resource"
|
||||
ctx = request.WithNamespace(ctx, ns)
|
||||
|
||||
request := &resource.CreateRequest{
|
||||
Key: &resource.ResourceKey{
|
||||
request := &resourcepb.CreateRequest{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "test.grafana",
|
||||
Resource: "Test",
|
||||
@@ -1064,7 +1064,7 @@ type WriteEventOptions struct {
|
||||
Value []byte
|
||||
}
|
||||
|
||||
func writeEvent(ctx context.Context, store resource.StorageBackend, name string, action resource.WatchEvent_Type, opts ...WriteEventOption) (int64, error) {
|
||||
func writeEvent(ctx context.Context, store resource.StorageBackend, name string, action resourcepb.WatchEvent_Type, opts ...WriteEventOption) (int64, error) {
|
||||
// Default options
|
||||
options := WriteEventOptions{
|
||||
Namespace: "namespace",
|
||||
@@ -1087,7 +1087,7 @@ func writeEvent(ctx context.Context, store resource.StorageBackend, name string,
|
||||
"namespace": options.Namespace,
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"value": name + " " + resource.WatchEvent_Type_name[int32(action)],
|
||||
"value": name + " " + resourcepb.WatchEvent_Type_name[int32(action)],
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1106,7 +1106,7 @@ func writeEvent(ctx context.Context, store resource.StorageBackend, name string,
|
||||
return store.WriteEvent(ctx, resource.WriteEvent{
|
||||
Type: action,
|
||||
Value: options.Value,
|
||||
Key: &resource.ResourceKey{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: options.Namespace,
|
||||
Group: options.Group,
|
||||
Resource: options.Resource,
|
||||
|
||||
Reference in New Issue
Block a user