K8s/Storage: Register field-selector on all kinds (#79822)

Co-authored-by: Dan Cech <dcech@grafana.com>
pull/80656/head
Ryan McKinley 1 year ago committed by GitHub
parent 73715a1de9
commit e1d387d826
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      pkg/registry/apis/folders/legacy_storage.go
  2. 23
      pkg/registry/apis/playlist/register.go
  3. 6
      pkg/services/grafana-apiserver/service.go
  4. 76
      pkg/services/grafana-apiserver/storage/entity/fieldRequirements.go
  5. 27
      pkg/services/grafana-apiserver/storage/entity/storage.go

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/grafana-apiserver/storage/entity"
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils"
"github.com/grafana/grafana/pkg/util"
)
@ -65,6 +66,17 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
return nil, err
}
parentUID := ""
fieldRequirements, fieldSelector, err := entity.ReadFieldRequirements(options.FieldSelector)
if err != nil {
return nil, err
}
if fieldRequirements.Folder != nil {
parentUID = *fieldRequirements.Folder
}
// Update the field selector to remove the unneeded selectors
options.FieldSelector = fieldSelector
paging, err := readContinueToken(options)
if err != nil {
return nil, err
@ -77,6 +89,7 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
// When nested folders are not enabled, all folders are root folders
hits, err := s.service.GetChildren(ctx, &folder.GetChildrenQuery{
UID: parentUID, // NOTE! we should do a different query when nested folders are enabled!
SignedInUser: user,
Limit: paging.page,
OrgID: orgId,

@ -2,7 +2,6 @@ package playlist
import (
"fmt"
"strings"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -68,28 +67,6 @@ func (b *PlaylistAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
Version: runtime.APIVersionInternal,
})
gvk := playlist.PlaylistResourceInfo.GroupVersionKind()
// Add playlist thing
_ = scheme.AddFieldLabelConversionFunc(gvk,
runtime.FieldLabelConversionFunc(
func(label, value string) (string, string, error) {
if strings.HasPrefix(label, "grafana.app/") {
return label, value, nil
}
switch label {
case "metadata.name":
return label, value, nil
case "metadata.namespace":
return label, value, nil
default:
return "", "", fmt.Errorf("%q is not a known field selector: only %q, %q", label, "metadata.name", "metadata.namespace")
}
},
),
)
// If multiple versions exist, then register conversions from zz_generated.conversion.go
// if err := playlist.RegisterConversions(scheme); err != nil {
// return err

@ -324,6 +324,12 @@ func (s *service) start(ctx context.Context) error {
return err
}
// support folder selection
err = entitystorage.RegisterFieldSelectorSupport(Scheme)
if err != nil {
return err
}
// Create the server
server, err := serverConfig.Complete().New("grafana-apiserver", genericapiserver.NewEmptyDelegate())
if err != nil {

@ -0,0 +1,76 @@
package entity
import (
"fmt"
"strings"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/selection"
)
const folderAnnoKey = "grafana.app/folder"
type FieldRequirements struct {
// Equals folder
Folder *string
}
func ReadFieldRequirements(selector fields.Selector) (FieldRequirements, fields.Selector, error) {
requirements := FieldRequirements{}
if selector == nil {
return requirements, selector, nil
}
for _, r := range selector.Requirements() {
switch r.Field {
case folderAnnoKey:
if (r.Operator != selection.Equals) && (r.Operator != selection.DoubleEquals) {
return requirements, selector, apierrors.NewBadRequest("only equality is supported in the selectors")
}
folder := r.Value
requirements.Folder = &folder
}
}
// use Transform function to remove grafana.app/folder field selector
selector, err := selector.Transform(func(field, value string) (string, string, error) {
switch field {
case folderAnnoKey:
return "", "", nil
}
return field, value, nil
})
return requirements, selector, err
}
func RegisterFieldSelectorSupport(scheme *runtime.Scheme) error {
grafanaFieldSupport := runtime.FieldLabelConversionFunc(
func(field, value string) (string, string, error) {
if strings.HasPrefix(field, "grafana.app/") {
return field, value, nil
}
return "", "", getBadSelectorError(field)
},
)
// Register all the internal types
for gvk := range scheme.AllKnownTypes() {
if strings.HasSuffix(gvk.Group, ".grafana.app") {
err := scheme.AddFieldLabelConversionFunc(gvk, grafanaFieldSupport)
if err != nil {
return err
}
}
}
return nil
}
func getBadSelectorError(f string) error {
return apierrors.NewBadRequest(
fmt.Sprintf("%q is not a known field selector: only %q works", f, folderAnnoKey),
)
}

@ -241,29 +241,16 @@ func (s *Storage) GetList(ctx context.Context, key string, opts storage.ListOpti
}
}
// translate grafana.app/folder field selector to folder condition
fields := opts.Predicate.Field.Requirements()
for _, f := range fields {
if f.Field == "grafana.app/folder" {
if f.Operator != selection.Equals {
return apierrors.NewBadRequest("grafana.app/folder field selector only supports equality")
}
// select items in the spcified folder
req.Folder = f.Value
}
}
// use Transform function to remove grafana.app/folder field selector
opts.Predicate.Field, err = opts.Predicate.Field.Transform(func(field, value string) (string, string, error) {
if field == "grafana.app/folder" {
return "", "", nil
}
return field, value, nil
})
// translate grafana.app/folder field selector to the folder condition
fieldRequirements, fieldSelector, err := ReadFieldRequirements(opts.Predicate.Field)
if err != nil {
return err
}
if fieldRequirements.Folder != nil {
req.Folder = *fieldRequirements.Folder
}
// Update the field selector to remove the unneeded selectors
opts.Predicate.Field = fieldSelector
rsp, err := s.store.List(ctx, req)
if err != nil {

Loading…
Cancel
Save