mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Storage: Avoid relying on RequestInfo (#89635)
This commit is contained in:
parent
4cf3ebbb3d
commit
71270f3203
132
pkg/apiserver/registry/generic/key.go
Normal file
132
pkg/apiserver/registry/generic/key.go
Normal file
@ -0,0 +1,132 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/validation/path"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
Group string
|
||||
Resource string
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
func ParseKey(key string) (*Key, error) {
|
||||
// /<group>/<resource>[/namespaces/<namespace>][/<name>]
|
||||
parts := strings.Split(key, "/")
|
||||
if len(parts) < 3 {
|
||||
return nil, fmt.Errorf("invalid key (expecting at least 2 parts): %s", key)
|
||||
}
|
||||
|
||||
if parts[0] != "" {
|
||||
return nil, fmt.Errorf("invalid key (expecting leading slash): %s", key)
|
||||
}
|
||||
|
||||
k := &Key{
|
||||
Group: parts[1],
|
||||
Resource: parts[2],
|
||||
}
|
||||
|
||||
if len(parts) == 3 {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
if parts[3] != "namespaces" {
|
||||
k.Name = parts[3]
|
||||
return k, nil
|
||||
}
|
||||
|
||||
if len(parts) < 5 {
|
||||
return nil, fmt.Errorf("invalid key (expecting namespace after 'namespaces'): %s", key)
|
||||
}
|
||||
|
||||
k.Namespace = parts[4]
|
||||
|
||||
if len(parts) == 5 {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
k.Name = parts[5]
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
func (k *Key) String() string {
|
||||
s := "/" + k.Group + "/" + k.Resource
|
||||
if len(k.Namespace) > 0 {
|
||||
s += "/namespaces/" + k.Namespace
|
||||
}
|
||||
if len(k.Name) > 0 {
|
||||
s += "/" + k.Name
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (k *Key) IsEqual(other *Key) bool {
|
||||
return k.Group == other.Group &&
|
||||
k.Resource == other.Resource &&
|
||||
k.Namespace == other.Namespace &&
|
||||
k.Name == other.Name
|
||||
}
|
||||
|
||||
// KeyRootFunc is used by the generic registry store to construct the first portion of the storage key.
|
||||
func KeyRootFunc(gr schema.GroupResource) func(ctx context.Context) string {
|
||||
return func(ctx context.Context) string {
|
||||
ns, _ := genericapirequest.NamespaceFrom(ctx)
|
||||
key := &Key{
|
||||
Group: gr.Group,
|
||||
Resource: gr.Resource,
|
||||
Namespace: ns,
|
||||
}
|
||||
return key.String()
|
||||
}
|
||||
}
|
||||
|
||||
// NamespaceKeyFunc is the default function for constructing storage paths to
|
||||
// a resource relative to the given prefix enforcing namespace rules. If the
|
||||
// context does not contain a namespace, it errors.
|
||||
func NamespaceKeyFunc(gr schema.GroupResource) func(ctx context.Context, name string) (string, error) {
|
||||
return func(ctx context.Context, name string) (string, error) {
|
||||
ns, ok := genericapirequest.NamespaceFrom(ctx)
|
||||
if !ok || len(ns) == 0 {
|
||||
return "", apierrors.NewBadRequest("Namespace parameter required.")
|
||||
}
|
||||
if len(name) == 0 {
|
||||
return "", apierrors.NewBadRequest("Name parameter required.")
|
||||
}
|
||||
if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
|
||||
return "", apierrors.NewBadRequest(fmt.Sprintf("Name parameter invalid: %q: %s", name, strings.Join(msgs, ";")))
|
||||
}
|
||||
key := &Key{
|
||||
Group: gr.Group,
|
||||
Resource: gr.Resource,
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
}
|
||||
return key.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoNamespaceKeyFunc is the default function for constructing storage paths
|
||||
// to a resource relative to the given prefix without a namespace.
|
||||
func NoNamespaceKeyFunc(ctx context.Context, prefix string, gr schema.GroupResource, name string) (string, error) {
|
||||
if len(name) == 0 {
|
||||
return "", apierrors.NewBadRequest("Name parameter required.")
|
||||
}
|
||||
if msgs := path.IsValidPathSegmentName(name); len(msgs) != 0 {
|
||||
return "", apierrors.NewBadRequest(fmt.Sprintf("Name parameter invalid: %q: %s", name, strings.Join(msgs, ";")))
|
||||
}
|
||||
key := &Key{
|
||||
Group: gr.Group,
|
||||
Resource: gr.Resource,
|
||||
Name: name,
|
||||
}
|
||||
return prefix + key.String(), nil
|
||||
}
|
@ -61,6 +61,8 @@ func NewStorage(
|
||||
s := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -26,6 +26,8 @@ func newStorage(scheme *runtime.Scheme) (*storage, error) {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -23,6 +23,8 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resource.NewFunc,
|
||||
NewListFunc: resource.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resource.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -28,6 +28,8 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -24,6 +24,8 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, le
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resource.NewFunc,
|
||||
NewListFunc: resource.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resource.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -31,6 +31,8 @@ func newScopeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGette
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
@ -73,6 +75,8 @@ func newScopeDashboardBindingStorage(scheme *runtime.Scheme, optsGetter generic.
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
@ -115,6 +119,8 @@ func newScopeNodeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsG
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -28,6 +28,8 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
|
@ -23,12 +23,12 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
entityStore "github.com/grafana/grafana/pkg/services/store/entity"
|
||||
)
|
||||
|
||||
@ -74,16 +74,16 @@ func NewStorage(
|
||||
// in seconds (0 means forever). If no error is returned and out is not nil, out will be
|
||||
// set to the read value from database.
|
||||
func (s *Storage) Create(ctx context.Context, key string, obj runtime.Object, out runtime.Object, ttl uint64) error {
|
||||
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return apierrors.NewInternalError(fmt.Errorf("could not get request info"))
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Versioner().PrepareObjectForStorage(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e, err := resourceToEntity(obj, requestInfo, s.codec)
|
||||
e, err := resourceToEntity(obj, *k, s.codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -114,17 +114,9 @@ func (s *Storage) Create(ctx context.Context, key string, obj runtime.Object, ou
|
||||
// current version of the object to avoid read operation from storage to get it.
|
||||
// However, the implementations have to retry in case suggestion is stale.
|
||||
func (s *Storage) Delete(ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions, validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error {
|
||||
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return apierrors.NewInternalError(fmt.Errorf("could not get request info"))
|
||||
}
|
||||
|
||||
k := &entityStore.Key{
|
||||
Group: requestInfo.APIGroup,
|
||||
Resource: requestInfo.Resource,
|
||||
Namespace: requestInfo.Namespace,
|
||||
Name: requestInfo.Name,
|
||||
Subresource: requestInfo.Subresource,
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
previousVersion := int64(0)
|
||||
@ -156,17 +148,9 @@ func (s *Storage) Delete(ctx context.Context, key string, out runtime.Object, pr
|
||||
// If resource version is "0", this interface will get current object at given key
|
||||
// and send it in an "ADDED" event, before watch starts.
|
||||
func (s *Storage) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) {
|
||||
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return nil, apierrors.NewInternalError(fmt.Errorf("could not get request info"))
|
||||
}
|
||||
|
||||
k := &entityStore.Key{
|
||||
Group: requestInfo.APIGroup,
|
||||
Resource: requestInfo.Resource,
|
||||
Namespace: requestInfo.Namespace,
|
||||
Name: requestInfo.Name,
|
||||
Subresource: requestInfo.Subresource,
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Predicate.Field != nil {
|
||||
@ -288,21 +272,12 @@ func (s *Storage) Watch(ctx context.Context, key string, opts storage.ListOption
|
||||
// The returned contents may be delayed, but it is guaranteed that they will
|
||||
// match 'opts.ResourceVersion' according 'opts.ResourceVersionMatch'.
|
||||
func (s *Storage) Get(ctx context.Context, key string, opts storage.GetOptions, objPtr runtime.Object) error {
|
||||
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return apierrors.NewInternalError(fmt.Errorf("could not get request info"))
|
||||
}
|
||||
|
||||
k := &entityStore.Key{
|
||||
Group: requestInfo.APIGroup,
|
||||
Resource: requestInfo.Resource,
|
||||
Namespace: requestInfo.Namespace,
|
||||
Name: requestInfo.Name,
|
||||
Subresource: requestInfo.Subresource,
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resourceVersion := int64(0)
|
||||
var err error
|
||||
if opts.ResourceVersion != "" {
|
||||
resourceVersion, err = strconv.ParseInt(opts.ResourceVersion, 10, 64)
|
||||
if err != nil {
|
||||
@ -343,17 +318,9 @@ func (s *Storage) Get(ctx context.Context, key string, opts storage.GetOptions,
|
||||
// The returned contents may be delayed, but it is guaranteed that they will
|
||||
// match 'opts.ResourceVersion' according 'opts.ResourceVersionMatch'.
|
||||
func (s *Storage) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error {
|
||||
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return apierrors.NewInternalError(fmt.Errorf("could not get request info"))
|
||||
}
|
||||
|
||||
k := &entityStore.Key{
|
||||
Group: requestInfo.APIGroup,
|
||||
Resource: requestInfo.Resource,
|
||||
Namespace: requestInfo.Namespace,
|
||||
Name: requestInfo.Name,
|
||||
Subresource: requestInfo.Subresource,
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listPtr, err := meta.GetItemsPtr(listObj)
|
||||
@ -519,17 +486,9 @@ func (s *Storage) GuaranteedUpdate(
|
||||
tryUpdate storage.UpdateFunc,
|
||||
cachedExistingObject runtime.Object,
|
||||
) error {
|
||||
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return apierrors.NewInternalError(fmt.Errorf("could not get request info"))
|
||||
}
|
||||
|
||||
k := &entityStore.Key{
|
||||
Group: requestInfo.APIGroup,
|
||||
Resource: requestInfo.Resource,
|
||||
Namespace: requestInfo.Namespace,
|
||||
Name: requestInfo.Name,
|
||||
Subresource: requestInfo.Subresource,
|
||||
k, err := grafanaregistry.ParseKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
getErr := s.Get(ctx, k.String(), storage.GetOptions{}, destination)
|
||||
@ -565,7 +524,7 @@ func (s *Storage) GuaranteedUpdate(
|
||||
return apierrors.NewInternalError(fmt.Errorf("could not successfully update object. key=%s, err=%s", k.String(), err.Error()))
|
||||
}
|
||||
|
||||
e, err := resourceToEntity(updatedObj, requestInfo, s.codec)
|
||||
e, err := resourceToEntity(updatedObj, *k, s.codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,166 +0,0 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
var _ storage.Interface = &RequestInfoWrapper{}
|
||||
|
||||
type RequestInfoWrapper struct {
|
||||
store storage.Interface
|
||||
gr schema.GroupResource
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) setRequestInfo(ctx context.Context, key string) (context.Context, error) {
|
||||
pkey, err := convertToParsedKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx = appcontext.WithUser(ctx, &user.SignedInUser{
|
||||
Login: "admin",
|
||||
UserID: 1,
|
||||
OrgID: 1,
|
||||
})
|
||||
|
||||
return request.WithRequestInfo(ctx, &request.RequestInfo{
|
||||
APIGroup: pkey.Group,
|
||||
APIVersion: "v1",
|
||||
Resource: pkey.Resource,
|
||||
Subresource: "",
|
||||
Namespace: pkey.Namespace,
|
||||
Name: pkey.Name,
|
||||
Parts: strings.Split(key, "/"),
|
||||
IsResourceRequest: true,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) Create(ctx context.Context, key string, obj runtime.Object, out runtime.Object, ttl uint64) error {
|
||||
ctx, err := r.setRequestInfo(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.store.Create(ctx, key, obj, out, ttl)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) Delete(ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions, validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error {
|
||||
ctx, err := r.setRequestInfo(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.store.Delete(ctx, key, out, preconditions, validateDeletion, cachedExistingObject)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) {
|
||||
ctx, err := r.setRequestInfo(ctx, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.store.Watch(ctx, key, opts)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) Get(ctx context.Context, key string, opts storage.GetOptions, objPtr runtime.Object) error {
|
||||
ctx, err := r.setRequestInfo(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.store.Get(ctx, key, opts, objPtr)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error {
|
||||
ctx, err := r.setRequestInfo(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.store.GetList(ctx, key, opts, listObj)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) GuaranteedUpdate(ctx context.Context, key string, destination runtime.Object, ignoreNotFound bool, preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, cachedExistingObject runtime.Object) error {
|
||||
ctx, err := r.setRequestInfo(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.store.GuaranteedUpdate(ctx, key, destination, ignoreNotFound, preconditions, tryUpdate, cachedExistingObject)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) Count(key string) (int64, error) {
|
||||
return r.store.Count(key)
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) Versioner() storage.Versioner {
|
||||
return r.store.Versioner()
|
||||
}
|
||||
|
||||
func (r *RequestInfoWrapper) RequestWatchProgress(ctx context.Context) error {
|
||||
return r.store.RequestWatchProgress(ctx)
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
Group string
|
||||
Resource string
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
func convertToParsedKey(key string) (*Key, error) {
|
||||
// NOTE: the following supports the watcher tests that run against v1/pods
|
||||
// Other than that, there are ambiguities in the key format that only field selector
|
||||
// when set to use metadata.name can be used to bring clarity in the 3-segment case
|
||||
|
||||
// Cases handled below:
|
||||
// namespace scoped:
|
||||
// /<resource>/[<namespace>]/[<name>]
|
||||
// /<resource>/[<namespace>]
|
||||
//
|
||||
// cluster scoped:
|
||||
// /<resource>/[<name>]
|
||||
// /<resource>
|
||||
k := &Key{}
|
||||
|
||||
if !strings.HasPrefix(key, "/") {
|
||||
key = "/" + key
|
||||
}
|
||||
|
||||
parts := strings.SplitN(key, "/", 5)
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf("invalid key format: %s", key)
|
||||
}
|
||||
|
||||
k.Resource = parts[1]
|
||||
if len(parts) < 3 {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// figure out whether the key is namespace scoped or cluster scoped
|
||||
if isTestNs(parts[2]) {
|
||||
k.Namespace = parts[2]
|
||||
if len(parts) >= 4 {
|
||||
k.Name = parts[3]
|
||||
}
|
||||
} else {
|
||||
k.Name = parts[2]
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
func isTestNs(part string) bool {
|
||||
return strings.HasPrefix(part, "test-ns-") || strings.HasPrefix(part, "ns-") || strings.Index(part, "-ns") > 0
|
||||
}
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/apitesting"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@ -20,11 +21,13 @@ import (
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
|
||||
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/storage/entity"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
@ -129,7 +132,12 @@ func testSetup(t *testing.T, opts ...setupOption) (context.Context, storage.Inte
|
||||
client,
|
||||
setupOpts.codec,
|
||||
func(obj runtime.Object) (string, error) {
|
||||
return storage.NamespaceKeyFunc(setupOpts.resourcePrefix, obj)
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
keyFn := grafanaregistry.NamespaceKeyFunc(setupOpts.groupResource)
|
||||
return keyFn(genericapirequest.WithNamespace(genericapirequest.NewContext(), accessor.GetNamespace()), accessor.GetName())
|
||||
},
|
||||
setupOpts.newFunc,
|
||||
setupOpts.newListFunc,
|
||||
@ -141,12 +149,7 @@ func testSetup(t *testing.T, opts ...setupOption) (context.Context, storage.Inte
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
wrappedStore := &RequestInfoWrapper{
|
||||
store: store,
|
||||
gr: setupOpts.groupResource,
|
||||
}
|
||||
|
||||
return ctx, wrappedStore, destroyFunc, nil
|
||||
return ctx, store, destroyFunc, nil
|
||||
}
|
||||
|
||||
func TestIntegrationWatch(t *testing.T) {
|
||||
@ -250,6 +253,7 @@ func TestIntegrationWatchContextCancel(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
t.Skip("In maintenance")
|
||||
|
||||
ctx, store, destroyFunc, err := testSetup(t)
|
||||
defer destroyFunc()
|
||||
@ -323,6 +327,7 @@ func TestIntegrationSendInitialEventsBackwardCompatibility(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
t.Skip("In maintenance")
|
||||
|
||||
ctx, store, destroyFunc, err := testSetup(t)
|
||||
defer destroyFunc()
|
||||
|
@ -13,9 +13,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
entityStore "github.com/grafana/grafana/pkg/services/store/entity"
|
||||
)
|
||||
|
||||
@ -98,7 +98,7 @@ func EntityToRuntimeObject(rsp *entityStore.Entity, res runtime.Object, codec ru
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceToEntity(res runtime.Object, requestInfo *request.RequestInfo, codec runtime.Codec) (*entityStore.Entity, error) {
|
||||
func resourceToEntity(res runtime.Object, k grafanaregistry.Key, codec runtime.Codec) (*entityStore.Entity, error) {
|
||||
metaAccessor, err := meta.Accessor(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -110,19 +110,13 @@ func resourceToEntity(res runtime.Object, requestInfo *request.RequestInfo, code
|
||||
}
|
||||
rv, _ := strconv.ParseInt(metaAccessor.GetResourceVersion(), 10, 64)
|
||||
|
||||
k := &entityStore.Key{
|
||||
Group: requestInfo.APIGroup,
|
||||
Resource: requestInfo.Resource,
|
||||
Namespace: requestInfo.Namespace,
|
||||
Name: metaAccessor.GetName(),
|
||||
Subresource: requestInfo.Subresource,
|
||||
}
|
||||
// add the object's name to the provided key
|
||||
k.Name = metaAccessor.GetName()
|
||||
|
||||
rsp := &entityStore.Entity{
|
||||
Group: k.Group,
|
||||
GroupVersion: requestInfo.APIVersion,
|
||||
GroupVersion: res.GetObjectKind().GroupVersionKind().Version,
|
||||
Resource: k.Resource,
|
||||
Subresource: k.Subresource,
|
||||
Namespace: k.Namespace,
|
||||
Key: k.String(),
|
||||
Name: k.Name,
|
||||
|
@ -10,9 +10,9 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
entityStore "github.com/grafana/grafana/pkg/services/store/entity"
|
||||
)
|
||||
|
||||
@ -30,7 +30,7 @@ func TestResourceToEntity(t *testing.T) {
|
||||
Codecs := serializer.NewCodecFactory(Scheme)
|
||||
|
||||
testCases := []struct {
|
||||
requestInfo *request.RequestInfo
|
||||
key grafanaregistry.Key
|
||||
resource runtime.Object
|
||||
codec runtime.Codec
|
||||
expectedKey string
|
||||
@ -52,14 +52,17 @@ func TestResourceToEntity(t *testing.T) {
|
||||
expectedBody []byte
|
||||
}{
|
||||
{
|
||||
requestInfo: &request.RequestInfo{
|
||||
APIGroup: "playlist.grafana.app",
|
||||
APIVersion: "v0alpha1",
|
||||
Resource: "playlists",
|
||||
Namespace: "default",
|
||||
Name: "test-name",
|
||||
key: grafanaregistry.Key{
|
||||
Group: "playlist.grafana.app",
|
||||
Resource: "playlists",
|
||||
Namespace: "default",
|
||||
Name: "test-name",
|
||||
},
|
||||
resource: &v0alpha1.Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "playlist.grafana.app/v0alpha1",
|
||||
Kind: "Playlist",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: createdAt,
|
||||
Labels: map[string]string{"label1": "value1", "label2": "value2"},
|
||||
@ -105,7 +108,7 @@ func TestResourceToEntity(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.resource.GetObjectKind().GroupVersionKind().Kind+" to entity conversion should succeed", func(t *testing.T) {
|
||||
entity, err := resourceToEntity(tc.resource, tc.requestInfo, Codecs.LegacyCodec(v0alpha1.PlaylistResourceInfo.GroupVersion()))
|
||||
entity, err := resourceToEntity(tc.resource, tc.key, Codecs.LegacyCodec(v0alpha1.PlaylistResourceInfo.GroupVersion()))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedKey, entity.Key)
|
||||
assert.Equal(t, tc.expectedName, entity.Name)
|
||||
|
@ -1,82 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
Group string
|
||||
Resource string
|
||||
Namespace string
|
||||
Name string
|
||||
Subresource string
|
||||
}
|
||||
|
||||
func ParseKey(key string) (*Key, error) {
|
||||
// /<group>/<resource>[/namespaces/<namespace>][/<name>[/<subresource>]]
|
||||
parts := strings.Split(key, "/")
|
||||
if len(parts) < 3 {
|
||||
return nil, fmt.Errorf("invalid key (expecting at least 2 parts): %s", key)
|
||||
}
|
||||
|
||||
if parts[0] != "" {
|
||||
return nil, fmt.Errorf("invalid key (expecting leading slash): %s", key)
|
||||
}
|
||||
|
||||
k := &Key{
|
||||
Group: parts[1],
|
||||
Resource: parts[2],
|
||||
}
|
||||
|
||||
if len(parts) == 3 {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
if parts[3] != "namespaces" {
|
||||
k.Name = parts[3]
|
||||
if len(parts) > 4 {
|
||||
k.Subresource = strings.Join(parts[4:], "/")
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
if len(parts) < 5 {
|
||||
return nil, fmt.Errorf("invalid key (expecting namespace after 'namespaces'): %s", key)
|
||||
}
|
||||
|
||||
k.Namespace = parts[4]
|
||||
|
||||
if len(parts) == 5 {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
k.Name = parts[5]
|
||||
if len(parts) > 6 {
|
||||
k.Subresource = strings.Join(parts[6:], "/")
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
func (k *Key) String() string {
|
||||
s := "/" + k.Group + "/" + k.Resource
|
||||
if len(k.Namespace) > 0 {
|
||||
s += "/namespaces/" + k.Namespace
|
||||
}
|
||||
if len(k.Name) > 0 {
|
||||
s += "/" + k.Name
|
||||
if len(k.Subresource) > 0 {
|
||||
s += "/" + k.Subresource
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (k *Key) IsEqual(other *Key) bool {
|
||||
return k.Group == other.Group &&
|
||||
k.Resource == other.Resource &&
|
||||
k.Namespace == other.Namespace &&
|
||||
k.Name == other.Name &&
|
||||
k.Subresource == other.Subresource
|
||||
}
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
|
||||
folder "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate"
|
||||
@ -21,7 +22,7 @@ func (s *sqlEntityServer) Create(ctx context.Context, r *entity.CreateEntityRequ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := entity.ParseKey(r.Entity.Key)
|
||||
key, err := grafanaregistry.ParseKey(r.Entity.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create entity: parse entity key: %w", err)
|
||||
}
|
||||
@ -98,7 +99,7 @@ func (s *sqlEntityServer) Create(ctx context.Context, r *entity.CreateEntityRequ
|
||||
|
||||
// entityForCreate validates the given request and returns a *returnsEntity
|
||||
// populated accordingly.
|
||||
func entityForCreate(ctx context.Context, r *entity.CreateEntityRequest, key *entity.Key) (*returnsEntity, error) {
|
||||
func entityForCreate(ctx context.Context, r *entity.CreateEntityRequest, key *grafanaregistry.Key) (*returnsEntity, error) {
|
||||
newEntity := &returnsEntity{
|
||||
Entity: cloneEntity(r.Entity),
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
folder "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate"
|
||||
@ -20,7 +21,7 @@ func (s *sqlEntityServer) Delete(ctx context.Context, r *entity.DeleteEntityRequ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := entity.ParseKey(r.Key)
|
||||
key, err := grafanaregistry.ParseKey(r.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("delete entity: parse entity key: %w", err)
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate"
|
||||
@ -218,7 +219,7 @@ func (r sqlEntityListFolderElementsRequest) Validate() error {
|
||||
// cases and proper database deserialization.
|
||||
type sqlEntityReadRequest struct {
|
||||
*sqltemplate.SQLTemplate
|
||||
Key *entity.Key
|
||||
Key *grafanaregistry.Key
|
||||
ResourceVersion int64
|
||||
SelectForUpdate bool
|
||||
returnsEntitySet
|
||||
@ -230,7 +231,7 @@ func (r sqlEntityReadRequest) Validate() error {
|
||||
|
||||
type sqlEntityDeleteRequest struct {
|
||||
*sqltemplate.SQLTemplate
|
||||
Key *entity.Key
|
||||
Key *grafanaregistry.Key
|
||||
}
|
||||
|
||||
func (r sqlEntityDeleteRequest) Validate() error {
|
||||
@ -479,7 +480,7 @@ func readEntity(
|
||||
ctx context.Context,
|
||||
x db.ContextExecer,
|
||||
d sqltemplate.Dialect,
|
||||
k *entity.Key,
|
||||
k *grafanaregistry.Key,
|
||||
asOfVersion int64,
|
||||
optimisticLocking bool,
|
||||
selectForUpdate bool,
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
sqlmock "github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate"
|
||||
@ -107,7 +108,7 @@ func TestQueries(t *testing.T) {
|
||||
Name: "single path",
|
||||
Data: &sqlEntityDeleteRequest{
|
||||
SQLTemplate: new(sqltemplate.SQLTemplate),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
},
|
||||
Expected: expected{
|
||||
"entity_delete_mysql_sqlite.sql": dialects{
|
||||
@ -173,7 +174,7 @@ func TestQueries(t *testing.T) {
|
||||
Name: "with resource version and select for update",
|
||||
Data: &sqlEntityReadRequest{
|
||||
SQLTemplate: new(sqltemplate.SQLTemplate),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
ResourceVersion: 1,
|
||||
SelectForUpdate: true,
|
||||
returnsEntitySet: returnsEntitySet{
|
||||
@ -190,7 +191,7 @@ func TestQueries(t *testing.T) {
|
||||
Name: "without resource version and select for update",
|
||||
Data: &sqlEntityReadRequest{
|
||||
SQLTemplate: new(sqltemplate.SQLTemplate),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
returnsEntitySet: returnsEntitySet{
|
||||
Entity: newReturnsEntity(),
|
||||
},
|
||||
@ -547,7 +548,7 @@ func TestReadEntity(t *testing.T) {
|
||||
// readonly, shared data for all subtests
|
||||
expectedEntity := newEmptyEntity()
|
||||
testdataJSON(t, `grpc-res-entity.json`, expectedEntity)
|
||||
key, err := entity.ParseKey(expectedEntity.Key)
|
||||
key, err := grafanaregistry.ParseKey(expectedEntity.Key)
|
||||
require.NoErrorf(t, err, "provided key: %#v", expectedEntity)
|
||||
|
||||
t.Run("happy path - entity table, optimistic locking", func(t *testing.T) {
|
||||
@ -567,7 +568,7 @@ func TestReadEntity(t *testing.T) {
|
||||
db, mock := newMockDBMatchWords(t)
|
||||
readReq := sqlEntityReadRequest{ // used to generate mock results
|
||||
SQLTemplate: sqltemplate.New(sqltemplate.MySQL),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
returnsEntitySet: newReturnsEntitySet(),
|
||||
}
|
||||
readReq.Entity.Entity = cloneEntity(expectedEntity)
|
||||
@ -592,7 +593,7 @@ func TestReadEntity(t *testing.T) {
|
||||
db, mock := newMockDBMatchWords(t)
|
||||
readReq := sqlEntityReadRequest{ // used to generate mock results
|
||||
SQLTemplate: sqltemplate.New(sqltemplate.MySQL),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
returnsEntitySet: newReturnsEntitySet(),
|
||||
}
|
||||
readReq.Entity.Entity = cloneEntity(expectedEntity)
|
||||
@ -627,7 +628,7 @@ func TestReadEntity(t *testing.T) {
|
||||
db, mock := newMockDBMatchWords(t)
|
||||
readReq := sqlEntityReadRequest{ // used to generate mock results
|
||||
SQLTemplate: sqltemplate.New(sqltemplate.MySQL),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
returnsEntitySet: newReturnsEntitySet(),
|
||||
}
|
||||
results := newMockResults(t, mock, sqlEntityRead, readReq)
|
||||
@ -652,7 +653,7 @@ func TestReadEntity(t *testing.T) {
|
||||
db, mock := newMockDBMatchWords(t)
|
||||
readReq := sqlEntityReadRequest{ // used to generate mock results
|
||||
SQLTemplate: sqltemplate.New(sqltemplate.MySQL),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
returnsEntitySet: newReturnsEntitySet(),
|
||||
}
|
||||
readReq.Entity.Entity = cloneEntity(expectedEntity)
|
||||
@ -684,7 +685,7 @@ func expectReadEntity(t *testing.T, mock sqlmock.Sqlmock, e *entity.Entity) func
|
||||
// test declarations
|
||||
readReq := sqlEntityReadRequest{ // used to generate mock results
|
||||
SQLTemplate: sqltemplate.New(sqltemplate.MySQL),
|
||||
Key: new(entity.Key),
|
||||
Key: new(grafanaregistry.Key),
|
||||
returnsEntitySet: newReturnsEntitySet(),
|
||||
}
|
||||
results := newMockResults(t, mock, sqlEntityRead, readReq)
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
@ -302,7 +303,7 @@ func (s *sqlEntityServer) read(ctx context.Context, tx session.SessionQuerier, r
|
||||
return nil, fmt.Errorf("missing key")
|
||||
}
|
||||
|
||||
key, err := entity.ParseKey(r.Key)
|
||||
key, err := grafanaregistry.ParseKey(r.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -395,7 +396,7 @@ func (s *sqlEntityServer) history(ctx context.Context, r *entity.EntityHistoryRe
|
||||
entityQuery.AddFields(fields...)
|
||||
|
||||
if r.Key != "" {
|
||||
key, err := entity.ParseKey(r.Key)
|
||||
key, err := grafanaregistry.ParseKey(r.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -629,7 +630,7 @@ func (s *sqlEntityServer) List(ctx context.Context, r *entity.EntityListRequest)
|
||||
where := []string{}
|
||||
args := []any{}
|
||||
for _, k := range r.Key {
|
||||
key, err := entity.ParseKey(k)
|
||||
key, err := grafanaregistry.ParseKey(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -868,7 +869,7 @@ func (s *sqlEntityServer) watchInit(ctx context.Context, r *entity.EntityWatchRe
|
||||
where := []string{}
|
||||
args := []any{}
|
||||
for _, k := range r.Key {
|
||||
key, err := entity.ParseKey(k)
|
||||
key, err := grafanaregistry.ParseKey(k)
|
||||
if err != nil {
|
||||
ctxLogger.Error("error parsing key", "error", err, "key", k)
|
||||
return lastRv, err
|
||||
@ -1153,7 +1154,7 @@ func watchMatches(r *entity.EntityWatchRequest, result *entity.Entity) bool {
|
||||
if len(r.Key) > 0 {
|
||||
matched := false
|
||||
for _, k := range r.Key {
|
||||
key, err := entity.ParseKey(k)
|
||||
key, err := grafanaregistry.ParseKey(k)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
folder "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/db"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate"
|
||||
@ -21,7 +22,7 @@ func (s *sqlEntityServer) Update(ctx context.Context, r *entity.UpdateEntityRequ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := entity.ParseKey(r.Entity.Key)
|
||||
key, err := grafanaregistry.ParseKey(r.Entity.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update entity: parse entity key: %w", err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user