mirror of
https://github.com/grafana/grafana.git
synced 2026-01-15 23:27:12 +00:00
Compare commits
5 Commits
undef1nd/s
...
selectable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6c27410e5 | ||
|
|
9c86309dea | ||
|
|
cc746ad106 | ||
|
|
b2d14cc42b | ||
|
|
584287dc61 |
@@ -8,6 +8,12 @@ foldersV1beta1: {
|
||||
spec: {
|
||||
title: string
|
||||
description?: string
|
||||
foo: bool
|
||||
bar: int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectableFields: [
|
||||
"spec.title",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,13 +5,26 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaFolder = resource.NewSimpleSchema("folder.grafana.app", "v1beta1", NewFolder(), &FolderList{}, resource.WithKind("Folder"),
|
||||
resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope))
|
||||
resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{
|
||||
FieldSelector: "spec.title",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*Folder)
|
||||
if !ok {
|
||||
return "", errors.New("provided object must be of type *Folder")
|
||||
}
|
||||
|
||||
return cast.Spec.Title, nil
|
||||
},
|
||||
},
|
||||
}))
|
||||
kindFolder = resource.Kind{
|
||||
Schema: schemaFolder,
|
||||
Codecs: map[resource.KindEncoding]resource.Codec{
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
v1beta1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "folder",
|
||||
Group: "folder.grafana.app",
|
||||
@@ -32,6 +34,9 @@ var appManifestData = app.ManifestData{
|
||||
Plural: "Folders",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
SelectableFields: []string{
|
||||
"spec.title",
|
||||
},
|
||||
},
|
||||
},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
|
||||
@@ -14,7 +14,7 @@ userKind: {
|
||||
}
|
||||
|
||||
userv0alpha1: userKind & {
|
||||
// TODO: Uncomment this when User will be added to ManagedKinds
|
||||
// TODO: Uncomment this when User will be added to ManagedKinds
|
||||
// validation: {
|
||||
// operations: [
|
||||
// "CREATE",
|
||||
|
||||
4
apps/iam/pkg/apis/iam_manifest.go
generated
4
apps/iam/pkg/apis/iam_manifest.go
generated
@@ -74,6 +74,10 @@ var appManifestData = app.ManifestData{
|
||||
Plural: "Users",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
SelectableFields: []string{
|
||||
"spec.email",
|
||||
"spec.login",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -4,11 +4,24 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
)
|
||||
|
||||
func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, optsGetter generic.RESTOptionsGetter) (*registry.Store, error) {
|
||||
type registryStoreOptions struct {
|
||||
attrFunc storage.AttrFunc
|
||||
}
|
||||
|
||||
type OptionFn func(*registryStoreOptions)
|
||||
|
||||
func WithAttrFunc(attrFunc storage.AttrFunc) OptionFn {
|
||||
return func(opts *registryStoreOptions) {
|
||||
opts.attrFunc = attrFunc
|
||||
}
|
||||
}
|
||||
|
||||
func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, optsGetter generic.RESTOptionsGetter, options ...OptionFn) (*registry.Store, error) {
|
||||
gv := resourceInfo.GroupVersion()
|
||||
gv.Version = runtime.APIVersionInternal
|
||||
strategy := NewStrategy(scheme, gv)
|
||||
@@ -20,7 +33,7 @@ func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, o
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: Matcher,
|
||||
//PredicateFunc: Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
TableConvertor: resourceInfo.TableConverter(),
|
||||
@@ -28,8 +41,16 @@ func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, o
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
|
||||
opts := ®istryStoreOptions{
|
||||
attrFunc: GetAttrs,
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
o := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: opts.attrFunc}
|
||||
if err := store.CompleteWithOptions(o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return store, nil
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
@@ -22,6 +24,8 @@ import (
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
sdkres "github.com/grafana/grafana-app-sdk/resource"
|
||||
|
||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/apps/iam/pkg/reconcilers"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
@@ -129,6 +133,29 @@ func (b *FolderAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
Version: runtime.APIVersionInternal,
|
||||
})
|
||||
|
||||
kinds := []sdkres.Kind{folders.FolderKind()}
|
||||
for _, kind := range kinds {
|
||||
gvk := gv.WithKind(kind.Kind())
|
||||
err := scheme.AddFieldLabelConversionFunc(
|
||||
gvk,
|
||||
func(label, value string) (string, string, error) {
|
||||
if label == "metadata.name" || label == "metadata.namespace" {
|
||||
return label, value, nil
|
||||
}
|
||||
fields := kind.SelectableFields()
|
||||
for _, field := range fields {
|
||||
if field.FieldSelector == label {
|
||||
return label, value, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("field label not supported for %s: %s", gvk, label)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If multiple versions exist, then register conversions from zz_generated.conversion.go
|
||||
// if err := playlist.RegisterConversions(scheme); err != nil {
|
||||
// return err
|
||||
@@ -137,6 +164,26 @@ func (b *FolderAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
return scheme.SetVersionPriority(gv)
|
||||
}
|
||||
|
||||
// TODO: work with all kinds from schema, not just one.
|
||||
func (b *FolderAPIBuilder) BuildGetAttrsFn(k sdkres.Kind) func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
return func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
if robj, ok := obj.(sdkres.Object); !ok {
|
||||
return nil, nil, fmt.Errorf("not a resource.Object")
|
||||
} else {
|
||||
fieldsSet := fields.Set{}
|
||||
|
||||
for _, f := range k.SelectableFields() {
|
||||
v, err := f.FieldValueFunc(robj)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fieldsSet[f.FieldSelector] = v
|
||||
}
|
||||
return robj.GetLabels(), fieldsSet, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *FolderAPIBuilder) AllowedV0Alpha1Resources() []string {
|
||||
return nil
|
||||
}
|
||||
@@ -148,10 +195,11 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||
Permissions: b.setDefaultFolderPermissions,
|
||||
})
|
||||
|
||||
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
|
||||
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter, grafanaregistry.WithAttrFunc(b.BuildGetAttrsFn(folders.FolderKind())))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.registerPermissionHooks(unified)
|
||||
b.storage = unified
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ import (
|
||||
"github.com/grafana/dskit/kv"
|
||||
"github.com/grafana/dskit/ring"
|
||||
ringclient "github.com/grafana/dskit/ring/client"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/options"
|
||||
"github.com/grafana/grafana/pkg/storage/unified"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
@@ -76,6 +79,22 @@ func newModuleServer(opts Options,
|
||||
) (*ModuleServer, error) {
|
||||
rootCtx, shutdownFn := context.WithCancel(context.Background())
|
||||
|
||||
// TODO should inject this with Wire
|
||||
apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver")
|
||||
searchServerAddress := apiserverCfg.Key("search_server_address").MustString("")
|
||||
var searchClient resourcepb.ResourceIndexClient
|
||||
var err error
|
||||
if searchServerAddress != "" {
|
||||
storageOptions := options.StorageOptions{
|
||||
SearchServerAddress: searchServerAddress,
|
||||
}
|
||||
searchClient, err = unified.NewSearchClient(storageOptions, features)
|
||||
if err != nil {
|
||||
shutdownFn()
|
||||
return nil, fmt.Errorf("failed to create search client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s := &ModuleServer{
|
||||
opts: opts,
|
||||
apiOpts: apiOpts,
|
||||
@@ -96,6 +115,7 @@ func newModuleServer(opts Options,
|
||||
license: license,
|
||||
moduleRegisterer: moduleRegisterer,
|
||||
storageBackend: storageBackend,
|
||||
searchClient: searchClient,
|
||||
hooksService: hooksService,
|
||||
}
|
||||
|
||||
@@ -119,6 +139,7 @@ type ModuleServer struct {
|
||||
isInitialized bool
|
||||
mtx sync.Mutex
|
||||
storageBackend resource.StorageBackend
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
storageMetrics *resource.StorageMetrics
|
||||
indexMetrics *resource.BleveIndexMetrics
|
||||
license licensing.Licensing
|
||||
@@ -202,7 +223,7 @@ func (s *ModuleServer) Run() error {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sql.ProvideUnifiedStorageGrpcService(s.cfg, s.features, nil, s.log, s.registerer, docBuilders, s.storageMetrics, s.indexMetrics, s.searchServerRing, s.MemberlistKVConfig, s.httpServerRouter, s.storageBackend)
|
||||
return sql.ProvideUnifiedStorageGrpcService(s.cfg, s.features, nil, s.log, s.registerer, docBuilders, s.storageMetrics, s.indexMetrics, s.searchServerRing, s.MemberlistKVConfig, s.httpServerRouter, s.storageBackend, s.searchClient)
|
||||
})
|
||||
|
||||
m.RegisterModule(modules.ZanzanaServer, func() (services.Service, error) {
|
||||
|
||||
@@ -546,6 +546,11 @@ func (s *Storage) processItem(ctx context.Context, item *resourcepb.ResourceWrap
|
||||
}
|
||||
}
|
||||
|
||||
// Temp fix for prototype testing - don't filter out when wildcard used for field selector
|
||||
if len(predicate.Field.Requirements()) == 1 && predicate.Field.Requirements()[0].Value == "*" {
|
||||
return obj, true, nil
|
||||
}
|
||||
|
||||
// Apply predicate filtering
|
||||
ok, err := predicate.Matches(obj)
|
||||
if err != nil || !ok {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
badger "github.com/dgraph-io/badger/v4"
|
||||
"github.com/fullstorydev/grpchan"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
otgrpc "github.com/opentracing-contrib/go-grpc"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -31,6 +33,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/federated"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/search"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql"
|
||||
"github.com/grafana/grafana/pkg/util/scheduler"
|
||||
@@ -91,6 +94,27 @@ func ProvideUnifiedStorageClient(opts *Options,
|
||||
return client, err
|
||||
}
|
||||
|
||||
// TODO use wire to provide to module server
|
||||
func NewSearchClient(opts options.StorageOptions, features featuremgmt.FeatureToggles) (resourcepb.ResourceIndexClient, error) {
|
||||
if opts.SearchServerAddress == "" {
|
||||
return nil, fmt.Errorf("expecting address for search server")
|
||||
}
|
||||
|
||||
var (
|
||||
conn grpc.ClientConnInterface
|
||||
err error
|
||||
metrics = newClientMetrics(prometheus.NewRegistry())
|
||||
)
|
||||
|
||||
conn, err = newGrpcConn(opts.SearchServerAddress, metrics, features, opts.GrpcClientKeepaliveTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := grpchan.InterceptClientConn(conn, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)
|
||||
return resourcepb.NewResourceIndexClient(cc), nil
|
||||
}
|
||||
|
||||
func newClient(opts options.StorageOptions,
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
|
||||
@@ -99,6 +99,12 @@ message ResourceSearchRequest {
|
||||
int64 page = 11;
|
||||
|
||||
int64 permission = 12;
|
||||
|
||||
// JSON-encoded array of sort values for SearchAfter pagination.
|
||||
bytes search_after = 13;
|
||||
|
||||
// JSON-encoded array of sort values for SearchBefore pagination.
|
||||
bytes search_before = 14;
|
||||
}
|
||||
|
||||
message ResourceSearchResponse {
|
||||
@@ -139,6 +145,9 @@ message ResourceSearchResponse {
|
||||
|
||||
// Facet results
|
||||
map<string,Facet> facet = 7;
|
||||
|
||||
// Resource version of the index used for this search.
|
||||
int64 resource_version = 8;
|
||||
}
|
||||
|
||||
message RebuildIndexesRequest {
|
||||
|
||||
@@ -18,6 +18,10 @@ type ContinueToken struct {
|
||||
ResourceVersion int64 `json:"v"`
|
||||
// SortAscending indicates the sort order (used by list history).
|
||||
SortAscending bool `json:"s,omitempty"`
|
||||
// SearchAfter is a JSON array of sort values for search pagination.
|
||||
SearchAfter json.RawMessage `json:"sa,omitempty"`
|
||||
// SearchBefore is a JSON array of sort values for search pagination.
|
||||
SearchBefore json.RawMessage `json:"sb,omitempty"`
|
||||
}
|
||||
|
||||
func (c ContinueToken) String() string {
|
||||
@@ -39,3 +43,12 @@ func GetContinueToken(token string) (*ContinueToken, error) {
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// NewSearchContinueToken encodes SearchAfter values into a continue token string.
|
||||
func NewSearchContinueToken(searchAfter []string, rv int64) (string, error) {
|
||||
raw, err := json.Marshal(searchAfter)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ContinueToken{SearchAfter: raw, ResourceVersion: rv}.String(), nil
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package resource
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
@@ -101,6 +103,9 @@ type IndexableDocument struct {
|
||||
// metadata, annotations, or external data linked at index time
|
||||
Fields map[string]any `json:"fields,omitempty"`
|
||||
|
||||
// Automatically indexed selectable fields, used for field-based filtering when listing.
|
||||
SelectableFields map[string]string `json:"selectable_fields,omitempty"`
|
||||
|
||||
// Maintain a list of resource references.
|
||||
// Someday this will likely be part of https://github.com/grafana/gamma
|
||||
References ResourceReferences `json:"references,omitempty"`
|
||||
@@ -175,7 +180,7 @@ func (m ResourceReferences) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
// Create a new indexable document based on a generic k8s resource
|
||||
func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.GrafanaMetaAccessor) *IndexableDocument {
|
||||
func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.GrafanaMetaAccessor, selectableFields map[string]string) *IndexableDocument {
|
||||
title := obj.FindTitle(key.Name)
|
||||
if title == key.Name {
|
||||
// TODO: something wrong with FindTitle
|
||||
@@ -191,14 +196,15 @@ func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.Grafa
|
||||
}
|
||||
}
|
||||
doc := &IndexableDocument{
|
||||
Key: key,
|
||||
RV: rv,
|
||||
Name: key.Name,
|
||||
Title: title, // We always want *something* to display
|
||||
Labels: obj.GetLabels(),
|
||||
Folder: obj.GetFolder(),
|
||||
CreatedBy: obj.GetCreatedBy(),
|
||||
UpdatedBy: obj.GetUpdatedBy(),
|
||||
Key: key,
|
||||
RV: rv,
|
||||
Name: key.Name,
|
||||
Title: title, // We always want *something* to display
|
||||
Labels: obj.GetLabels(),
|
||||
Folder: obj.GetFolder(),
|
||||
CreatedBy: obj.GetCreatedBy(),
|
||||
UpdatedBy: obj.GetUpdatedBy(),
|
||||
SelectableFields: selectableFields,
|
||||
}
|
||||
m, ok := obj.GetManagerProperties()
|
||||
if ok {
|
||||
@@ -220,11 +226,14 @@ func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.Grafa
|
||||
return doc.UpdateCopyFields()
|
||||
}
|
||||
|
||||
func StandardDocumentBuilder() DocumentBuilder {
|
||||
return &standardDocumentBuilder{}
|
||||
func StandardDocumentBuilder(manifests []app.Manifest) DocumentBuilder {
|
||||
return &standardDocumentBuilder{selectableFields: SelectableFieldsForManifests(manifests)}
|
||||
}
|
||||
|
||||
type standardDocumentBuilder struct{}
|
||||
type standardDocumentBuilder struct {
|
||||
// Maps "group/resource" (in lowercase) to list of selectable fields.
|
||||
selectableFields map[string][]string
|
||||
}
|
||||
|
||||
func (s *standardDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*IndexableDocument, error) {
|
||||
tmp := &unstructured.Unstructured{}
|
||||
@@ -238,10 +247,36 @@ func (s *standardDocumentBuilder) BuildDocument(ctx context.Context, key *resour
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := NewIndexableDocument(key, rv, obj)
|
||||
sfKey := strings.ToLower(key.GetGroup() + "/" + key.GetResource())
|
||||
selectableFields := buildSelectableFields(tmp, s.selectableFields[sfKey])
|
||||
|
||||
doc := NewIndexableDocument(key, rv, obj, selectableFields)
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func buildSelectableFields(tmp *unstructured.Unstructured, fields []string) map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
for _, field := range fields {
|
||||
path := strings.Split(field, ".")
|
||||
val, ok, err := unstructured.NestedFieldNoCopy(tmp.Object, path...)
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
result[field] = v
|
||||
case bool:
|
||||
result[field] = strconv.FormatBool(v)
|
||||
case int, float64:
|
||||
result[field] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type searchableDocumentFields struct {
|
||||
names []string
|
||||
fields map[string]*resourceTableColumn
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
func TestStandardDocumentBuilder(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder := StandardDocumentBuilder()
|
||||
builder := StandardDocumentBuilder(nil)
|
||||
|
||||
body, err := os.ReadFile("testdata/playlist-resource.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
45
pkg/storage/unified/resource/selectable_fields.go
Normal file
45
pkg/storage/unified/resource/selectable_fields.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
|
||||
folder "github.com/grafana/grafana/apps/folder/pkg/apis/manifestdata"
|
||||
iam "github.com/grafana/grafana/apps/iam/pkg/apis"
|
||||
)
|
||||
|
||||
func AppManifests() []app.Manifest {
|
||||
return []app.Manifest{
|
||||
iam.LocalManifest(),
|
||||
folder.LocalManifest(),
|
||||
}
|
||||
}
|
||||
|
||||
func SelectableFields() map[string][]string {
|
||||
return SelectableFieldsForManifests(AppManifests())
|
||||
}
|
||||
|
||||
// SelectableFieldsForManifests returns map of <group/kind> to list of selectable fields.
|
||||
// Also <group/plural> is included as a key, pointing to the same fields.
|
||||
func SelectableFieldsForManifests(manifests []app.Manifest) map[string][]string {
|
||||
fields := map[string][]string{}
|
||||
|
||||
for _, m := range manifests {
|
||||
group := m.ManifestData.Group
|
||||
|
||||
for _, version := range m.ManifestData.Versions {
|
||||
for _, kind := range version.Kinds {
|
||||
key := strings.ToLower(group + "/" + kind.Kind)
|
||||
keyPlural := strings.ToLower(group + "/" + kind.Plural)
|
||||
|
||||
if len(kind.SelectableFields) > 0 {
|
||||
fields[key] = kind.SelectableFields
|
||||
fields[keyPlural] = kind.SelectableFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/google/uuid"
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/backoff"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -20,9 +22,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/backoff"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/validation"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -223,6 +222,9 @@ type ResourceServerOptions struct {
|
||||
// Search options
|
||||
Search SearchOptions
|
||||
|
||||
// to be used by storage
|
||||
SearchClient resourcepb.ResourceIndexClient
|
||||
|
||||
// Quota service
|
||||
OverridesService *OverridesService
|
||||
|
||||
@@ -348,6 +350,7 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
|
||||
queue: opts.QOSQueue,
|
||||
queueConfig: opts.QOSConfig,
|
||||
overridesService: opts.OverridesService,
|
||||
searchClient: opts.SearchClient,
|
||||
|
||||
artificialSuccessfulWriteDelay: opts.Search.IndexMinUpdateInterval,
|
||||
}
|
||||
@@ -387,6 +390,9 @@ type server struct {
|
||||
indexMetrics *BleveIndexMetrics
|
||||
overridesService *OverridesService
|
||||
|
||||
// only to be used with storage server for field selector search
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
|
||||
// Background watch task -- this has permissions for everything
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@@ -1043,6 +1049,111 @@ func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resour
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
// Remove metadata.namespace filter from requirement fields, if it's present.
|
||||
for ix := 0; ix < len(req.Options.Fields); {
|
||||
v := req.Options.Fields[ix]
|
||||
if v.Key == "metadata.namespace" && v.Operator == "=" {
|
||||
if len(v.Values) == 1 && v.Values[0] == req.Options.Key.Namespace {
|
||||
// Remove this requirement from fields, as it's implied by the key.namespace.
|
||||
req.Options.Fields = append(req.Options.Fields[:ix], req.Options.Fields[ix+1:]...)
|
||||
// Don't increment ix, as we're removing an element from the slice.
|
||||
continue
|
||||
}
|
||||
}
|
||||
ix++
|
||||
}
|
||||
|
||||
// TODO: What to do about RV and version_match fields?
|
||||
// If we get here, we're doing list with selectable fields. Let's do search instead, since
|
||||
// we index all selectable fields, and fetch resulting documents one by one.
|
||||
if (s.search != nil || s.searchClient != nil) && req.Source == resourcepb.ListRequest_STORE && (len(req.Options.Fields) > 0) {
|
||||
if req.Options.Key.Namespace == "" {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("namespace must be specified for list with filter"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO should make it clear that listing with field selectors utilizes search and only the current data is available
|
||||
// Should we return an error when version match is specified?
|
||||
if req.VersionMatchV2 == resourcepb.ResourceVersionMatchV2_Exact || req.VersionMatchV2 == resourcepb.ResourceVersionMatchV2_NotOlderThan {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("version_match_v2 is not supported for list when using field selectors since it utilizes search"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
srq := &resourcepb.ResourceSearchRequest{
|
||||
Options: req.Options,
|
||||
//Federated: nil,
|
||||
Limit: req.Limit,
|
||||
// Page: 0,
|
||||
// Permission: 0, // Not needed, default is List
|
||||
}
|
||||
|
||||
var listRv int64
|
||||
if req.NextPageToken != "" {
|
||||
token, err := GetContinueToken(req.NextPageToken)
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("invalid continue token"),
|
||||
}, nil
|
||||
}
|
||||
listRv = token.ResourceVersion
|
||||
srq.SearchAfter = token.SearchAfter
|
||||
srq.SearchBefore = token.SearchBefore
|
||||
}
|
||||
|
||||
var searchResp *resourcepb.ResourceSearchResponse
|
||||
var err error
|
||||
if s.searchClient != nil {
|
||||
searchResp, err = s.searchClient.Search(ctx, srq)
|
||||
} else {
|
||||
searchResp, err = s.search.Search(ctx, srq)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if listRv <= 0 {
|
||||
listRv = searchResp.ResourceVersion
|
||||
}
|
||||
|
||||
rsp := &resourcepb.ListResponse{ResourceVersion: listRv}
|
||||
// Using searchResp.GetResults().GetRows() will not panic if anything is nil on the path.
|
||||
for _, row := range searchResp.GetResults().GetRows() {
|
||||
// TODO: use batch reading
|
||||
val, err := s.Read(ctx, &resourcepb.ReadRequest{
|
||||
Key: row.Key,
|
||||
ResourceVersion: row.ResourceVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
if len(val.Value) > 0 {
|
||||
rsp.Items = append(rsp.Items, &resourcepb.ResourceWrapper{
|
||||
Value: val.Value,
|
||||
ResourceVersion: val.ResourceVersion,
|
||||
})
|
||||
if val.ResourceVersion > rsp.ResourceVersion {
|
||||
rsp.ResourceVersion = val.ResourceVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The token on the search results will have the current RV of the index from the most recent search page.
|
||||
// But since were paginating, we want to override this with the RV from the start of the list operation.
|
||||
if len(rsp.Items) > 0 {
|
||||
token, err := GetContinueToken(searchResp.GetResults().GetNextPageToken())
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("invalid continue token"),
|
||||
}, nil
|
||||
}
|
||||
token.ResourceVersion = listRv
|
||||
rsp.NextPageToken = token.String()
|
||||
}
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
if req.Limit < 1 {
|
||||
req.Limit = 500 // default max 500 items in a page
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ type kvStorageBackend struct {
|
||||
dataStore *dataStore
|
||||
eventStore *eventStore
|
||||
notifier *notifier
|
||||
builder DocumentBuilder
|
||||
log logging.Logger
|
||||
withPruner bool
|
||||
eventRetentionPeriod time.Duration
|
||||
@@ -109,8 +108,7 @@ func NewKVStorageBackend(opts KVBackendOptions) (StorageBackend, error) {
|
||||
eventStore: eventStore,
|
||||
notifier: newNotifier(eventStore, notifierOptions{}),
|
||||
snowflake: s,
|
||||
builder: StandardDocumentBuilder(), // For now we use the standard document builder.
|
||||
log: &logging.NoOpLogger{}, // Make this configurable
|
||||
log: &logging.NoOpLogger{}, // Make this configurable
|
||||
eventRetentionPeriod: eventRetentionPeriod,
|
||||
eventPruningInterval: eventPruningInterval,
|
||||
withExperimentalClusterScope: opts.WithExperimentalClusterScope,
|
||||
|
||||
@@ -165,10 +165,14 @@ type ResourceSearchRequest struct {
|
||||
// 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"`
|
||||
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"`
|
||||
// JSON-encoded array of sort values for SearchAfter pagination.
|
||||
SearchAfter []byte `protobuf:"bytes,13,opt,name=search_after,json=searchAfter,proto3" json:"search_after,omitempty"`
|
||||
// JSON-encoded array of sort values for SearchBefore pagination.
|
||||
SearchBefore []byte `protobuf:"bytes,14,opt,name=search_before,json=searchBefore,proto3" json:"search_before,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -287,6 +291,20 @@ func (x *ResourceSearchRequest) GetPermission() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetSearchAfter() []byte {
|
||||
if x != nil {
|
||||
return x.SearchAfter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchRequest) GetSearchBefore() []byte {
|
||||
if x != nil {
|
||||
return x.SearchBefore
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceSearchResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Error details
|
||||
@@ -302,9 +320,11 @@ type ResourceSearchResponse struct {
|
||||
// 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
|
||||
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"`
|
||||
// Resource version of the index used for this search.
|
||||
ResourceVersion int64 `protobuf:"varint,8,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) Reset() {
|
||||
@@ -386,6 +406,13 @@ func (x *ResourceSearchResponse) GetFacet() map[string]*ResourceSearchResponse_F
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceSearchResponse) GetResourceVersion() int64 {
|
||||
if x != nil {
|
||||
return x.ResourceVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RebuildIndexesRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Namespace (tenant) must be the same as all keys' namespace
|
||||
@@ -818,7 +845,7 @@ var file_search_proto_rawDesc = string([]byte{
|
||||
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,
|
||||
0x74, 0x22, 0xd6, 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,
|
||||
@@ -846,93 +873,100 @@ var file_search_proto_rawDesc = string([]byte{
|
||||
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,
|
||||
0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x23, 0x0a,
|
||||
0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x0e,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x42, 0x65, 0x66, 0x6f,
|
||||
0x72, 0x65, 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, 0x95, 0x05, 0x0a, 0x16, 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,
|
||||
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,
|
||||
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, 0x22,
|
||||
0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65,
|
||||
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, 0x29, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 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, 0x04, 0x6b, 0x65, 0x79,
|
||||
0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64,
|
||||
0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c,
|
||||
0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x18, 0x03, 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, 0x32, 0xfe, 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, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 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,
|
||||
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, 0x12, 0x29, 0x0a, 0x10,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 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, 0x22, 0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64,
|
||||
0x65, 0x78, 0x65, 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, 0x29, 0x0a, 0x04, 0x6b, 0x65, 0x79,
|
||||
0x73, 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, 0x04,
|
||||
0x6b, 0x65, 0x79, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x22, 0x0a, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2b, 0x0a,
|
||||
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 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, 0x32, 0xfe, 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, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c,
|
||||
0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78,
|
||||
0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x65, 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 (
|
||||
|
||||
@@ -93,6 +93,8 @@ type bleveBackend struct {
|
||||
|
||||
indexMetrics *resource.BleveIndexMetrics
|
||||
|
||||
selectableFields map[string][]string
|
||||
|
||||
bgTasksCancel func()
|
||||
bgTasksWg sync.WaitGroup
|
||||
}
|
||||
@@ -135,11 +137,12 @@ func NewBleveBackend(opts BleveOptions, indexMetrics *resource.BleveIndexMetrics
|
||||
}
|
||||
|
||||
be := &bleveBackend{
|
||||
log: l,
|
||||
cache: map[resource.NamespacedResource]*bleveIndex{},
|
||||
opts: opts,
|
||||
ownsIndexFn: ownFn,
|
||||
indexMetrics: indexMetrics,
|
||||
log: l,
|
||||
cache: map[resource.NamespacedResource]*bleveIndex{},
|
||||
opts: opts,
|
||||
ownsIndexFn: ownFn,
|
||||
indexMetrics: indexMetrics,
|
||||
selectableFields: resource.SelectableFields(),
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -366,7 +369,9 @@ func (b *bleveBackend) BuildIndex(
|
||||
attribute.String("reason", indexBuildReason),
|
||||
)
|
||||
|
||||
mapper, err := GetBleveMappings(fields)
|
||||
selectableFields := b.selectableFields[fmt.Sprintf("%s/%s", key.Group, key.Resource)]
|
||||
|
||||
mapper, err := GetBleveMappings(fields, selectableFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -459,7 +464,7 @@ func (b *bleveBackend) BuildIndex(
|
||||
}
|
||||
|
||||
// Batch all the changes
|
||||
idx := b.newBleveIndex(key, index, newIndexType, fields, allFields, standardSearchFields, updater, b.log.New("namespace", key.Namespace, "group", key.Group, "resource", key.Resource))
|
||||
idx := b.newBleveIndex(key, index, newIndexType, fields, allFields, standardSearchFields, selectableFields, updater, b.log.New("namespace", key.Namespace, "group", key.Group, "resource", key.Resource))
|
||||
|
||||
if build {
|
||||
if b.indexMetrics != nil {
|
||||
@@ -699,8 +704,9 @@ type bleveIndex struct {
|
||||
// Subsequent update requests only trigger new update if minUpdateInterval has elapsed.
|
||||
nextUpdateTime time.Time
|
||||
|
||||
standard resource.SearchableDocumentFields
|
||||
fields resource.SearchableDocumentFields
|
||||
standard resource.SearchableDocumentFields
|
||||
fields resource.SearchableDocumentFields
|
||||
selectableFields []string
|
||||
|
||||
indexStorage string // memory or file, used when updating metrics
|
||||
|
||||
@@ -736,6 +742,7 @@ func (b *bleveBackend) newBleveIndex(
|
||||
fields resource.SearchableDocumentFields,
|
||||
allFields []*resourcepb.ResourceTableColumnDefinition,
|
||||
standardSearchFields resource.SearchableDocumentFields,
|
||||
selectableFields []string,
|
||||
updaterFn resource.UpdateFn,
|
||||
logger log.Logger,
|
||||
) *bleveIndex {
|
||||
@@ -745,6 +752,7 @@ func (b *bleveBackend) newBleveIndex(
|
||||
indexStorage: newIndexType,
|
||||
fields: fields,
|
||||
allFields: allFields,
|
||||
selectableFields: selectableFields,
|
||||
standard: standardSearchFields,
|
||||
logger: logger,
|
||||
updaterFn: updaterFn,
|
||||
@@ -1029,6 +1037,8 @@ func (b *bleveIndex) Search(
|
||||
return response, nil
|
||||
}
|
||||
|
||||
response.ResourceVersion = b.resourceVersion
|
||||
|
||||
// Verifies the index federation
|
||||
index, err := b.getIndex(ctx, req, federate)
|
||||
if err != nil {
|
||||
@@ -1080,6 +1090,19 @@ func (b *bleveIndex) Search(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// I'm setting the NextPageToken on the response results here because we need to get the Sort fields from the last hit,
|
||||
// which isn't available in the ResourceTable results on the search response.
|
||||
// Another alternative would be to return the Sort fields of the last hit as part of the search response.
|
||||
if response.Results != nil && len(res.Hits) > 0 && response.TotalHits > int64(len(res.Hits)) {
|
||||
values := searchAfterValuesFromHit(res.Hits[len(res.Hits)-1])
|
||||
if len(values) > 0 {
|
||||
token, err := resource.NewSearchContinueToken(values, b.resourceVersion)
|
||||
if err == nil {
|
||||
response.Results.NextPageToken = token
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse the facet fields
|
||||
for k, v := range res.Facets {
|
||||
f := newResponseFacet(v)
|
||||
@@ -1200,6 +1223,22 @@ func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.R
|
||||
Explain: req.Explain,
|
||||
Facets: facets,
|
||||
}
|
||||
if len(req.SearchAfter) > 0 {
|
||||
var values []string
|
||||
if err := json.Unmarshal(req.SearchAfter, &values); err != nil {
|
||||
return nil, resource.AsErrorResult(err)
|
||||
}
|
||||
searchrequest.SearchAfter = values
|
||||
searchrequest.From = 0
|
||||
}
|
||||
if len(req.SearchBefore) > 0 {
|
||||
var values []string
|
||||
if err := json.Unmarshal(req.SearchBefore, &values); err != nil {
|
||||
return nil, resource.AsErrorResult(err)
|
||||
}
|
||||
searchrequest.SearchBefore = values
|
||||
searchrequest.From = 0
|
||||
}
|
||||
|
||||
// Currently everything is within an AND query
|
||||
queries := []query.Query{}
|
||||
@@ -1215,7 +1254,11 @@ func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.R
|
||||
// filters
|
||||
if len(req.Options.Fields) > 0 {
|
||||
for _, v := range req.Options.Fields {
|
||||
q, err := requirementQuery(v, "")
|
||||
prefix := ""
|
||||
if b.isSelectableField(v.Key) {
|
||||
prefix = "selectable_fields."
|
||||
}
|
||||
q, err := requirementQuery(v, prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1787,6 +1830,32 @@ func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hit
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func searchAfterValuesFromHit(hit *search.DocumentMatch) []string {
|
||||
if hit == nil {
|
||||
return nil
|
||||
}
|
||||
if len(hit.Sort) > 0 {
|
||||
values := make([]string, 0, len(hit.Sort))
|
||||
for _, v := range hit.Sort {
|
||||
values = append(values, fmt.Sprintf("%v", v))
|
||||
}
|
||||
return values
|
||||
}
|
||||
if hit.ID != "" {
|
||||
return []string{hit.ID}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bleveIndex) isSelectableField(key string) bool {
|
||||
for _, f := range b.selectableFields {
|
||||
if key == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getAllFields(standard resource.SearchableDocumentFields, custom resource.SearchableDocumentFields) ([]*resourcepb.ResourceTableColumnDefinition, error) {
|
||||
fields := []*resourcepb.ResourceTableColumnDefinition{
|
||||
standard.Field(resource.SEARCH_FIELD_ID),
|
||||
|
||||
@@ -10,19 +10,19 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func GetBleveMappings(fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
|
||||
func GetBleveMappings(fields resource.SearchableDocumentFields, selectableFields []string) (mapping.IndexMapping, error) {
|
||||
mapper := bleve.NewIndexMapping()
|
||||
|
||||
err := RegisterCustomAnalyzers(mapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapper.DefaultMapping = getBleveDocMappings(fields)
|
||||
mapper.DefaultMapping = getBleveDocMappings(fields, selectableFields)
|
||||
|
||||
return mapper, nil
|
||||
}
|
||||
|
||||
func getBleveDocMappings(fields resource.SearchableDocumentFields) *mapping.DocumentMapping {
|
||||
func getBleveDocMappings(fields resource.SearchableDocumentFields, selectableFields []string) *mapping.DocumentMapping {
|
||||
mapper := bleve.NewDocumentStaticMapping()
|
||||
|
||||
nameMapping := &mapping.FieldMapping{
|
||||
@@ -165,5 +165,73 @@ func getBleveDocMappings(fields resource.SearchableDocumentFields) *mapping.Docu
|
||||
|
||||
mapper.AddSubDocumentMapping("fields", fieldMapper)
|
||||
|
||||
selectableFieldsMapper := bleve.NewDocumentStaticMapping()
|
||||
for _, field := range selectableFields {
|
||||
selectableFieldsMapper.AddFieldMappingsAt(field, &mapping.FieldMapping{
|
||||
Name: field,
|
||||
Type: "text",
|
||||
Analyzer: keyword.Name,
|
||||
Store: false,
|
||||
Index: true,
|
||||
})
|
||||
}
|
||||
mapper.AddSubDocumentMapping("selectable_fields", selectableFieldsMapper)
|
||||
|
||||
return mapper
|
||||
}
|
||||
|
||||
/*
|
||||
Here's a tree representation of the field mappings in pkg/storage/unified/search/bleve_mappings.go:
|
||||
|
||||
Document Root (DefaultMapping)
|
||||
│
|
||||
├── name [text, keyword analyzer]
|
||||
│
|
||||
├── title_phrase [keyword, not stored]
|
||||
│
|
||||
├── title [3 mappings]
|
||||
│ ├── [1] standard analyzer, stored
|
||||
│ ├── [2] TITLE_ANALYZER (edge ngram), not stored
|
||||
│ └── [3] keyword, not stored
|
||||
│
|
||||
├── description [text, stored]
|
||||
│
|
||||
├── tags [text, keyword analyzer, stored, includeInAll]
|
||||
│
|
||||
├── folder [text, keyword analyzer, stored, includeInAll, docValues]
|
||||
│
|
||||
├── managedBy [text, keyword analyzer, not stored]
|
||||
│
|
||||
├── source/ [sub-document]
|
||||
│ ├── path [text, keyword analyzer, stored]
|
||||
│ ├── checksum [text, keyword analyzer, stored]
|
||||
│ └── timestampMillis [numeric]
|
||||
│
|
||||
├── manager/ [sub-document]
|
||||
│ ├── kind [text, keyword analyzer, stored, includeInAll]
|
||||
│ └── id [text, keyword analyzer, stored, includeInAll]
|
||||
│
|
||||
├── reference/ [sub-document, default analyzer: keyword]
|
||||
│ └── (dynamic fields inherit keyword analyzer)
|
||||
│
|
||||
├── labels/ [sub-document]
|
||||
│ └── (dynamic fields)
|
||||
│
|
||||
└── fields/ [sub-document]
|
||||
└── (conditional mappings)
|
||||
├── {filterable string fields} [keyword, stored]
|
||||
└── {other fields} [dynamically mapped by Bleve]
|
||||
|
||||
Key observations:
|
||||
|
||||
- Root level has standard searchable fields (name, title, description, tags, folder)
|
||||
- title has 3 analyzers applied: standard (for word search), edge ngram (for prefix search), and keyword (for phrase sorting)
|
||||
- source/, manager/: Static sub-documents with explicitly mapped fields
|
||||
- reference/: Dynamic sub-document with keyword default analyzer (line 142)
|
||||
- labels/, fields/: Dynamic sub-documents where Bleve auto-detects field types at index time
|
||||
|
||||
References:
|
||||
- Main mapping function: pkg/storage/unified/search/bleve_mappings.go:25-169
|
||||
- Sub-document mappings: lines 88-143
|
||||
- Dynamic fields handling: lines 148-166
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDocumentMapping(t *testing.T) {
|
||||
mappings, err := search.GetBleveMappings(nil)
|
||||
mappings, err := search.GetBleveMappings(nil, nil)
|
||||
require.NoError(t, err)
|
||||
data := resource.IndexableDocument{
|
||||
Title: "title",
|
||||
|
||||
@@ -264,7 +264,7 @@ func (s *DashboardDocumentBuilder) BuildDocument(ctx context.Context, key *resou
|
||||
summary.UID = obj.GetName()
|
||||
summary.ID = obj.GetDeprecatedInternalID() // nolint:staticcheck
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
doc.Title = summary.Title
|
||||
doc.Description = summary.Description
|
||||
doc.Tags = summary.Tags
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestDashboardDocumentBuilder(t *testing.T) {
|
||||
"aaa",
|
||||
})
|
||||
|
||||
builder = resource.StandardDocumentBuilder()
|
||||
builder = resource.StandardDocumentBuilder(nil)
|
||||
doSnapshotTests(t, builder, "folder", &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "folder.grafana.app",
|
||||
|
||||
@@ -66,7 +66,7 @@ func (u *extGroupMappingDocumentBuilder) BuildDocument(ctx context.Context, key
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if extGroupMapping.Spec.TeamRef.Name != "" {
|
||||
|
||||
@@ -70,7 +70,7 @@ func (t *teamSearchBuilder) BuildDocument(ctx context.Context, key *resourcepb.R
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if team.Spec.Email != "" {
|
||||
|
||||
@@ -66,7 +66,7 @@ func (u *userDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if user.Spec.Email != "" {
|
||||
|
||||
@@ -25,7 +25,7 @@ func (s *StandardDocumentBuilders) GetDocumentBuilders() ([]resource.DocumentBui
|
||||
|
||||
result := []resource.DocumentBuilderInfo{
|
||||
{
|
||||
Builder: resource.StandardDocumentBuilder(),
|
||||
Builder: resource.StandardDocumentBuilder(resource.AppManifests()),
|
||||
},
|
||||
}
|
||||
return append(result, all...), nil
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
@@ -37,6 +38,7 @@ type ServerOptions struct {
|
||||
Tracer trace.Tracer
|
||||
Reg prometheus.Registerer
|
||||
AccessClient types.AccessClient
|
||||
SearchClient resourcepb.ResourceIndexClient
|
||||
SearchOptions resource.SearchOptions
|
||||
StorageMetrics *resource.StorageMetrics
|
||||
IndexMetrics *resource.BleveIndexMetrics
|
||||
@@ -115,6 +117,10 @@ func NewResourceServer(opts ServerOptions) (resource.ResourceServer, error) {
|
||||
serverOptions.Lifecycle = backend
|
||||
}
|
||||
|
||||
// use the search client when search isnt initialized. Dont need both.
|
||||
if opts.SearchOptions.Resources == nil {
|
||||
serverOptions.SearchClient = opts.SearchClient
|
||||
}
|
||||
serverOptions.Search = opts.SearchOptions
|
||||
serverOptions.IndexMetrics = opts.IndexMetrics
|
||||
serverOptions.QOSQueue = opts.QOSQueue
|
||||
|
||||
@@ -59,12 +59,13 @@ type service struct {
|
||||
subservicesWatcher *services.FailureWatcher
|
||||
hasSubservices bool
|
||||
|
||||
backend resource.StorageBackend
|
||||
cfg *setting.Cfg
|
||||
features featuremgmt.FeatureToggles
|
||||
db infraDB.DB
|
||||
stopCh chan struct{}
|
||||
stoppedCh chan error
|
||||
backend resource.StorageBackend
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
cfg *setting.Cfg
|
||||
features featuremgmt.FeatureToggles
|
||||
db infraDB.DB
|
||||
stopCh chan struct{}
|
||||
stoppedCh chan error
|
||||
|
||||
handler grpcserver.Provider
|
||||
|
||||
@@ -99,6 +100,7 @@ func ProvideUnifiedStorageGrpcService(
|
||||
memberlistKVConfig kv.Config,
|
||||
httpServerRouter *mux.Router,
|
||||
backend resource.StorageBackend,
|
||||
searchClient resourcepb.ResourceIndexClient,
|
||||
) (UnifiedStorageGrpcService, error) {
|
||||
var err error
|
||||
tracer := otel.Tracer("unified-storage")
|
||||
@@ -112,6 +114,7 @@ func ProvideUnifiedStorageGrpcService(
|
||||
|
||||
s := &service{
|
||||
backend: backend,
|
||||
searchClient: searchClient,
|
||||
cfg: cfg,
|
||||
features: features,
|
||||
stopCh: make(chan struct{}),
|
||||
@@ -272,6 +275,7 @@ func (s *service) starting(ctx context.Context) error {
|
||||
Tracer: s.tracing,
|
||||
Reg: s.reg,
|
||||
AccessClient: authzClient,
|
||||
SearchClient: s.searchClient,
|
||||
SearchOptions: searchOptions,
|
||||
StorageMetrics: s.storageMetrics,
|
||||
IndexMetrics: s.indexMetrics,
|
||||
|
||||
Reference in New Issue
Block a user