diff --git a/go.mod b/go.mod index e8ac930a4ca..59cc35cf90e 100644 --- a/go.mod +++ b/go.mod @@ -241,6 +241,7 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/FZambia/eagle v0.1.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect + github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect @@ -250,6 +251,7 @@ require ( github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/apache/thrift v0.21.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect @@ -397,6 +399,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/klauspost/asmfmt v1.3.2 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -416,6 +419,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/miekg/dns v1.1.62 // indirect + github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect + github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect diff --git a/go.sum b/go.sum index b4820083bad..7ce560e11a0 100644 --- a/go.sum +++ b/go.sum @@ -708,6 +708,7 @@ github.com/FZambia/eagle v0.1.0 h1:9gyX6x+xjoIfglgyPTcYm7dvY7FJ93us1QY5De4CyXA= github.com/FZambia/eagle v0.1.0/go.mod h1:YjGSPVkQTNcVLfzEUQJNgW9ScPR0K4u/Ky0yeFa4oDA= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index a144f4491fd..e977deb84eb 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -144,6 +144,11 @@ var adminCommands = []*cli.Command{ Usage: "Migrates passwords from unsecured fields to secure_json_data field. Return ok unless there is an error. Safe to execute multiple times.", Action: runDbCommand(datamigrations.EncryptDatasourcePasswords), }, + { + Name: "to-unified-storage", + Usage: "Migrates classic SQL data into unified storage", + Action: runDbCommand(datamigrations.ToUnifiedStorage), + }, }, }, { diff --git a/pkg/cmd/grafana-cli/commands/datamigrations/stubs.go b/pkg/cmd/grafana-cli/commands/datamigrations/stubs.go new file mode 100644 index 00000000000..22dc15d6fc7 --- /dev/null +++ b/pkg/cmd/grafana-cli/commands/datamigrations/stubs.go @@ -0,0 +1,70 @@ +package datamigrations + +import ( + "context" + "path/filepath" + + "github.com/grafana/grafana/pkg/services/provisioning" + "github.com/grafana/grafana/pkg/services/provisioning/dashboards" +) + +var ( + _ provisioning.ProvisioningService = (*stubProvisioning)(nil) +) + +func newStubProvisioning(path string) (provisioning.ProvisioningService, error) { + cfgs, err := dashboards.ReadDashboardConfig(filepath.Join(path, "dashboards")) + if err != nil { + return nil, err + } + stub := &stubProvisioning{ + path: make(map[string]string), + } + for _, cfg := range cfgs { + stub.path[cfg.Name] = cfg.Options["path"].(string) + } + return &stubProvisioning{}, nil +} + +type stubProvisioning struct { + path map[string]string // name > options.path +} + +// GetAllowUIUpdatesFromConfig implements provisioning.ProvisioningService. +func (s *stubProvisioning) GetAllowUIUpdatesFromConfig(name string) bool { + return false +} + +func (s *stubProvisioning) GetDashboardProvisionerResolvedPath(name string) string { + return s.path[name] +} + +// ProvisionAlerting implements provisioning.ProvisioningService. +func (s *stubProvisioning) ProvisionAlerting(ctx context.Context) error { + panic("unimplemented") +} + +// ProvisionDashboards implements provisioning.ProvisioningService. +func (s *stubProvisioning) ProvisionDashboards(ctx context.Context) error { + panic("unimplemented") +} + +// ProvisionDatasources implements provisioning.ProvisioningService. +func (s *stubProvisioning) ProvisionDatasources(ctx context.Context) error { + panic("unimplemented") +} + +// ProvisionPlugins implements provisioning.ProvisioningService. +func (s *stubProvisioning) ProvisionPlugins(ctx context.Context) error { + panic("unimplemented") +} + +// Run implements provisioning.ProvisioningService. +func (s *stubProvisioning) Run(ctx context.Context) error { + panic("unimplemented") +} + +// RunInitProvisioners implements provisioning.ProvisioningService. +func (s *stubProvisioning) RunInitProvisioners(ctx context.Context) error { + panic("unimplemented") +} diff --git a/pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage.go b/pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage.go new file mode 100644 index 00000000000..6b6673a9742 --- /dev/null +++ b/pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage.go @@ -0,0 +1,207 @@ +package datamigrations + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strings" + "time" + + "github.com/prometheus/client_golang/prometheus" + "k8s.io/apimachinery/pkg/runtime/schema" + + dashboard "github.com/grafana/grafana/pkg/apis/dashboard" + folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" + + authlib "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" + "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/tracing" + "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/storage/legacysql" + "github.com/grafana/grafana/pkg/storage/unified" + "github.com/grafana/grafana/pkg/storage/unified/parquet" + "github.com/grafana/grafana/pkg/storage/unified/resource" +) + +// ToUnifiedStorage converts dashboards+folders into unified storage +func ToUnifiedStorage(c utils.CommandLine, cfg *setting.Cfg, sqlStore db.DB) error { + namespace := "default" // TODO... from command line + ns, err := authlib.ParseNamespace(namespace) + if err != nil { + return err + } + ctx := identity.WithServiceIdentityContext(context.Background(), ns.OrgID) + start := time.Now() + last := time.Now() + + opts := legacy.MigrateOptions{ + Namespace: namespace, + Resources: []schema.GroupResource{ + {Group: folders.GROUP, Resource: folders.RESOURCE}, + {Group: dashboard.GROUP, Resource: dashboard.DASHBOARD_RESOURCE}, + {Group: dashboard.GROUP, Resource: dashboard.LIBRARY_PANEL_RESOURCE}, + }, + LargeObjects: nil, // TODO... from config + Progress: func(count int, msg string) { + if count < 1 || time.Since(last) > time.Second { + fmt.Printf("[%4d] %s\n", count, msg) + last = time.Now() + } + }, + } + + provisioning, err := newStubProvisioning(cfg.ProvisioningPath) + if err != nil { + return err + } + + migrator := legacy.NewDashboardAccess( + legacysql.NewDatabaseProvider(sqlStore), + authlib.OrgNamespaceFormatter, + nil, provisioning, false, + ) + + yes, err := promptYesNo(fmt.Sprintf("Count legacy resources for namespace: %s?", opts.Namespace)) + if err != nil { + return err + } + if yes { + opts.OnlyCount = true + rsp, err := migrator.Migrate(ctx, opts) + if err != nil { + return err + } + + fmt.Printf("Counting DONE: %s\n", time.Since(start)) + if rsp != nil { + jj, _ := json.MarshalIndent(rsp, "", " ") + fmt.Printf("%s\n", string(jj)) + } + } + + opts.OnlyCount = false + opts.WithHistory, err = promptYesNo("Include history in exports?") + if err != nil { + return err + } + + yes, err = promptYesNo("Export legacy resources to parquet file?") + if err != nil { + return err + } + if yes { + file, err := os.CreateTemp(cfg.DataPath, "grafana-export-*.parquet") + if err != nil { + return err + } + start = time.Now() + last = time.Now() + opts.Store, err = newParquetClient(file) + if err != nil { + return err + } + rsp, err := migrator.Migrate(ctx, opts) + if err != nil { + return err + } + fmt.Printf("Parquet export DONE: %s\n", time.Since(start)) + if rsp != nil { + jj, _ := json.MarshalIndent(rsp, "", " ") + fmt.Printf("%s\n", string(jj)) + } + fmt.Printf("File: %s\n", file.Name()) + } + + yes, err = promptYesNo("Export legacy resources to unified storage?") + if err != nil { + return err + } + if yes { + client, err := newUnifiedClient(cfg, sqlStore) + if err != nil { + return err + } + + // Check the stats (eventually compare) + req := &resource.ResourceStatsRequest{ + Namespace: opts.Namespace, + } + for _, r := range opts.Resources { + req.Kinds = append(req.Kinds, fmt.Sprintf("%s/%s", r.Group, r.Resource)) + } + + stats, err := client.GetStats(ctx, req) + if err != nil { + return err + } + + if stats != nil { + fmt.Printf("Existing resources in unified storage:\n") + jj, _ := json.MarshalIndent(stats, "", " ") + fmt.Printf("%s\n", string(jj)) + } + + yes, err = promptYesNo("Would you like to continue? (existing resources will be replaced)") + if err != nil { + return err + } + if yes { + start = time.Now() + last = time.Now() + opts.Store = client + opts.BlobStore = client + rsp, err := migrator.Migrate(ctx, opts) + if err != nil { + return err + } + fmt.Printf("Unified storage export: %s\n", time.Since(start)) + if rsp != nil { + jj, _ := json.MarshalIndent(rsp, "", " ") + fmt.Printf("%s\n", string(jj)) + } + } + } + return nil +} + +func promptYesNo(prompt string) (bool, error) { + line := "" + for { + fmt.Printf("%s (Y/N) >", prompt) + _, err := fmt.Scanln(&line) + if err != nil && err.Error() != "unexpected newline" { + return false, err + } + switch strings.ToLower(line) { + case "y", "yes": + return true, nil + case "n", "no": + return false, nil + } + } +} + +func newUnifiedClient(cfg *setting.Cfg, sqlStore db.DB) (resource.ResourceClient, error) { + return unified.ProvideUnifiedStorageClient(cfg, + featuremgmt.WithFeatures(), // none?? + sqlStore, + tracing.NewNoopTracerService(), + prometheus.NewPedanticRegistry(), + authlib.FixedAccessClient(true), // always true! + nil, // document supplier + ) +} + +func newParquetClient(file *os.File) (resource.BatchStoreClient, error) { + writer, err := parquet.NewParquetWriter(file) + if err != nil { + return nil, err + } + client := parquet.NewBatchResourceWriterClient(writer) + return client, nil +} diff --git a/pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage_test.go b/pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage_test.go new file mode 100644 index 00000000000..1fdbfc7b026 --- /dev/null +++ b/pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage_test.go @@ -0,0 +1,29 @@ +package datamigrations + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/infra/db" +) + +func TestUnifiedStorageCommand(t *testing.T) { + // setup datasources with password, basic_auth and none + store := db.InitTestDB(t) + err := store.WithDbSession(context.Background(), func(sess *db.Session) error { + unistoreMigrationTest(t, sess, store) + return nil + }) + require.NoError(t, err) +} + +func unistoreMigrationTest(t *testing.T, session *db.Session, sqlstore db.DB) { + // empty stats + + t.Run("get stats", func(t *testing.T) { + fmt.Printf("TODO... add folders and check that they migrate\n") + }) +} diff --git a/pkg/registry/apis/dashboard/legacy/client.go b/pkg/registry/apis/dashboard/legacy/client.go index eee92598a15..77dc173b69f 100644 --- a/pkg/registry/apis/dashboard/legacy/client.go +++ b/pkg/registry/apis/dashboard/legacy/client.go @@ -87,5 +87,10 @@ func (d *directResourceClient) Update(ctx context.Context, in *resource.UpdateRe // Watch implements ResourceClient. func (d *directResourceClient) Watch(ctx context.Context, in *resource.WatchRequest, opts ...grpc.CallOption) (resource.ResourceStore_WatchClient, error) { - return nil, fmt.Errorf("watch not yet supported with direct resource client") + return nil, fmt.Errorf("watch not supported with direct resource client") +} + +// BatchProcess implements resource.ResourceClient. +func (d *directResourceClient) BatchProcess(ctx context.Context, opts ...grpc.CallOption) (resource.BatchStore_BatchProcessClient, error) { + return nil, fmt.Errorf("BatchProcess not supported with direct resource client") } diff --git a/pkg/registry/apis/dashboard/legacy/migrate.go b/pkg/registry/apis/dashboard/legacy/migrate.go new file mode 100644 index 00000000000..c1236ef4f2d --- /dev/null +++ b/pkg/registry/apis/dashboard/legacy/migrate.go @@ -0,0 +1,411 @@ +package legacy + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + + "google.golang.org/grpc/metadata" + "k8s.io/apimachinery/pkg/runtime/schema" + + authlib "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/utils" + dashboard "github.com/grafana/grafana/pkg/apis/dashboard" + folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/storage/unified/apistore" + "github.com/grafana/grafana/pkg/storage/unified/resource" +) + +type MigrateOptions struct { + Namespace string + Store resource.BatchStoreClient + Writer resource.BatchResourceWriter + LargeObjects apistore.LargeObjectSupport + BlobStore resource.BlobStoreClient + Resources []schema.GroupResource + WithHistory bool // only applies to dashboards + OnlyCount bool // just count the values + Progress func(count int, msg string) +} + +// Read from legacy and write into unified storage +type LegacyMigrator interface { + Migrate(ctx context.Context, opts MigrateOptions) (*resource.BatchResponse, error) +} + +type BlobStoreInfo struct { + Count int64 + Size int64 +} + +// migrate function -- works for a single kind +type migrator = func(ctx context.Context, orgId int64, opts MigrateOptions, stream resource.BatchStore_BatchProcessClient) (*BlobStoreInfo, error) + +func (a *dashboardSqlAccess) Migrate(ctx context.Context, opts MigrateOptions) (*resource.BatchResponse, error) { + info, err := authlib.ParseNamespace(opts.Namespace) + if err != nil { + return nil, err + } + + // Migrate everything + if len(opts.Resources) < 1 { + return nil, fmt.Errorf("missing resource selector") + } + + migrators := []migrator{} + settings := resource.BatchSettings{ + RebuildCollection: true, + SkipValidation: true, + } + + for _, res := range opts.Resources { + switch fmt.Sprintf("%s/%s", res.Group, res.Resource) { + case "folder.grafana.app/folders": + migrators = append(migrators, a.migrateFolders) + settings.Collection = append(settings.Collection, &resource.ResourceKey{ + Namespace: opts.Namespace, + Group: folders.GROUP, + Resource: folders.RESOURCE, + }) + + case "dashboard.grafana.app/librarypanels": + migrators = append(migrators, a.migratePanels) + settings.Collection = append(settings.Collection, &resource.ResourceKey{ + Namespace: opts.Namespace, + Group: dashboard.GROUP, + Resource: dashboard.LIBRARY_PANEL_RESOURCE, + }) + + case "dashboard.grafana.app/dashboards": + migrators = append(migrators, a.migrateDashboards) + settings.Collection = append(settings.Collection, &resource.ResourceKey{ + Namespace: opts.Namespace, + Group: dashboard.GROUP, + Resource: dashboard.DASHBOARD_RESOURCE, + }) + default: + return nil, fmt.Errorf("unsupported resource: %s", res) + } + } + + if opts.OnlyCount { + return a.countValues(ctx, opts) + } + + ctx = metadata.NewOutgoingContext(ctx, settings.ToMD()) + stream, err := opts.Store.BatchProcess(ctx) + if err != nil { + return nil, err + } + + // Now run each migration + blobStore := BlobStoreInfo{} + for _, m := range migrators { + blobs, err := m(ctx, info.OrgID, opts, stream) + if err != nil { + return nil, err + } + if blobs != nil { + blobStore.Count += blobs.Count + blobStore.Size += blobs.Size + } + } + fmt.Printf("BLOBS: %+v\n", blobStore) + return stream.CloseAndRecv() +} + +func (a *dashboardSqlAccess) countValues(ctx context.Context, opts MigrateOptions) (*resource.BatchResponse, error) { + sql, err := a.sql(ctx) + if err != nil { + return nil, err + } + ns, err := authlib.ParseNamespace(opts.Namespace) + if err != nil { + return nil, err + } + orgId := ns.OrgID + rsp := &resource.BatchResponse{} + err = sql.DB.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { + for _, res := range opts.Resources { + switch fmt.Sprintf("%s/%s", res.Group, res.Resource) { + case "folder.grafana.app/folders": + summary := &resource.BatchResponse_Summary{} + summary.Group = folders.GROUP + summary.Group = folders.RESOURCE + _, err = sess.SQL("SELECT COUNT(*) FROM "+sql.Table("dashboard")+ + " WHERE is_folder=FALSE AND org_id=?", orgId).Get(&summary.Count) + rsp.Summary = append(rsp.Summary, summary) + + case "dashboard.grafana.app/librarypanels": + summary := &resource.BatchResponse_Summary{} + summary.Group = dashboard.GROUP + summary.Resource = dashboard.LIBRARY_PANEL_RESOURCE + _, err = sess.SQL("SELECT COUNT(*) FROM "+sql.Table("library_element")+ + " WHERE org_id=?", orgId).Get(&summary.Count) + rsp.Summary = append(rsp.Summary, summary) + + case "dashboard.grafana.app/dashboards": + summary := &resource.BatchResponse_Summary{} + summary.Group = dashboard.GROUP + summary.Resource = dashboard.DASHBOARD_RESOURCE + rsp.Summary = append(rsp.Summary, summary) + + _, err = sess.SQL("SELECT COUNT(*) FROM "+sql.Table("dashboard")+ + " WHERE is_folder=FALSE AND org_id=?", orgId).Get(&summary.Count) + if err != nil { + return err + } + + // Also count history + _, err = sess.SQL(`SELECT COUNT(*) + FROM `+sql.Table("dashboard_version")+` as dv + JOIN `+sql.Table("dashboard")+` as dd + ON dd.id = dv.dashboard_id + WHERE org_id=?`, orgId).Get(&summary.History) + } + if err != nil { + return err + } + } + return nil + }) + return rsp, nil +} + +func (a *dashboardSqlAccess) migrateDashboards(ctx context.Context, orgId int64, opts MigrateOptions, stream resource.BatchStore_BatchProcessClient) (*BlobStoreInfo, error) { + query := &DashboardQuery{ + OrgID: orgId, + Limit: 100000000, + GetHistory: opts.WithHistory, // include history + } + + blobs := &BlobStoreInfo{} + sql, err := a.sql(ctx) + if err != nil { + return blobs, err + } + + opts.Progress(-1, "migrating dashboards...") + rows, err := a.getRows(ctx, sql, query) + if rows != nil { + defer func() { + _ = rows.Close() + }() + } + if err != nil { + return blobs, err + } + + large := opts.LargeObjects + + // Now send each dashboard + for i := 1; rows.Next(); i++ { + dash := rows.row.Dash + dash.APIVersion = fmt.Sprintf("%s/v0alpha1", dashboard.GROUP) // << eventually v0 + dash.SetNamespace(opts.Namespace) + dash.SetResourceVersion("") // it will be filled in by the backend + + body, err := json.Marshal(dash) + if err != nil { + err = fmt.Errorf("error reading json from: %s // %w", rows.row.Dash.Name, err) + return blobs, err + } + + req := &resource.BatchRequest{ + Key: &resource.ResourceKey{ + Namespace: opts.Namespace, + Group: dashboard.GROUP, + Resource: dashboard.DASHBOARD_RESOURCE, + Name: rows.Name(), + }, + Value: body, + Folder: rows.row.FolderUID, + Action: resource.BatchRequest_ADDED, + } + if dash.Generation > 1 { + req.Action = resource.BatchRequest_MODIFIED + } else if dash.Generation < 0 { + req.Action = resource.BatchRequest_DELETED + } + + // With large object support + if large != nil && len(body) > large.Threshold() { + obj, err := utils.MetaAccessor(dash) + if err != nil { + return blobs, err + } + + opts.Progress(i, fmt.Sprintf("[v:%d] %s Large object (%d)", dash.Generation, dash.Name, len(body))) + err = large.Deconstruct(ctx, req.Key, opts.BlobStore, obj, req.Value) + if err != nil { + return blobs, err + } + + // The smaller version (most of spec removed) + req.Value, err = json.Marshal(dash) + if err != nil { + return blobs, err + } + blobs.Count++ + blobs.Size += int64(len(body)) + } + + opts.Progress(i, fmt.Sprintf("[v:%2d] %s (size:%d / %d|%d)", dash.Generation, dash.Name, len(req.Value), i, rows.count)) + + err = stream.Send(req) + if err != nil { + if errors.Is(err, io.EOF) { + opts.Progress(i, fmt.Sprintf("stream EOF/cancelled. index=%d", i)) + err = nil + } + return blobs, err + } + } + + if len(rows.rejected) > 0 { + for _, row := range rows.rejected { + id := row.Dash.Labels[utils.LabelKeyDeprecatedInternalID] + fmt.Printf("REJECTED: %s / %s\n", id, row.Dash.Name) + opts.Progress(-2, fmt.Sprintf("rejected: id:%s, uid:%s", id, row.Dash.Name)) + } + } + + if rows.Error() != nil { + return blobs, rows.Error() + } + + opts.Progress(-2, fmt.Sprintf("finished dashboards... (%d)", rows.count)) + return blobs, err +} + +func (a *dashboardSqlAccess) migrateFolders(ctx context.Context, orgId int64, opts MigrateOptions, stream resource.BatchStore_BatchProcessClient) (*BlobStoreInfo, error) { + query := &DashboardQuery{ + OrgID: orgId, + Limit: 100000000, + GetFolders: true, + } + + sql, err := a.sql(ctx) + if err != nil { + return nil, err + } + + opts.Progress(-1, "migrating folders...") + rows, err := a.getRows(ctx, sql, query) + if rows != nil { + defer func() { + _ = rows.Close() + }() + } + if err != nil { + return nil, err + } + + // Now send each dashboard + for i := 1; rows.Next(); i++ { + dash := rows.row.Dash + dash.APIVersion = "folder.grafana.app/v0alpha1" + dash.Kind = "Folder" + dash.SetNamespace(opts.Namespace) + dash.SetResourceVersion("") // it will be filled in by the backend + + spec := map[string]any{ + "title": dash.Spec.Object["title"], + } + description := dash.Spec.Object["description"] + if description != nil { + spec["description"] = description + } + dash.Spec.Object = spec + + body, err := json.Marshal(dash) + if err != nil { + return nil, err + } + + req := &resource.BatchRequest{ + Key: &resource.ResourceKey{ + Namespace: opts.Namespace, + Group: "folder.grafana.app", + Resource: "folders", + Name: rows.Name(), + }, + Value: body, + Folder: rows.row.FolderUID, + Action: resource.BatchRequest_ADDED, + } + if dash.Generation > 1 { + req.Action = resource.BatchRequest_MODIFIED + } else if dash.Generation < 0 { + req.Action = resource.BatchRequest_DELETED + } + + opts.Progress(i, fmt.Sprintf("[v:%d] %s (%d)", dash.Generation, dash.Name, len(req.Value))) + + err = stream.Send(req) + if err != nil { + if errors.Is(err, io.EOF) { + err = nil + } + return nil, err + } + } + + if rows.Error() != nil { + return nil, rows.Error() + } + + opts.Progress(-2, fmt.Sprintf("finished folders... (%d)", rows.count)) + return nil, err +} + +func (a *dashboardSqlAccess) migratePanels(ctx context.Context, orgId int64, opts MigrateOptions, stream resource.BatchStore_BatchProcessClient) (*BlobStoreInfo, error) { + opts.Progress(-1, "migrating library panels...") + panels, err := a.GetLibraryPanels(ctx, LibraryPanelQuery{ + OrgID: orgId, + Limit: 1000000, + }) + if err != nil { + return nil, err + } + for i, panel := range panels.Items { + meta, err := utils.MetaAccessor(&panel) + if err != nil { + return nil, err + } + body, err := json.Marshal(panel) + if err != nil { + return nil, err + } + + req := &resource.BatchRequest{ + Key: &resource.ResourceKey{ + Namespace: opts.Namespace, + Group: dashboard.GROUP, + Resource: dashboard.LIBRARY_PANEL_RESOURCE, + Name: panel.Name, + }, + Value: body, + Folder: meta.GetFolder(), + Action: resource.BatchRequest_ADDED, + } + if panel.Generation > 1 { + req.Action = resource.BatchRequest_MODIFIED + } + + opts.Progress(i, fmt.Sprintf("[v:%d] %s (%d)", i, meta.GetName(), len(req.Value))) + + err = stream.Send(req) + if err != nil { + if errors.Is(err, io.EOF) { + err = nil + } + return nil, err + } + } + opts.Progress(-2, fmt.Sprintf("finished panels... (%d)", len(panels.Items))) + return nil, nil +} diff --git a/pkg/registry/apis/dashboard/legacy/types.go b/pkg/registry/apis/dashboard/legacy/types.go index cb01bfe68bb..8a62ca0c8df 100644 --- a/pkg/registry/apis/dashboard/legacy/types.go +++ b/pkg/registry/apis/dashboard/legacy/types.go @@ -49,6 +49,7 @@ type LibraryPanelQuery struct { type DashboardAccess interface { resource.StorageBackend resource.ResourceIndexServer + LegacyMigrator GetDashboard(ctx context.Context, orgId int64, uid string, version int64) (*dashboard.Dashboard, int64, error) SaveDashboard(ctx context.Context, orgId int64, dash *dashboard.Dashboard) (*dashboard.Dashboard, bool, error) diff --git a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go index b97b2ea51e9..4421b441113 100644 --- a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go +++ b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go @@ -529,6 +529,9 @@ func (r resourceClientMock) List(ctx context.Context, in *resource.ListRequest, func (r resourceClientMock) Watch(ctx context.Context, in *resource.WatchRequest, opts ...grpc.CallOption) (resource.ResourceStore_WatchClient, error) { return nil, nil } +func (r resourceClientMock) BatchProcess(ctx context.Context, opts ...grpc.CallOption) (resource.BatchStore_BatchProcessClient, error) { + return nil, nil +} func (r resourceClientMock) Search(ctx context.Context, in *resource.ResourceSearchRequest, opts ...grpc.CallOption) (*resource.ResourceSearchResponse, error) { if len(in.Options.Labels) > 0 && in.Options.Labels[0].Key == utils.LabelKeyDeprecatedInternalID && diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index f5e31c31b38..61ab8c11119 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -47,6 +47,7 @@ require ( github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/BurntSushi/toml v1.4.0 // indirect + github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect @@ -57,8 +58,10 @@ require ( github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect github.com/Yiling-J/theine-go v0.6.0 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect + github.com/apache/thrift v0.21.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect @@ -218,6 +221,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 // indirect + github.com/klauspost/asmfmt v1.3.2 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -238,6 +242,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/miekg/dns v1.1.62 // indirect + github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect + github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 2820fd08a9b..9c7ca47a33a 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -87,6 +87,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -1052,6 +1054,8 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/pkg/storage/unified/parquet/README.md b/pkg/storage/unified/parquet/README.md new file mode 100644 index 00000000000..221d7a51f58 --- /dev/null +++ b/pkg/storage/unified/parquet/README.md @@ -0,0 +1,6 @@ +# Parquet Support + +This package implements a limited parquet backend that is currently only useful +as a pass-though buffer while batch writing values. + +Eventually this package could evolve into a full storage backend. \ No newline at end of file diff --git a/pkg/storage/unified/parquet/client.go b/pkg/storage/unified/parquet/client.go new file mode 100644 index 00000000000..3b573bdb1f5 --- /dev/null +++ b/pkg/storage/unified/parquet/client.go @@ -0,0 +1,78 @@ +package parquet + +import ( + "context" + "errors" + "fmt" + + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + "github.com/grafana/grafana/pkg/storage/unified/resource" +) + +var ( + _ resource.BatchStoreClient = (*writerClient)(nil) + _ resource.BatchStore_BatchProcessClient = (*writerClient)(nil) + + errUnimplemented = errors.New("not implemented (BatchResourceWriter as BatchStoreClient shim)") +) + +type writerClient struct { + writer resource.BatchResourceWriter + ctx context.Context +} + +// NewBatchResourceWriterClient wraps a BatchResourceWriter so that it can be used as a ResourceStoreClient +func NewBatchResourceWriterClient(writer resource.BatchResourceWriter) *writerClient { + return &writerClient{writer: writer} +} + +// Send implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) Send(req *resource.BatchRequest) error { + return w.writer.Write(w.ctx, req.Key, req.Value) +} + +// BatchProcess implements resource.ResourceStoreClient. +func (w *writerClient) BatchProcess(ctx context.Context, opts ...grpc.CallOption) (resource.BatchStore_BatchProcessClient, error) { + if w.ctx != nil { + return nil, fmt.Errorf("only one batch request supported") + } + w.ctx = ctx + return w, nil +} + +// CloseAndRecv implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) CloseAndRecv() (*resource.BatchResponse, error) { + return w.writer.CloseWithResults() +} + +// CloseSend implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) CloseSend() error { + return w.writer.Close() +} + +// Context implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) Context() context.Context { + return w.ctx +} + +// Header implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) Header() (metadata.MD, error) { + return nil, errUnimplemented +} + +// RecvMsg implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) RecvMsg(m any) error { + return errUnimplemented +} + +// SendMsg implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) SendMsg(m any) error { + return errUnimplemented +} + +// Trailer implements resource.ResourceStore_BatchProcessClient. +func (w *writerClient) Trailer() metadata.MD { + return nil +} diff --git a/pkg/storage/unified/parquet/reader.go b/pkg/storage/unified/parquet/reader.go new file mode 100644 index 00000000000..84695d07315 --- /dev/null +++ b/pkg/storage/unified/parquet/reader.go @@ -0,0 +1,264 @@ +package parquet + +import ( + "fmt" + + "github.com/apache/arrow-go/v18/parquet" + "github.com/apache/arrow-go/v18/parquet/file" + + "github.com/grafana/grafana/pkg/storage/unified/resource" +) + +var ( + _ resource.BatchRequestIterator = (*parquetReader)(nil) +) + +func NewParquetReader(inputPath string, batchSize int64) (resource.BatchRequestIterator, error) { + return newResourceReader(inputPath, batchSize) +} + +type parquetReader struct { + reader *file.Reader + + namespace *stringColumn + group *stringColumn + resource *stringColumn + name *stringColumn + value *stringColumn + folder *stringColumn + action *int32Column + columns []columnBuffer + + batchSize int64 + + defLevels []int16 + repLevels []int16 + + // how many we already read + bufferSize int + bufferIndex int + rowGroupIDX int + + req *resource.BatchRequest + err error +} + +// Next implements resource.BatchRequestIterator. +func (r *parquetReader) Next() bool { + r.req = nil + for r.err == nil && r.reader != nil { + if r.bufferIndex >= r.bufferSize && r.value.reader.HasNext() { + r.bufferIndex = 0 + r.err = r.readBatch() + if r.err != nil { + return false + } + r.bufferIndex = r.value.count + } + + if r.bufferSize > r.bufferIndex { + i := r.bufferIndex + r.bufferIndex++ + + r.req = &resource.BatchRequest{ + Key: &resource.ResourceKey{ + Group: r.group.buffer[i].String(), + Resource: r.resource.buffer[i].String(), + Namespace: r.namespace.buffer[i].String(), + Name: r.name.buffer[i].String(), + }, + Action: resource.BatchRequest_Action(r.action.buffer[i]), + Value: r.value.buffer[i].Bytes(), + Folder: r.folder.buffer[i].String(), + } + + return true + } + + r.rowGroupIDX++ + if r.rowGroupIDX >= r.reader.NumRowGroups() { + _ = r.reader.Close() + r.reader = nil + return false + } + r.err = r.open(r.reader.RowGroup(r.rowGroupIDX)) + } + + return false +} + +// Request implements resource.BatchRequestIterator. +func (r *parquetReader) Request() *resource.BatchRequest { + return r.req +} + +// RollbackRequested implements resource.BatchRequestIterator. +func (r *parquetReader) RollbackRequested() bool { + return r.err != nil +} + +func newResourceReader(inputPath string, batchSize int64) (*parquetReader, error) { + rdr, err := file.OpenParquetFile(inputPath, true) + if err != nil { + return nil, err + } + + schema := rdr.MetaData().Schema + makeColumn := func(name string) *stringColumn { + index := schema.ColumnIndexByName(name) + if index < 0 { + err = fmt.Errorf("missing column: %s", name) + } + return &stringColumn{ + index: index, + buffer: make([]parquet.ByteArray, batchSize), + } + } + + reader := &parquetReader{ + reader: rdr, + + namespace: makeColumn("namespace"), + group: makeColumn("group"), + resource: makeColumn("resource"), + name: makeColumn("name"), + value: makeColumn("value"), + folder: makeColumn("folder"), + + action: &int32Column{ + index: schema.ColumnIndexByName("action"), + buffer: make([]int32, batchSize), + }, + + batchSize: batchSize, + defLevels: make([]int16, batchSize), + repLevels: make([]int16, batchSize), + } + + if err != nil { + _ = rdr.Close() + return nil, err + } + + reader.columns = []columnBuffer{ + reader.namespace, + reader.group, + reader.resource, + reader.name, + reader.action, + reader.value, + } + + // Empty file, close and return + if rdr.NumRowGroups() < 1 { + err = rdr.Close() + reader.reader = nil + return reader, err + } + + err = reader.open(rdr.RowGroup(0)) + if err != nil { + _ = rdr.Close() + return nil, err + } + + // get the first batch + err = reader.readBatch() + if err != nil { + _ = rdr.Close() + return nil, err + } + + return reader, nil +} + +func (r *parquetReader) open(rgr *file.RowGroupReader) error { + for _, c := range r.columns { + err := c.open(rgr) + if err != nil { + return err + } + } + return nil +} + +func (r *parquetReader) readBatch() error { + r.bufferIndex = 0 + r.bufferSize = 0 + for i, c := range r.columns { + count, err := c.batch(r.batchSize, r.defLevels, r.repLevels) + if err != nil { + return err + } + if i > 0 && r.bufferSize != count { + return fmt.Errorf("expecting the same size for all columns") + } + r.bufferSize = count + } + return nil +} + +//------------------------------- +// Column support +//------------------------------- + +type columnBuffer interface { + open(rgr *file.RowGroupReader) error + batch(batchSize int64, defLevels []int16, repLevels []int16) (int, error) +} + +type stringColumn struct { + index int // within the schema + reader *file.ByteArrayColumnChunkReader + buffer []parquet.ByteArray + count int // the active count +} + +func (c *stringColumn) open(rgr *file.RowGroupReader) error { + tmp, err := rgr.Column(c.index) + if err != nil { + return err + } + var ok bool + c.reader, ok = tmp.(*file.ByteArrayColumnChunkReader) + if !ok { + return fmt.Errorf("expected resource strings") + } + return nil +} + +func (c *stringColumn) batch(batchSize int64, defLevels []int16, repLevels []int16) (int, error) { + _, count, err := c.reader.ReadBatch(batchSize, c.buffer, defLevels, repLevels) + c.count = count + return count, err +} + +type int32Column struct { + index int // within the schemna + reader *file.Int32ColumnChunkReader + buffer []int32 + count int // the active count +} + +func (c *int32Column) open(rgr *file.RowGroupReader) error { + tmp, err := rgr.Column(c.index) + if err != nil { + return err + } + var ok bool + c.reader, ok = tmp.(*file.Int32ColumnChunkReader) + if !ok { + return fmt.Errorf("expected resource strings") + } + return nil +} + +func (c *int32Column) batch(batchSize int64, defLevels []int16, repLevels []int16) (int, error) { + _, count, err := c.reader.ReadBatch(batchSize, c.buffer, defLevels, repLevels) + c.count = count + return count, err +} + +//------------------------------- +// Column support +//------------------------------- diff --git a/pkg/storage/unified/parquet/reader_test.go b/pkg/storage/unified/parquet/reader_test.go new file mode 100644 index 00000000000..e8414c36d74 --- /dev/null +++ b/pkg/storage/unified/parquet/reader_test.go @@ -0,0 +1,125 @@ +package parquet + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/storage/unified/resource" +) + +func TestParquetWriteThenRead(t *testing.T) { + t.Run("read-write-couple-rows", func(t *testing.T) { + file, err := os.CreateTemp(t.TempDir(), "temp-*.parquet") + require.NoError(t, err) + defer func() { _ = os.Remove(file.Name()) }() + + writer, err := NewParquetWriter(file) + require.NoError(t, err) + ctx := context.Background() + + require.NoError(t, writer.Write(toKeyAndBytes(ctx, "ggg", "rrr", &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "namespace": "ns", + "name": "aaa", + "resourceVersion": "1234", + "annotations": map[string]string{ + utils.AnnoKeyFolder: "xyz", + }, + }, + "spec": map[string]any{ + "hello": "first", + }, + }, + }))) + + require.NoError(t, writer.Write(toKeyAndBytes(ctx, "ggg", "rrr", &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "namespace": "ns", + "name": "bbb", + "resourceVersion": "5678", + "generation": -999, // deleted action + }, + "spec": map[string]any{ + "hello": "second", + }, + }, + }))) + + require.NoError(t, writer.Write(toKeyAndBytes(ctx, "ggg", "rrr", &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "namespace": "ns", + "name": "ccc", + "resourceVersion": "789", + "generation": 3, // modified action + }, + "spec": map[string]any{ + "hello": "thirt", + }, + }, + }))) + + res, err := writer.CloseWithResults() + require.NoError(t, err) + require.Equal(t, int64(3), res.Processed) + + var keys []string + reader, err := newResourceReader(file.Name(), 20) + require.NoError(t, err) + for reader.Next() { + req := reader.Request() + keys = append(keys, req.Key.SearchID()) + } + + // Verify that we read all values + require.Equal(t, []string{ + "rrr/ns/ggg/aaa", + "rrr/ns/ggg/bbb", + "rrr/ns/ggg/ccc", + }, keys) + }) + + t.Run("read-write-empty-db", func(t *testing.T) { + file, err := os.CreateTemp(t.TempDir(), "temp-*.parquet") + require.NoError(t, err) + defer func() { _ = os.Remove(file.Name()) }() + + writer, err := NewParquetWriter(file) + require.NoError(t, err) + err = writer.Close() + require.NoError(t, err) + + var keys []string + reader, err := newResourceReader(file.Name(), 20) + require.NoError(t, err) + for reader.Next() { + req := reader.Request() + keys = append(keys, req.Key.SearchID()) + } + require.NoError(t, reader.err) + require.Empty(t, keys) + }) +} + +func toKeyAndBytes(ctx context.Context, group string, res string, obj *unstructured.Unstructured) (context.Context, *resource.ResourceKey, []byte) { + if obj.GetKind() == "" { + obj.SetKind(res) + } + if obj.GetAPIVersion() == "" { + obj.SetAPIVersion(group + "/vXyz") + } + data, _ := obj.MarshalJSON() + return ctx, &resource.ResourceKey{ + Namespace: obj.GetNamespace(), + Resource: res, + Group: group, + Name: obj.GetName(), + }, data +} diff --git a/pkg/storage/unified/parquet/writer.go b/pkg/storage/unified/parquet/writer.go new file mode 100644 index 00000000000..421946b71a7 --- /dev/null +++ b/pkg/storage/unified/parquet/writer.go @@ -0,0 +1,209 @@ +package parquet + +import ( + "context" + "io" + + "github.com/apache/arrow-go/v18/arrow" + "github.com/apache/arrow-go/v18/arrow/array" + "github.com/apache/arrow-go/v18/arrow/memory" + "github.com/apache/arrow-go/v18/parquet" + "github.com/apache/arrow-go/v18/parquet/compress" + "github.com/apache/arrow-go/v18/parquet/pqarrow" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/grafana/grafana-app-sdk/logging" + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/storage/unified/resource" +) + +var ( + _ resource.BatchResourceWriter = (*parquetWriter)(nil) +) + +// Write resources into a parquet file +func NewParquetWriter(f io.Writer) (*parquetWriter, error) { + w := &parquetWriter{ + pool: memory.DefaultAllocator, + schema: newSchema(nil), + buffer: 1024 * 10 * 100 * 10, // 10MB + logger: logging.DefaultLogger.With("logger", "parquet.writer"), + rsp: &resource.BatchResponse{}, + summary: make(map[string]*resource.BatchResponse_Summary), + } + + props := parquet.NewWriterProperties( + parquet.WithCompression(compress.Codecs.Brotli), + ) + writer, err := pqarrow.NewFileWriter(w.schema, f, props, pqarrow.DefaultWriterProps()) + if err != nil { + return nil, err + } + w.writer = writer + return w, w.init() +} + +// ProcessBatch implements resource.BatchProcessingBackend. +func (w *parquetWriter) ProcessBatch(ctx context.Context, setting resource.BatchSettings, iter resource.BatchRequestIterator) *resource.BatchResponse { + defer func() { _ = w.Close() }() + + var err error + for iter.Next() { + if iter.RollbackRequested() { + break + } + + req := iter.Request() + + err = w.Write(ctx, req.Key, req.Value) + if err != nil { + break + } + } + + rsp, err := w.CloseWithResults() + if err != nil { + w.logger.Warn("error closing parquet file", "err", err) + } + if rsp == nil { + rsp = &resource.BatchResponse{} + } + if err != nil { + rsp.Error = resource.AsErrorResult(err) + } + return rsp +} + +type parquetWriter struct { + pool memory.Allocator + buffer int + wrote int + + schema *arrow.Schema + writer *pqarrow.FileWriter + logger logging.Logger + + rv *array.Int64Builder + namespace *array.StringBuilder + group *array.StringBuilder + resource *array.StringBuilder + name *array.StringBuilder + folder *array.StringBuilder + action *array.Int8Builder + value *array.StringBuilder + + rsp *resource.BatchResponse + summary map[string]*resource.BatchResponse_Summary +} + +func (w *parquetWriter) CloseWithResults() (*resource.BatchResponse, error) { + err := w.Close() + return w.rsp, err +} + +func (w *parquetWriter) Close() error { + if w.rv.Len() > 0 { + _ = w.flush() + } + w.logger.Info("close") + return w.writer.Close() +} + +// writes the current buffer to parquet and re-inits the arrow buffer +func (w *parquetWriter) flush() error { + w.logger.Info("flush", "count", w.rv.Len()) + rec := array.NewRecord(w.schema, []arrow.Array{ + w.rv.NewArray(), + w.namespace.NewArray(), + w.group.NewArray(), + w.resource.NewArray(), + w.name.NewArray(), + w.folder.NewArray(), + w.action.NewArray(), + w.value.NewArray(), + }, int64(w.rv.Len())) + defer rec.Release() + err := w.writer.Write(rec) + if err != nil { + return err + } + return w.init() +} + +func (w *parquetWriter) init() error { + w.rv = array.NewInt64Builder(w.pool) + w.namespace = array.NewStringBuilder(w.pool) + w.group = array.NewStringBuilder(w.pool) + w.resource = array.NewStringBuilder(w.pool) + w.name = array.NewStringBuilder(w.pool) + w.folder = array.NewStringBuilder(w.pool) + w.action = array.NewInt8Builder(w.pool) + w.value = array.NewStringBuilder(w.pool) + w.wrote = 0 + return nil +} + +func (w *parquetWriter) Write(ctx context.Context, key *resource.ResourceKey, value []byte) error { + w.rsp.Processed++ + obj := &unstructured.Unstructured{} + err := obj.UnmarshalJSON(value) + if err != nil { + return err + } + meta, err := utils.MetaAccessor(obj) + if err != nil { + return err + } + rv, _ := meta.GetResourceVersionInt64() // it can be empty + + w.rv.Append(rv) + w.namespace.Append(key.Namespace) + w.group.Append(key.Group) + w.resource.Append(key.Resource) + w.name.Append(key.Name) + w.folder.Append(meta.GetFolder()) + w.value.Append(string(value)) + + var action resource.WatchEvent_Type + switch meta.GetGeneration() { + case 0, 1: + action = resource.WatchEvent_ADDED + case utils.DeletedGeneration: + action = resource.WatchEvent_DELETED + default: + action = resource.WatchEvent_MODIFIED + } + w.action.Append(int8(action)) + + w.wrote = w.wrote + len(value) + if w.wrote > w.buffer { + w.logger.Info("buffer full", "buffer", w.wrote, "max", w.buffer) + return w.flush() + } + + summary := w.summary[key.BatchID()] + if summary == nil { + summary = &resource.BatchResponse_Summary{ + Namespace: key.Namespace, + Group: key.Group, + Resource: key.Resource, + } + w.summary[key.BatchID()] = summary + w.rsp.Summary = append(w.rsp.Summary, summary) + } + summary.Count++ + return nil +} + +func newSchema(metadata *arrow.Metadata) *arrow.Schema { + return arrow.NewSchema([]arrow.Field{ + {Name: "resource_version", Type: &arrow.Int64Type{}, Nullable: false}, + {Name: "group", Type: &arrow.StringType{}, Nullable: false}, + {Name: "resource", Type: &arrow.StringType{}, Nullable: false}, + {Name: "namespace", Type: &arrow.StringType{}, Nullable: false}, + {Name: "name", Type: &arrow.StringType{}, Nullable: false}, + {Name: "folder", Type: &arrow.StringType{}, Nullable: false}, + {Name: "action", Type: &arrow.Int8Type{}, Nullable: false}, // 1,2,3 + {Name: "value", Type: &arrow.StringType{}, Nullable: false}, + }, metadata) +} diff --git a/pkg/storage/unified/resource/batch.go b/pkg/storage/unified/resource/batch.go new file mode 100644 index 00000000000..4997706a5e1 --- /dev/null +++ b/pkg/storage/unified/resource/batch.go @@ -0,0 +1,297 @@ +package resource + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + + "google.golang.org/grpc/metadata" + + authlib "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/utils" +) + +const grpcMetaKeyCollection = "x-gf-batch-collection" +const grpcMetaKeyRebuildCollection = "x-gf-batch-rebuild-collection" +const grpcMetaKeySkipValidation = "x-gf-batch-skip-validation" + +func grpcMetaValueIsTrue(vals []string) bool { + return len(vals) == 1 && vals[0] == "true" +} + +type BatchRequestIterator interface { + Next() bool + + // The next event we should process + Request() *BatchRequest + + // Rollback requested + RollbackRequested() bool +} + +type BatchProcessingBackend interface { + ProcessBatch(ctx context.Context, setting BatchSettings, iter BatchRequestIterator) *BatchResponse +} + +type BatchResourceWriter interface { + io.Closer + + Write(ctx context.Context, key *ResourceKey, value []byte) error + + // Called when finished writing + CloseWithResults() (*BatchResponse, error) +} + +type BatchSettings struct { + // All requests will be within this namespace/group/resource + Collection []*ResourceKey + + // The batch will include everything from the collection + // - all existing values will be removed/replaced if the batch completes successfully + RebuildCollection bool + + // The byte[] payload and folder has already been validated - no need to decode and verify + SkipValidation bool +} + +func (x *BatchSettings) ToMD() metadata.MD { + md := make(metadata.MD) + if len(x.Collection) > 0 { + for _, v := range x.Collection { + md[grpcMetaKeyCollection] = append(md[grpcMetaKeyCollection], v.SearchID()) + } + } + if x.RebuildCollection { + md[grpcMetaKeyRebuildCollection] = []string{"true"} + } + if x.SkipValidation { + md[grpcMetaKeySkipValidation] = []string{"true"} + } + return md +} + +func NewBatchSettings(md metadata.MD) (BatchSettings, error) { + settings := BatchSettings{} + for k, v := range md { + switch k { + case grpcMetaKeyCollection: + for _, c := range v { + key := &ResourceKey{} + err := key.ReadSearchID(c) + if err != nil { + return settings, fmt.Errorf("error reading collection metadata: %s / %w", c, err) + } + settings.Collection = append(settings.Collection, key) + } + case grpcMetaKeyRebuildCollection: + settings.RebuildCollection = grpcMetaValueIsTrue(v) + case grpcMetaKeySkipValidation: + settings.SkipValidation = grpcMetaValueIsTrue(v) + } + } + return settings, nil +} + +// BatchWrite implements ResourceServer. +// All requests must be to the same NAMESPACE/GROUP/RESOURCE +func (s *server) BatchProcess(stream BatchStore_BatchProcessServer) error { + ctx := stream.Context() + user, ok := authlib.AuthInfoFrom(ctx) + if !ok || user == nil { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "no user found in context", + Code: http.StatusUnauthorized, + }, + }) + } + + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "unable to read metadata gRPC request", + Code: http.StatusPreconditionFailed, + }, + }) + } + runner := &batchRunner{ + checker: make(map[string]authlib.ItemChecker), // Can create + stream: stream, + } + settings, err := NewBatchSettings(md) + if err != nil { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "error reading settings", + Reason: err.Error(), + Code: http.StatusPreconditionFailed, + }, + }) + } + + if len(settings.Collection) < 1 { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "Missing target collection(s) in request header", + Code: http.StatusBadRequest, + }, + }) + } + + // HACK!!! always allow everything!!!!!! + access := authlib.FixedAccessClient(true) + + if settings.RebuildCollection { + for _, k := range settings.Collection { + // Can we delete the whole collection + rsp, err := access.Check(ctx, user, authlib.CheckRequest{ + Namespace: k.Namespace, + Group: k.Group, + Resource: k.Resource, + Verb: utils.VerbDeleteCollection, + }) + if err != nil || !rsp.Allowed { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: fmt.Sprintf("Requester must be able to: %s", utils.VerbDeleteCollection), + Code: http.StatusForbidden, + }, + }) + } + + // This will be called for each request -- with the folder ID + runner.checker[k.BatchID()], err = access.Compile(ctx, user, authlib.ListRequest{ + Namespace: k.Namespace, + Group: k.Group, + Resource: k.Resource, + Verb: utils.VerbCreate, + }) + if err != nil { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "Unable to check `create` permission", + Code: http.StatusForbidden, + }, + }) + } + } + } else { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "Batch currently only supports RebuildCollection", + Code: http.StatusBadRequest, + }, + }) + } + + backend, ok := s.backend.(BatchProcessingBackend) + if !ok { + return stream.SendAndClose(&BatchResponse{ + Error: &ErrorResult{ + Message: "The server backend does not support batch processing", + Code: http.StatusNotImplemented, + }, + }) + } + + // BatchProcess requests + rsp := backend.ProcessBatch(ctx, settings, runner) + if rsp == nil { + rsp = &BatchResponse{ + Error: &ErrorResult{ + Code: http.StatusInternalServerError, + Message: "Nothing returned from process batch", + }, + } + } + if runner.err != nil { + rsp.Error = AsErrorResult(runner.err) + } + + if rsp.Error == nil && s.search != nil { + // Rebuild any changed indexes + for _, summary := range rsp.Summary { + _, _, err := s.search.build(ctx, NamespacedResource{ + Namespace: summary.Namespace, + Group: summary.Group, + Resource: summary.Resource, + }, summary.Count, summary.ResourceVersion) + if err != nil { + s.log.Warn("error building search index after batch load", "err", err) + rsp.Error = &ErrorResult{ + Code: http.StatusInternalServerError, + Message: "err building search index: " + summary.Resource, + Reason: err.Error(), + } + } + } + } + return stream.SendAndClose(rsp) +} + +var ( + _ BatchRequestIterator = (*batchRunner)(nil) +) + +type batchRunner struct { + stream BatchStore_BatchProcessServer + rollback bool + request *BatchRequest + err error + checker map[string]authlib.ItemChecker +} + +// Next implements BatchRequestIterator. +func (b *batchRunner) Next() bool { + if b.rollback { + return true + } + + b.request, b.err = b.stream.Recv() + if errors.Is(b.err, io.EOF) { + b.err = nil + b.rollback = false + b.request = nil + return false + } + + if b.err != nil { + b.rollback = true + return true + } + + if b.request != nil { + key := b.request.Key + k := key.BatchID() + checker, ok := b.checker[k] + if !ok { + b.err = fmt.Errorf("missing access control for: %s", k) + b.rollback = true + } else if !checker(key.Namespace, key.Name, b.request.Folder) { + b.err = fmt.Errorf("not allowed to create resource") + b.rollback = true + } + return true + } + return false +} + +// Request implements BatchRequestIterator. +func (b *batchRunner) Request() *BatchRequest { + if b.rollback { + return nil + } + return b.request +} + +// RollbackRequested implements BatchRequestIterator. +func (b *batchRunner) RollbackRequested() bool { + if b.rollback { + b.rollback = false // break iterator + return true + } + return false +} diff --git a/pkg/storage/unified/resource/client.go b/pkg/storage/unified/resource/client.go index bd67e68d428..a345bdd3376 100644 --- a/pkg/storage/unified/resource/client.go +++ b/pkg/storage/unified/resource/client.go @@ -25,6 +25,7 @@ type ResourceClient interface { ResourceStoreClient ResourceIndexClient RepositoryIndexClient + BatchStoreClient BlobStoreClient DiagnosticsClient } @@ -34,6 +35,7 @@ type resourceClient struct { ResourceStoreClient ResourceIndexClient RepositoryIndexClient + BatchStoreClient BlobStoreClient DiagnosticsClient } @@ -44,6 +46,7 @@ func NewLegacyResourceClient(channel *grpc.ClientConn) ResourceClient { ResourceStoreClient: NewResourceStoreClient(cc), ResourceIndexClient: NewResourceIndexClient(cc), RepositoryIndexClient: NewRepositoryIndexClient(cc), + BatchStoreClient: NewBatchStoreClient(cc), BlobStoreClient: NewBlobStoreClient(cc), DiagnosticsClient: NewDiagnosticsClient(cc), } @@ -59,6 +62,7 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient { &ResourceIndex_ServiceDesc, &RepositoryIndex_ServiceDesc, &BlobStore_ServiceDesc, + &BatchStore_ServiceDesc, &Diagnostics_ServiceDesc, } { channel.RegisterService( @@ -82,6 +86,7 @@ func NewLocalResourceClient(server ResourceServer) ResourceClient { ResourceStoreClient: NewResourceStoreClient(cc), ResourceIndexClient: NewResourceIndexClient(cc), RepositoryIndexClient: NewRepositoryIndexClient(cc), + BatchStoreClient: NewBatchStoreClient(cc), BlobStoreClient: NewBlobStoreClient(cc), DiagnosticsClient: NewDiagnosticsClient(cc), } @@ -101,10 +106,12 @@ func NewGRPCResourceClient(tracer tracing.Tracer, conn *grpc.ClientConn) (Resour cc := grpchan.InterceptClientConn(conn, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor) return &resourceClient{ - ResourceStoreClient: NewResourceStoreClient(cc), - ResourceIndexClient: NewResourceIndexClient(cc), - BlobStoreClient: NewBlobStoreClient(cc), - DiagnosticsClient: NewDiagnosticsClient(cc), + ResourceStoreClient: NewResourceStoreClient(cc), + ResourceIndexClient: NewResourceIndexClient(cc), + BlobStoreClient: NewBlobStoreClient(cc), + BatchStoreClient: NewBatchStoreClient(cc), + RepositoryIndexClient: NewRepositoryIndexClient(cc), + DiagnosticsClient: NewDiagnosticsClient(cc), }, nil } @@ -126,10 +133,12 @@ func NewCloudResourceClient(tracer tracing.Tracer, conn *grpc.ClientConn, cfg au cc := grpchan.InterceptClientConn(conn, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor) return &resourceClient{ - ResourceStoreClient: NewResourceStoreClient(cc), - ResourceIndexClient: NewResourceIndexClient(cc), - BlobStoreClient: NewBlobStoreClient(cc), - DiagnosticsClient: NewDiagnosticsClient(cc), + ResourceStoreClient: NewResourceStoreClient(cc), + ResourceIndexClient: NewResourceIndexClient(cc), + BlobStoreClient: NewBlobStoreClient(cc), + BatchStoreClient: NewBatchStoreClient(cc), + RepositoryIndexClient: NewRepositoryIndexClient(cc), + DiagnosticsClient: NewDiagnosticsClient(cc), }, nil } diff --git a/pkg/storage/unified/resource/keys.go b/pkg/storage/unified/resource/keys.go index 7e0c7e049ab..d323e107ea1 100644 --- a/pkg/storage/unified/resource/keys.go +++ b/pkg/storage/unified/resource/keys.go @@ -48,24 +48,43 @@ func (x *ResourceKey) SearchID() string { sb.WriteString(x.Group) sb.WriteString("/") sb.WriteString(x.Resource) - sb.WriteString("/") - sb.WriteString(x.Name) + if x.Name != "" { + sb.WriteString("/") + sb.WriteString(x.Name) + } return sb.String() } func (x *ResourceKey) ReadSearchID(v string) error { parts := strings.Split(v, "/") - if len(parts) != 4 { + if len(parts) < 3 { return fmt.Errorf("invalid search id (expecting 3 slashes)") } x.Namespace = parts[0] x.Group = parts[1] x.Resource = parts[2] - x.Name = parts[3] + if len(parts) > 3 { + x.Name = parts[3] + } if x.Namespace == clusterNamespace { x.Namespace = "" } return nil } + +// The namespace/group/resource +func (x *ResourceKey) BatchID() string { + var sb strings.Builder + if x.Namespace == "" { + sb.WriteString(clusterNamespace) + } else { + sb.WriteString(x.Namespace) + } + sb.WriteString("/") + sb.WriteString(x.Group) + sb.WriteString("/") + sb.WriteString(x.Resource) + return sb.String() +} diff --git a/pkg/storage/unified/resource/keys_test.go b/pkg/storage/unified/resource/keys_test.go index 0e9de7325f2..96d4da3555b 100644 --- a/pkg/storage/unified/resource/keys_test.go +++ b/pkg/storage/unified/resource/keys_test.go @@ -33,13 +33,20 @@ func TestSearchIDKeys(t *testing.T) { Resource: "resource", Name: "name", }}, - {input: "/group/resource/", // missing name + {input: "/group/resource/", expected: &ResourceKey{ Namespace: "", Group: "group", Resource: "resource", Name: "", }}, + {input: "default/group/resource", + expected: &ResourceKey{ + Namespace: "default", + Group: "group", + Resource: "resource", + Name: "", + }}, {input: "**cluster**/group/resource/aaa", // cluster namespace expected: &ResourceKey{ Namespace: "", diff --git a/pkg/storage/unified/resource/resource.pb.go b/pkg/storage/unified/resource/resource.pb.go index 0e72212de6f..ee8801f361b 100644 --- a/pkg/storage/unified/resource/resource.pb.go +++ b/pkg/storage/unified/resource/resource.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.1 +// protoc-gen-go v1.36.4 // protoc (unknown) // source: resource.proto @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -173,6 +174,60 @@ func (WatchEvent_Type) EnumDescriptor() ([]byte, []int) { return file_resource_proto_rawDescGZIP(), []int{18, 0} } +type BatchRequest_Action int32 + +const ( + // will be an error + BatchRequest_UNKNOWN BatchRequest_Action = 0 + // Matches Watch event enum + BatchRequest_ADDED BatchRequest_Action = 1 + BatchRequest_MODIFIED BatchRequest_Action = 2 + BatchRequest_DELETED BatchRequest_Action = 3 +) + +// Enum value maps for BatchRequest_Action. +var ( + BatchRequest_Action_name = map[int32]string{ + 0: "UNKNOWN", + 1: "ADDED", + 2: "MODIFIED", + 3: "DELETED", + } + BatchRequest_Action_value = map[string]int32{ + "UNKNOWN": 0, + "ADDED": 1, + "MODIFIED": 2, + "DELETED": 3, + } +) + +func (x BatchRequest_Action) Enum() *BatchRequest_Action { + p := new(BatchRequest_Action) + *p = x + return p +} + +func (x BatchRequest_Action) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BatchRequest_Action) Descriptor() protoreflect.EnumDescriptor { + return file_resource_proto_enumTypes[3].Descriptor() +} + +func (BatchRequest_Action) Type() protoreflect.EnumType { + return &file_resource_proto_enumTypes[3] +} + +func (x BatchRequest_Action) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BatchRequest_Action.Descriptor instead. +func (BatchRequest_Action) EnumDescriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{19, 0} +} + type HealthCheckResponse_ServingStatus int32 const ( @@ -209,11 +264,11 @@ func (x HealthCheckResponse_ServingStatus) String() string { } func (HealthCheckResponse_ServingStatus) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[3].Descriptor() + return file_resource_proto_enumTypes[4].Descriptor() } func (HealthCheckResponse_ServingStatus) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[3] + return &file_resource_proto_enumTypes[4] } func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber { @@ -222,7 +277,7 @@ func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber { // Deprecated: Use HealthCheckResponse_ServingStatus.Descriptor instead. func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{28, 0} + return file_resource_proto_rawDescGZIP(), []int{30, 0} } // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. @@ -284,11 +339,11 @@ func (x ResourceTableColumnDefinition_ColumnType) String() string { } func (ResourceTableColumnDefinition_ColumnType) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[4].Descriptor() + return file_resource_proto_enumTypes[5].Descriptor() } func (ResourceTableColumnDefinition_ColumnType) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[4] + return &file_resource_proto_enumTypes[5] } func (x ResourceTableColumnDefinition_ColumnType) Number() protoreflect.EnumNumber { @@ -297,7 +352,7 @@ func (x ResourceTableColumnDefinition_ColumnType) Number() protoreflect.EnumNumb // Deprecated: Use ResourceTableColumnDefinition_ColumnType.Descriptor instead. func (ResourceTableColumnDefinition_ColumnType) EnumDescriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{30, 0} + return file_resource_proto_rawDescGZIP(), []int{32, 0} } type PutBlobRequest_Method int32 @@ -332,11 +387,11 @@ func (x PutBlobRequest_Method) String() string { } func (PutBlobRequest_Method) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[5].Descriptor() + return file_resource_proto_enumTypes[6].Descriptor() } func (PutBlobRequest_Method) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[5] + return &file_resource_proto_enumTypes[6] } func (x PutBlobRequest_Method) Number() protoreflect.EnumNumber { @@ -345,7 +400,7 @@ func (x PutBlobRequest_Method) Number() protoreflect.EnumNumber { // Deprecated: Use PutBlobRequest_Method.Descriptor instead. func (PutBlobRequest_Method) EnumDescriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{34, 0} + return file_resource_proto_rawDescGZIP(), []int{36, 0} } type ResourceKey struct { @@ -1602,7 +1657,7 @@ type WatchEvent struct { state protoimpl.MessageState `protogen:"open.v1"` // Timestamp the event was sent Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // Timestamp the event was sent + // The event type Type WatchEvent_Type `protobuf:"varint,2,opt,name=type,proto3,enum=resource.WatchEvent_Type" json:"type,omitempty"` // Resource version for the object Resource *WatchEvent_Resource `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` @@ -1670,6 +1725,150 @@ func (x *WatchEvent) GetPrevious() *WatchEvent_Resource { return nil } +type BatchRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // NOTE everything in the same stream must share the same Namespace/Group/Resource + Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // Requested action + Action BatchRequest_Action `protobuf:"varint,2,opt,name=action,proto3,enum=resource.BatchRequest_Action" json:"action,omitempty"` + // The resource value + Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + // Hint that a new version will be written on-top of this + Folder string `protobuf:"bytes,4,opt,name=folder,proto3" json:"folder,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BatchRequest) Reset() { + *x = BatchRequest{} + mi := &file_resource_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchRequest) ProtoMessage() {} + +func (x *BatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchRequest.ProtoReflect.Descriptor instead. +func (*BatchRequest) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{19} +} + +func (x *BatchRequest) GetKey() *ResourceKey { + if x != nil { + return x.Key + } + return nil +} + +func (x *BatchRequest) GetAction() BatchRequest_Action { + if x != nil { + return x.Action + } + return BatchRequest_UNKNOWN +} + +func (x *BatchRequest) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *BatchRequest) GetFolder() string { + if x != nil { + return x.Folder + } + return "" +} + +type BatchResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Error details + Error *ErrorResult `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + // Total events processed + Processed int64 `protobuf:"varint,2,opt,name=processed,proto3" json:"processed,omitempty"` + // Summary status for the processed values + Summary []*BatchResponse_Summary `protobuf:"bytes,3,rep,name=summary,proto3" json:"summary,omitempty"` + // Rejected + Rejected []*BatchResponse_Rejected `protobuf:"bytes,4,rep,name=rejected,proto3" json:"rejected,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BatchResponse) Reset() { + *x = BatchResponse{} + mi := &file_resource_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchResponse) ProtoMessage() {} + +func (x *BatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchResponse.ProtoReflect.Descriptor instead. +func (*BatchResponse) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{20} +} + +func (x *BatchResponse) GetError() *ErrorResult { + if x != nil { + return x.Error + } + return nil +} + +func (x *BatchResponse) GetProcessed() int64 { + if x != nil { + return x.Processed + } + return 0 +} + +func (x *BatchResponse) GetSummary() []*BatchResponse_Summary { + if x != nil { + return x.Summary + } + return nil +} + +func (x *BatchResponse) GetRejected() []*BatchResponse_Rejected { + if x != nil { + return x.Rejected + } + return nil +} + // Get statistics across multiple resources // For these queries, we do not need authorization to see the actual values type ResourceStatsRequest struct { @@ -1688,7 +1887,7 @@ type ResourceStatsRequest struct { func (x *ResourceStatsRequest) Reset() { *x = ResourceStatsRequest{} - mi := &file_resource_proto_msgTypes[19] + mi := &file_resource_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1700,7 +1899,7 @@ func (x *ResourceStatsRequest) String() string { func (*ResourceStatsRequest) ProtoMessage() {} func (x *ResourceStatsRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[19] + mi := &file_resource_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1713,7 +1912,7 @@ func (x *ResourceStatsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceStatsRequest.ProtoReflect.Descriptor instead. func (*ResourceStatsRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{19} + return file_resource_proto_rawDescGZIP(), []int{21} } func (x *ResourceStatsRequest) GetNamespace() string { @@ -1749,7 +1948,7 @@ type ResourceStatsResponse struct { func (x *ResourceStatsResponse) Reset() { *x = ResourceStatsResponse{} - mi := &file_resource_proto_msgTypes[20] + mi := &file_resource_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1761,7 +1960,7 @@ func (x *ResourceStatsResponse) String() string { func (*ResourceStatsResponse) ProtoMessage() {} func (x *ResourceStatsResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[20] + mi := &file_resource_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1774,7 +1973,7 @@ func (x *ResourceStatsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceStatsResponse.ProtoReflect.Descriptor instead. func (*ResourceStatsResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{20} + return file_resource_proto_rawDescGZIP(), []int{22} } func (x *ResourceStatsResponse) GetError() *ErrorResult { @@ -1823,7 +2022,7 @@ type ResourceSearchRequest struct { func (x *ResourceSearchRequest) Reset() { *x = ResourceSearchRequest{} - mi := &file_resource_proto_msgTypes[21] + mi := &file_resource_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1835,7 +2034,7 @@ func (x *ResourceSearchRequest) String() string { func (*ResourceSearchRequest) ProtoMessage() {} func (x *ResourceSearchRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[21] + mi := &file_resource_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1848,7 +2047,7 @@ func (x *ResourceSearchRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSearchRequest.ProtoReflect.Descriptor instead. func (*ResourceSearchRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{21} + return file_resource_proto_rawDescGZIP(), []int{23} } func (x *ResourceSearchRequest) GetOptions() *ListOptions { @@ -1950,7 +2149,7 @@ type ResourceSearchResponse struct { func (x *ResourceSearchResponse) Reset() { *x = ResourceSearchResponse{} - mi := &file_resource_proto_msgTypes[22] + mi := &file_resource_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1962,7 +2161,7 @@ func (x *ResourceSearchResponse) String() string { func (*ResourceSearchResponse) ProtoMessage() {} func (x *ResourceSearchResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[22] + mi := &file_resource_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1975,7 +2174,7 @@ func (x *ResourceSearchResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSearchResponse.ProtoReflect.Descriptor instead. func (*ResourceSearchResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{22} + return file_resource_proto_rawDescGZIP(), []int{24} } func (x *ResourceSearchResponse) GetError() *ErrorResult { @@ -2043,7 +2242,7 @@ type ListRepositoryObjectsRequest struct { func (x *ListRepositoryObjectsRequest) Reset() { *x = ListRepositoryObjectsRequest{} - mi := &file_resource_proto_msgTypes[23] + mi := &file_resource_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2055,7 +2254,7 @@ func (x *ListRepositoryObjectsRequest) String() string { func (*ListRepositoryObjectsRequest) ProtoMessage() {} func (x *ListRepositoryObjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[23] + mi := &file_resource_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2068,7 +2267,7 @@ func (x *ListRepositoryObjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRepositoryObjectsRequest.ProtoReflect.Descriptor instead. func (*ListRepositoryObjectsRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{23} + return file_resource_proto_rawDescGZIP(), []int{25} } func (x *ListRepositoryObjectsRequest) GetNextPageToken() string { @@ -2106,7 +2305,7 @@ type ListRepositoryObjectsResponse struct { func (x *ListRepositoryObjectsResponse) Reset() { *x = ListRepositoryObjectsResponse{} - mi := &file_resource_proto_msgTypes[24] + mi := &file_resource_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2118,7 +2317,7 @@ func (x *ListRepositoryObjectsResponse) String() string { func (*ListRepositoryObjectsResponse) ProtoMessage() {} func (x *ListRepositoryObjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[24] + mi := &file_resource_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2131,7 +2330,7 @@ func (x *ListRepositoryObjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRepositoryObjectsResponse.ProtoReflect.Descriptor instead. func (*ListRepositoryObjectsResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{24} + return file_resource_proto_rawDescGZIP(), []int{26} } func (x *ListRepositoryObjectsResponse) GetItems() []*ListRepositoryObjectsResponse_Item { @@ -2169,7 +2368,7 @@ type CountRepositoryObjectsRequest struct { func (x *CountRepositoryObjectsRequest) Reset() { *x = CountRepositoryObjectsRequest{} - mi := &file_resource_proto_msgTypes[25] + mi := &file_resource_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2181,7 +2380,7 @@ func (x *CountRepositoryObjectsRequest) String() string { func (*CountRepositoryObjectsRequest) ProtoMessage() {} func (x *CountRepositoryObjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[25] + mi := &file_resource_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2194,7 +2393,7 @@ func (x *CountRepositoryObjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CountRepositoryObjectsRequest.ProtoReflect.Descriptor instead. func (*CountRepositoryObjectsRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{25} + return file_resource_proto_rawDescGZIP(), []int{27} } func (x *CountRepositoryObjectsRequest) GetNamespace() string { @@ -2224,7 +2423,7 @@ type CountRepositoryObjectsResponse struct { func (x *CountRepositoryObjectsResponse) Reset() { *x = CountRepositoryObjectsResponse{} - mi := &file_resource_proto_msgTypes[26] + mi := &file_resource_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2236,7 +2435,7 @@ func (x *CountRepositoryObjectsResponse) String() string { func (*CountRepositoryObjectsResponse) ProtoMessage() {} func (x *CountRepositoryObjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[26] + mi := &file_resource_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2249,7 +2448,7 @@ func (x *CountRepositoryObjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CountRepositoryObjectsResponse.ProtoReflect.Descriptor instead. func (*CountRepositoryObjectsResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{26} + return file_resource_proto_rawDescGZIP(), []int{28} } func (x *CountRepositoryObjectsResponse) GetItems() []*CountRepositoryObjectsResponse_ResourceCount { @@ -2275,7 +2474,7 @@ type HealthCheckRequest struct { func (x *HealthCheckRequest) Reset() { *x = HealthCheckRequest{} - mi := &file_resource_proto_msgTypes[27] + mi := &file_resource_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2287,7 +2486,7 @@ func (x *HealthCheckRequest) String() string { func (*HealthCheckRequest) ProtoMessage() {} func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[27] + mi := &file_resource_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2300,7 +2499,7 @@ func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. func (*HealthCheckRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{27} + return file_resource_proto_rawDescGZIP(), []int{29} } func (x *HealthCheckRequest) GetService() string { @@ -2319,7 +2518,7 @@ type HealthCheckResponse struct { func (x *HealthCheckResponse) Reset() { *x = HealthCheckResponse{} - mi := &file_resource_proto_msgTypes[28] + mi := &file_resource_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2331,7 +2530,7 @@ func (x *HealthCheckResponse) String() string { func (*HealthCheckResponse) ProtoMessage() {} func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[28] + mi := &file_resource_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2344,7 +2543,7 @@ func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. func (*HealthCheckResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{28} + return file_resource_proto_rawDescGZIP(), []int{30} } func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus { @@ -2385,7 +2584,7 @@ type ResourceTable struct { func (x *ResourceTable) Reset() { *x = ResourceTable{} - mi := &file_resource_proto_msgTypes[29] + mi := &file_resource_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2397,7 +2596,7 @@ func (x *ResourceTable) String() string { func (*ResourceTable) ProtoMessage() {} func (x *ResourceTable) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[29] + mi := &file_resource_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2410,7 +2609,7 @@ func (x *ResourceTable) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceTable.ProtoReflect.Descriptor instead. func (*ResourceTable) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{29} + return file_resource_proto_rawDescGZIP(), []int{31} } func (x *ResourceTable) GetColumns() []*ResourceTableColumnDefinition { @@ -2471,7 +2670,7 @@ type ResourceTableColumnDefinition struct { func (x *ResourceTableColumnDefinition) Reset() { *x = ResourceTableColumnDefinition{} - mi := &file_resource_proto_msgTypes[30] + mi := &file_resource_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2483,7 +2682,7 @@ func (x *ResourceTableColumnDefinition) String() string { func (*ResourceTableColumnDefinition) ProtoMessage() {} func (x *ResourceTableColumnDefinition) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[30] + mi := &file_resource_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2496,7 +2695,7 @@ func (x *ResourceTableColumnDefinition) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceTableColumnDefinition.ProtoReflect.Descriptor instead. func (*ResourceTableColumnDefinition) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{30} + return file_resource_proto_rawDescGZIP(), []int{32} } func (x *ResourceTableColumnDefinition) GetName() string { @@ -2564,7 +2763,7 @@ type ResourceTableRow struct { func (x *ResourceTableRow) Reset() { *x = ResourceTableRow{} - mi := &file_resource_proto_msgTypes[31] + mi := &file_resource_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2576,7 +2775,7 @@ func (x *ResourceTableRow) String() string { func (*ResourceTableRow) ProtoMessage() {} func (x *ResourceTableRow) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[31] + mi := &file_resource_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2589,7 +2788,7 @@ func (x *ResourceTableRow) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceTableRow.ProtoReflect.Descriptor instead. func (*ResourceTableRow) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{31} + return file_resource_proto_rawDescGZIP(), []int{33} } func (x *ResourceTableRow) GetKey() *ResourceKey { @@ -2632,7 +2831,7 @@ type RestoreRequest struct { func (x *RestoreRequest) Reset() { *x = RestoreRequest{} - mi := &file_resource_proto_msgTypes[32] + mi := &file_resource_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2644,7 +2843,7 @@ func (x *RestoreRequest) String() string { func (*RestoreRequest) ProtoMessage() {} func (x *RestoreRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[32] + mi := &file_resource_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2657,7 +2856,7 @@ func (x *RestoreRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreRequest.ProtoReflect.Descriptor instead. func (*RestoreRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{32} + return file_resource_proto_rawDescGZIP(), []int{34} } func (x *RestoreRequest) GetKey() *ResourceKey { @@ -2686,7 +2885,7 @@ type RestoreResponse struct { func (x *RestoreResponse) Reset() { *x = RestoreResponse{} - mi := &file_resource_proto_msgTypes[33] + mi := &file_resource_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2698,7 +2897,7 @@ func (x *RestoreResponse) String() string { func (*RestoreResponse) ProtoMessage() {} func (x *RestoreResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[33] + mi := &file_resource_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2711,7 +2910,7 @@ func (x *RestoreResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreResponse.ProtoReflect.Descriptor instead. func (*RestoreResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{33} + return file_resource_proto_rawDescGZIP(), []int{35} } func (x *RestoreResponse) GetError() *ErrorResult { @@ -2746,7 +2945,7 @@ type PutBlobRequest struct { func (x *PutBlobRequest) Reset() { *x = PutBlobRequest{} - mi := &file_resource_proto_msgTypes[34] + mi := &file_resource_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2758,7 +2957,7 @@ func (x *PutBlobRequest) String() string { func (*PutBlobRequest) ProtoMessage() {} func (x *PutBlobRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[34] + mi := &file_resource_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2771,7 +2970,7 @@ func (x *PutBlobRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PutBlobRequest.ProtoReflect.Descriptor instead. func (*PutBlobRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{34} + return file_resource_proto_rawDescGZIP(), []int{36} } func (x *PutBlobRequest) GetResource() *ResourceKey { @@ -2824,7 +3023,7 @@ type PutBlobResponse struct { func (x *PutBlobResponse) Reset() { *x = PutBlobResponse{} - mi := &file_resource_proto_msgTypes[35] + mi := &file_resource_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2836,7 +3035,7 @@ func (x *PutBlobResponse) String() string { func (*PutBlobResponse) ProtoMessage() {} func (x *PutBlobResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[35] + mi := &file_resource_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2849,7 +3048,7 @@ func (x *PutBlobResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PutBlobResponse.ProtoReflect.Descriptor instead. func (*PutBlobResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{35} + return file_resource_proto_rawDescGZIP(), []int{37} } func (x *PutBlobResponse) GetError() *ErrorResult { @@ -2914,7 +3113,7 @@ type GetBlobRequest struct { func (x *GetBlobRequest) Reset() { *x = GetBlobRequest{} - mi := &file_resource_proto_msgTypes[36] + mi := &file_resource_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2926,7 +3125,7 @@ func (x *GetBlobRequest) String() string { func (*GetBlobRequest) ProtoMessage() {} func (x *GetBlobRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[36] + mi := &file_resource_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2939,7 +3138,7 @@ func (x *GetBlobRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBlobRequest.ProtoReflect.Descriptor instead. func (*GetBlobRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{36} + return file_resource_proto_rawDescGZIP(), []int{38} } func (x *GetBlobRequest) GetResource() *ResourceKey { @@ -2981,7 +3180,7 @@ type GetBlobResponse struct { func (x *GetBlobResponse) Reset() { *x = GetBlobResponse{} - mi := &file_resource_proto_msgTypes[37] + mi := &file_resource_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2993,7 +3192,7 @@ func (x *GetBlobResponse) String() string { func (*GetBlobResponse) ProtoMessage() {} func (x *GetBlobResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[37] + mi := &file_resource_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3006,7 +3205,7 @@ func (x *GetBlobResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBlobResponse.ProtoReflect.Descriptor instead. func (*GetBlobResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{37} + return file_resource_proto_rawDescGZIP(), []int{39} } func (x *GetBlobResponse) GetError() *ErrorResult { @@ -3047,7 +3246,7 @@ type WatchEvent_Resource struct { func (x *WatchEvent_Resource) Reset() { *x = WatchEvent_Resource{} - mi := &file_resource_proto_msgTypes[38] + mi := &file_resource_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3059,7 +3258,7 @@ func (x *WatchEvent_Resource) String() string { func (*WatchEvent_Resource) ProtoMessage() {} func (x *WatchEvent_Resource) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[38] + mi := &file_resource_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3089,6 +3288,168 @@ func (x *WatchEvent_Resource) GetValue() []byte { return nil } +type BatchResponse_Summary struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Group string `protobuf:"bytes,2,opt,name=group,proto3" json:"group,omitempty"` + Resource string `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` + Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` + History int64 `protobuf:"varint,5,opt,name=history,proto3" json:"history,omitempty"` + ResourceVersion int64 `protobuf:"varint,6,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` // The max saved RV + // The previous count + PreviousCount int64 `protobuf:"varint,7,opt,name=previous_count,json=previousCount,proto3" json:"previous_count,omitempty"` + PreviousHistory int64 `protobuf:"varint,8,opt,name=previous_history,json=previousHistory,proto3" json:"previous_history,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BatchResponse_Summary) Reset() { + *x = BatchResponse_Summary{} + mi := &file_resource_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BatchResponse_Summary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchResponse_Summary) ProtoMessage() {} + +func (x *BatchResponse_Summary) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[41] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchResponse_Summary.ProtoReflect.Descriptor instead. +func (*BatchResponse_Summary) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{20, 0} +} + +func (x *BatchResponse_Summary) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *BatchResponse_Summary) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *BatchResponse_Summary) GetResource() string { + if x != nil { + return x.Resource + } + return "" +} + +func (x *BatchResponse_Summary) GetCount() int64 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *BatchResponse_Summary) GetHistory() int64 { + if x != nil { + return x.History + } + return 0 +} + +func (x *BatchResponse_Summary) GetResourceVersion() int64 { + if x != nil { + return x.ResourceVersion + } + return 0 +} + +func (x *BatchResponse_Summary) GetPreviousCount() int64 { + if x != nil { + return x.PreviousCount + } + return 0 +} + +func (x *BatchResponse_Summary) GetPreviousHistory() int64 { + if x != nil { + return x.PreviousHistory + } + return 0 +} + +// Collect a few invalid messages +type BatchResponse_Rejected struct { + state protoimpl.MessageState `protogen:"open.v1"` + Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Action BatchRequest_Action `protobuf:"varint,2,opt,name=action,proto3,enum=resource.BatchRequest_Action" json:"action,omitempty"` + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BatchResponse_Rejected) Reset() { + *x = BatchResponse_Rejected{} + mi := &file_resource_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BatchResponse_Rejected) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchResponse_Rejected) ProtoMessage() {} + +func (x *BatchResponse_Rejected) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[42] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchResponse_Rejected.ProtoReflect.Descriptor instead. +func (*BatchResponse_Rejected) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{20, 1} +} + +func (x *BatchResponse_Rejected) GetKey() *ResourceKey { + if x != nil { + return x.Key + } + return nil +} + +func (x *BatchResponse_Rejected) GetAction() BatchRequest_Action { + if x != nil { + return x.Action + } + return BatchRequest_UNKNOWN +} + +func (x *BatchResponse_Rejected) GetError() string { + if x != nil { + return x.Error + } + return "" +} + type ResourceStatsResponse_Stats struct { state protoimpl.MessageState `protogen:"open.v1"` // Resource group @@ -3103,7 +3464,7 @@ type ResourceStatsResponse_Stats struct { func (x *ResourceStatsResponse_Stats) Reset() { *x = ResourceStatsResponse_Stats{} - mi := &file_resource_proto_msgTypes[39] + mi := &file_resource_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3115,7 +3476,7 @@ func (x *ResourceStatsResponse_Stats) String() string { func (*ResourceStatsResponse_Stats) ProtoMessage() {} func (x *ResourceStatsResponse_Stats) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[39] + mi := &file_resource_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3128,7 +3489,7 @@ func (x *ResourceStatsResponse_Stats) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceStatsResponse_Stats.ProtoReflect.Descriptor instead. func (*ResourceStatsResponse_Stats) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{20, 0} + return file_resource_proto_rawDescGZIP(), []int{22, 0} } func (x *ResourceStatsResponse_Stats) GetGroup() string { @@ -3162,7 +3523,7 @@ type ResourceSearchRequest_Sort struct { func (x *ResourceSearchRequest_Sort) Reset() { *x = ResourceSearchRequest_Sort{} - mi := &file_resource_proto_msgTypes[40] + mi := &file_resource_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3174,7 +3535,7 @@ func (x *ResourceSearchRequest_Sort) String() string { func (*ResourceSearchRequest_Sort) ProtoMessage() {} func (x *ResourceSearchRequest_Sort) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[40] + mi := &file_resource_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3187,7 +3548,7 @@ func (x *ResourceSearchRequest_Sort) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSearchRequest_Sort.ProtoReflect.Descriptor instead. func (*ResourceSearchRequest_Sort) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{21, 0} + return file_resource_proto_rawDescGZIP(), []int{23, 0} } func (x *ResourceSearchRequest_Sort) GetField() string { @@ -3214,7 +3575,7 @@ type ResourceSearchRequest_Facet struct { func (x *ResourceSearchRequest_Facet) Reset() { *x = ResourceSearchRequest_Facet{} - mi := &file_resource_proto_msgTypes[41] + mi := &file_resource_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3226,7 +3587,7 @@ func (x *ResourceSearchRequest_Facet) String() string { func (*ResourceSearchRequest_Facet) ProtoMessage() {} func (x *ResourceSearchRequest_Facet) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[41] + mi := &file_resource_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3239,7 +3600,7 @@ func (x *ResourceSearchRequest_Facet) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSearchRequest_Facet.ProtoReflect.Descriptor instead. func (*ResourceSearchRequest_Facet) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{21, 1} + return file_resource_proto_rawDescGZIP(), []int{23, 1} } func (x *ResourceSearchRequest_Facet) GetField() string { @@ -3271,7 +3632,7 @@ type ResourceSearchResponse_Facet struct { func (x *ResourceSearchResponse_Facet) Reset() { *x = ResourceSearchResponse_Facet{} - mi := &file_resource_proto_msgTypes[43] + mi := &file_resource_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3283,7 +3644,7 @@ func (x *ResourceSearchResponse_Facet) String() string { func (*ResourceSearchResponse_Facet) ProtoMessage() {} func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[43] + mi := &file_resource_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3296,7 +3657,7 @@ func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSearchResponse_Facet.ProtoReflect.Descriptor instead. func (*ResourceSearchResponse_Facet) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{22, 0} + return file_resource_proto_rawDescGZIP(), []int{24, 0} } func (x *ResourceSearchResponse_Facet) GetField() string { @@ -3337,7 +3698,7 @@ type ResourceSearchResponse_TermFacet struct { func (x *ResourceSearchResponse_TermFacet) Reset() { *x = ResourceSearchResponse_TermFacet{} - mi := &file_resource_proto_msgTypes[44] + mi := &file_resource_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3349,7 +3710,7 @@ func (x *ResourceSearchResponse_TermFacet) String() string { func (*ResourceSearchResponse_TermFacet) ProtoMessage() {} func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[44] + mi := &file_resource_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3362,7 +3723,7 @@ func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSearchResponse_TermFacet.ProtoReflect.Descriptor instead. func (*ResourceSearchResponse_TermFacet) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{22, 1} + return file_resource_proto_rawDescGZIP(), []int{24, 1} } func (x *ResourceSearchResponse_TermFacet) GetTerm() string { @@ -3399,7 +3760,7 @@ type ListRepositoryObjectsResponse_Item struct { func (x *ListRepositoryObjectsResponse_Item) Reset() { *x = ListRepositoryObjectsResponse_Item{} - mi := &file_resource_proto_msgTypes[46] + mi := &file_resource_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3411,7 +3772,7 @@ func (x *ListRepositoryObjectsResponse_Item) String() string { func (*ListRepositoryObjectsResponse_Item) ProtoMessage() {} func (x *ListRepositoryObjectsResponse_Item) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[46] + mi := &file_resource_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3424,7 +3785,7 @@ func (x *ListRepositoryObjectsResponse_Item) ProtoReflect() protoreflect.Message // Deprecated: Use ListRepositoryObjectsResponse_Item.ProtoReflect.Descriptor instead. func (*ListRepositoryObjectsResponse_Item) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{24, 0} + return file_resource_proto_rawDescGZIP(), []int{26, 0} } func (x *ListRepositoryObjectsResponse_Item) GetObject() *ResourceKey { @@ -3481,7 +3842,7 @@ type CountRepositoryObjectsResponse_ResourceCount struct { func (x *CountRepositoryObjectsResponse_ResourceCount) Reset() { *x = CountRepositoryObjectsResponse_ResourceCount{} - mi := &file_resource_proto_msgTypes[47] + mi := &file_resource_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3493,7 +3854,7 @@ func (x *CountRepositoryObjectsResponse_ResourceCount) String() string { func (*CountRepositoryObjectsResponse_ResourceCount) ProtoMessage() {} func (x *CountRepositoryObjectsResponse_ResourceCount) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[47] + mi := &file_resource_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3506,7 +3867,7 @@ func (x *CountRepositoryObjectsResponse_ResourceCount) ProtoReflect() protorefle // Deprecated: Use CountRepositoryObjectsResponse_ResourceCount.ProtoReflect.Descriptor instead. func (*CountRepositoryObjectsResponse_ResourceCount) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{26, 0} + return file_resource_proto_rawDescGZIP(), []int{28, 0} } func (x *CountRepositoryObjectsResponse_ResourceCount) GetRepository() string { @@ -3559,7 +3920,7 @@ type ResourceTableColumnDefinition_Properties struct { func (x *ResourceTableColumnDefinition_Properties) Reset() { *x = ResourceTableColumnDefinition_Properties{} - mi := &file_resource_proto_msgTypes[48] + mi := &file_resource_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3571,7 +3932,7 @@ func (x *ResourceTableColumnDefinition_Properties) String() string { func (*ResourceTableColumnDefinition_Properties) ProtoMessage() {} func (x *ResourceTableColumnDefinition_Properties) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[48] + mi := &file_resource_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3584,7 +3945,7 @@ func (x *ResourceTableColumnDefinition_Properties) ProtoReflect() protoreflect.M // Deprecated: Use ResourceTableColumnDefinition_Properties.ProtoReflect.Descriptor instead. func (*ResourceTableColumnDefinition_Properties) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{30, 0} + return file_resource_proto_rawDescGZIP(), []int{32, 0} } func (x *ResourceTableColumnDefinition_Properties) GetUniqueValues() bool { @@ -3624,7 +3985,7 @@ func (x *ResourceTableColumnDefinition_Properties) GetDefaultValue() []byte { var File_resource_proto protoreflect.FileDescriptor -var file_resource_proto_rawDesc = []byte{ +var file_resource_proto_rawDesc = string([]byte{ 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x71, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, @@ -3806,527 +4167,598 @@ var file_resource_proto_rawDesc = []byte{ 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x4f, 0x4f, 0x4b, 0x4d, 0x41, 0x52, 0x4b, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, - 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x22, 0x62, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x69, - 0x6e, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x22, 0xd2, 0x01, 0x0a, 0x15, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x22, 0xd9, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x35, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x22, 0x3b, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x41, 0x44, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x4f, 0x44, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, + 0x44, 0x10, 0x03, 0x22, 0xdf, 0x04, 0x0a, 0x0d, 0x42, 0x61, 0x74, 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, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x1a, - 0x4f, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, - 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0xee, 0x04, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x66, - 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, - 0x42, 0x79, 0x12, 0x40, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66, - 0x61, 0x63, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x08, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x65, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, - 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 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, + 0x6f, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, + 0x12, 0x39, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3c, 0x0a, 0x08, 0x72, + 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, + 0x08, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x1a, 0x86, 0x02, 0x0a, 0x07, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x1a, 0x80, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, + 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x62, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6b, + 0x69, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x69, 0x6e, 0x64, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x22, 0xd2, 0x01, 0x0a, 0x15, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x1a, 0x4f, 0x0a, + 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xee, + 0x04, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x66, 0x65, 0x64, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4b, 0x65, 0x79, 0x52, 0x09, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, + 0x12, 0x40, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x07, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x68, - 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x63, 0x6f, - 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x43, - 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x53, 0x63, 0x6f, 0x72, 0x65, - 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66, 0x61, - 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, - 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, - 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x60, 0x0a, 0x0a, - 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, - 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, - 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x78, - 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, - 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, - 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xda, 0x02, 0x0a, 0x1d, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05, 0x69, 0x74, - 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66, 0x61, 0x63, + 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, + 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f, 0x72, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05, 0x46, 0x61, 0x63, + 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x1a, 0x5f, + 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3b, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, + 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x68, 0x69, 0x74, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x69, + 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x41, + 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66, 0x61, 0x63, 0x65, + 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x74, 0x65, + 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x60, 0x0a, 0x0a, 0x46, 0x61, + 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, + 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x78, 0x0a, 0x1c, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, + 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xda, 0x02, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x0f, + 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 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, 0x1a, 0x9f, 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x06, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, + 0x79, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, + 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x22, 0x51, 0x0a, 0x1d, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x94, 0x02, 0x0a, 0x1e, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x05, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x26, - 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, - 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 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, 0x1a, 0x9f, 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x06, - 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, - 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, - 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, - 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x22, 0x51, 0x0a, 0x1d, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x94, 0x02, 0x0a, 0x1e, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x05, 0x69, - 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x02, 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, 0x1a, 0x77, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2e, 0x0a, + 0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0xab, 0x01, + 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4f, 0x0a, 0x0d, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, + 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x45, 0x52, + 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x22, 0x87, 0x02, 0x0a, 0x0d, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x41, 0x0a, + 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, + 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, + 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, + 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, + 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf1, 0x04, 0x0a, 0x1d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x52, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, + 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x1a, 0xae, 0x01, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x23, 0x0a, 0x0d, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x72, 0x65, 0x65, 0x54, 0x65, 0x78, + 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x6e, 0x6f, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x12, 0x23, 0x0a, 0x0d, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x22, 0x95, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, + 0x0a, 0x07, 0x42, 0x4f, 0x4f, 0x4c, 0x45, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x49, + 0x4e, 0x54, 0x33, 0x32, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, + 0x04, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, + 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x41, 0x54, 0x45, + 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, + 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x09, 0x12, 0x0a, 0x0a, + 0x06, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x0a, 0x22, 0x94, 0x01, 0x0a, 0x10, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x12, 0x27, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, + 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x22, 0x64, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x69, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, + 0x65, 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, 0x1a, 0x77, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0x2e, 0x0a, 0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, - 0xab, 0x01, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4f, 0x0a, 0x0d, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, - 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x54, 0x5f, 0x53, - 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x45, 0x52, 0x56, - 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x22, 0x87, 0x02, - 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, - 0x41, 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, - 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, - 0x77, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, - 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, - 0x6e, 0x67, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x74, - 0x65, 0x6d, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf1, 0x04, 0x0a, 0x1d, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, - 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x1a, 0xae, 0x01, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, - 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x72, 0x65, 0x65, 0x54, - 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x61, 0x62, 0x6c, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x61, - 0x62, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6e, 0x6f, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x12, 0x23, - 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x95, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, - 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4f, 0x4f, 0x4c, 0x45, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x09, 0x0a, - 0x05, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, - 0x34, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x10, 0x05, 0x12, 0x0a, - 0x0a, 0x06, 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x41, - 0x54, 0x45, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x09, 0x12, - 0x0a, 0x0a, 0x06, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x0a, 0x22, 0x94, 0x01, 0x0a, 0x10, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, - 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x22, 0x64, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, - 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x69, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0xd3, 0x01, 0x0a, 0x0e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1c, 0x0a, 0x06, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, 0x00, 0x12, 0x08, 0x0a, + 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x01, 0x22, 0xc1, 0x01, 0x0a, 0x0f, 0x50, 0x75, 0x74, 0x42, + 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x01, 0x0a, 0x0e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1c, 0x0a, 0x06, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x01, 0x22, 0xc1, 0x01, 0x0a, 0x0f, 0x50, 0x75, - 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, - 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, - 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x22, 0x98, 0x01, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, - 0x0a, 0x10, 0x6d, 0x75, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x73, 0x74, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x2a, 0x33, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x10, 0x0a, 0x0c, - 0x4e, 0x6f, 0x74, 0x4f, 0x6c, 0x64, 0x65, 0x72, 0x54, 0x68, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x09, - 0x0a, 0x05, 0x45, 0x78, 0x61, 0x63, 0x74, 0x10, 0x01, 0x32, 0xad, 0x03, 0x0a, 0x0d, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x52, - 0x65, 0x61, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3b, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x52, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x37, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, - 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x32, 0xa9, 0x01, 0x0a, 0x0d, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4b, 0x0a, 0x06, 0x53, - 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x22, 0x98, 0x01, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, + 0x6d, 0x75, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x2a, 0x33, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, + 0x74, 0x4f, 0x6c, 0x64, 0x65, 0x72, 0x54, 0x68, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, + 0x45, 0x78, 0x61, 0x63, 0x74, 0x10, 0x01, 0x32, 0xad, 0x03, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x52, 0x65, 0x61, + 0x64, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3b, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, + 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, + 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, + 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x32, 0x4f, 0x0a, 0x0a, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x32, 0xa9, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, - 0x65, 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, 0x32, 0xe8, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x6b, 0x0a, 0x16, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, - 0x26, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, - 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x32, 0x8b, 0x01, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x3e, - 0x0a, 0x07, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, - 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x57, - 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, - 0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 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, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} + 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, 0x32, 0xe8, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x6b, 0x0a, 0x16, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x12, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x26, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, + 0x8b, 0x01, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x3e, 0x0a, + 0x07, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x50, 0x75, + 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, + 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x47, 0x65, + 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x57, 0x0a, + 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, 0x09, + 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 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, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) var ( file_resource_proto_rawDescOnce sync.Once - file_resource_proto_rawDescData = file_resource_proto_rawDesc + file_resource_proto_rawDescData []byte ) func file_resource_proto_rawDescGZIP() []byte { file_resource_proto_rawDescOnce.Do(func() { - file_resource_proto_rawDescData = protoimpl.X.CompressGZIP(file_resource_proto_rawDescData) + file_resource_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_proto_rawDesc), len(file_resource_proto_rawDesc))) }) return file_resource_proto_rawDescData } -var file_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 49) +var file_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 7) +var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 53) var file_resource_proto_goTypes = []any{ (ResourceVersionMatch)(0), // 0: resource.ResourceVersionMatch (ListRequest_Source)(0), // 1: resource.ListRequest.Source (WatchEvent_Type)(0), // 2: resource.WatchEvent.Type - (HealthCheckResponse_ServingStatus)(0), // 3: resource.HealthCheckResponse.ServingStatus - (ResourceTableColumnDefinition_ColumnType)(0), // 4: resource.ResourceTableColumnDefinition.ColumnType - (PutBlobRequest_Method)(0), // 5: resource.PutBlobRequest.Method - (*ResourceKey)(nil), // 6: resource.ResourceKey - (*ResourceWrapper)(nil), // 7: resource.ResourceWrapper - (*ErrorResult)(nil), // 8: resource.ErrorResult - (*ErrorDetails)(nil), // 9: resource.ErrorDetails - (*ErrorCause)(nil), // 10: resource.ErrorCause - (*CreateRequest)(nil), // 11: resource.CreateRequest - (*CreateResponse)(nil), // 12: resource.CreateResponse - (*UpdateRequest)(nil), // 13: resource.UpdateRequest - (*UpdateResponse)(nil), // 14: resource.UpdateResponse - (*DeleteRequest)(nil), // 15: resource.DeleteRequest - (*DeleteResponse)(nil), // 16: resource.DeleteResponse - (*ReadRequest)(nil), // 17: resource.ReadRequest - (*ReadResponse)(nil), // 18: resource.ReadResponse - (*Requirement)(nil), // 19: resource.Requirement - (*ListOptions)(nil), // 20: resource.ListOptions - (*ListRequest)(nil), // 21: resource.ListRequest - (*ListResponse)(nil), // 22: resource.ListResponse - (*WatchRequest)(nil), // 23: resource.WatchRequest - (*WatchEvent)(nil), // 24: resource.WatchEvent - (*ResourceStatsRequest)(nil), // 25: resource.ResourceStatsRequest - (*ResourceStatsResponse)(nil), // 26: resource.ResourceStatsResponse - (*ResourceSearchRequest)(nil), // 27: resource.ResourceSearchRequest - (*ResourceSearchResponse)(nil), // 28: resource.ResourceSearchResponse - (*ListRepositoryObjectsRequest)(nil), // 29: resource.ListRepositoryObjectsRequest - (*ListRepositoryObjectsResponse)(nil), // 30: resource.ListRepositoryObjectsResponse - (*CountRepositoryObjectsRequest)(nil), // 31: resource.CountRepositoryObjectsRequest - (*CountRepositoryObjectsResponse)(nil), // 32: resource.CountRepositoryObjectsResponse - (*HealthCheckRequest)(nil), // 33: resource.HealthCheckRequest - (*HealthCheckResponse)(nil), // 34: resource.HealthCheckResponse - (*ResourceTable)(nil), // 35: resource.ResourceTable - (*ResourceTableColumnDefinition)(nil), // 36: resource.ResourceTableColumnDefinition - (*ResourceTableRow)(nil), // 37: resource.ResourceTableRow - (*RestoreRequest)(nil), // 38: resource.RestoreRequest - (*RestoreResponse)(nil), // 39: resource.RestoreResponse - (*PutBlobRequest)(nil), // 40: resource.PutBlobRequest - (*PutBlobResponse)(nil), // 41: resource.PutBlobResponse - (*GetBlobRequest)(nil), // 42: resource.GetBlobRequest - (*GetBlobResponse)(nil), // 43: resource.GetBlobResponse - (*WatchEvent_Resource)(nil), // 44: resource.WatchEvent.Resource - (*ResourceStatsResponse_Stats)(nil), // 45: resource.ResourceStatsResponse.Stats - (*ResourceSearchRequest_Sort)(nil), // 46: resource.ResourceSearchRequest.Sort - (*ResourceSearchRequest_Facet)(nil), // 47: resource.ResourceSearchRequest.Facet - nil, // 48: resource.ResourceSearchRequest.FacetEntry - (*ResourceSearchResponse_Facet)(nil), // 49: resource.ResourceSearchResponse.Facet - (*ResourceSearchResponse_TermFacet)(nil), // 50: resource.ResourceSearchResponse.TermFacet - nil, // 51: resource.ResourceSearchResponse.FacetEntry - (*ListRepositoryObjectsResponse_Item)(nil), // 52: resource.ListRepositoryObjectsResponse.Item - (*CountRepositoryObjectsResponse_ResourceCount)(nil), // 53: resource.CountRepositoryObjectsResponse.ResourceCount - (*ResourceTableColumnDefinition_Properties)(nil), // 54: resource.ResourceTableColumnDefinition.Properties + (BatchRequest_Action)(0), // 3: resource.BatchRequest.Action + (HealthCheckResponse_ServingStatus)(0), // 4: resource.HealthCheckResponse.ServingStatus + (ResourceTableColumnDefinition_ColumnType)(0), // 5: resource.ResourceTableColumnDefinition.ColumnType + (PutBlobRequest_Method)(0), // 6: resource.PutBlobRequest.Method + (*ResourceKey)(nil), // 7: resource.ResourceKey + (*ResourceWrapper)(nil), // 8: resource.ResourceWrapper + (*ErrorResult)(nil), // 9: resource.ErrorResult + (*ErrorDetails)(nil), // 10: resource.ErrorDetails + (*ErrorCause)(nil), // 11: resource.ErrorCause + (*CreateRequest)(nil), // 12: resource.CreateRequest + (*CreateResponse)(nil), // 13: resource.CreateResponse + (*UpdateRequest)(nil), // 14: resource.UpdateRequest + (*UpdateResponse)(nil), // 15: resource.UpdateResponse + (*DeleteRequest)(nil), // 16: resource.DeleteRequest + (*DeleteResponse)(nil), // 17: resource.DeleteResponse + (*ReadRequest)(nil), // 18: resource.ReadRequest + (*ReadResponse)(nil), // 19: resource.ReadResponse + (*Requirement)(nil), // 20: resource.Requirement + (*ListOptions)(nil), // 21: resource.ListOptions + (*ListRequest)(nil), // 22: resource.ListRequest + (*ListResponse)(nil), // 23: resource.ListResponse + (*WatchRequest)(nil), // 24: resource.WatchRequest + (*WatchEvent)(nil), // 25: resource.WatchEvent + (*BatchRequest)(nil), // 26: resource.BatchRequest + (*BatchResponse)(nil), // 27: resource.BatchResponse + (*ResourceStatsRequest)(nil), // 28: resource.ResourceStatsRequest + (*ResourceStatsResponse)(nil), // 29: resource.ResourceStatsResponse + (*ResourceSearchRequest)(nil), // 30: resource.ResourceSearchRequest + (*ResourceSearchResponse)(nil), // 31: resource.ResourceSearchResponse + (*ListRepositoryObjectsRequest)(nil), // 32: resource.ListRepositoryObjectsRequest + (*ListRepositoryObjectsResponse)(nil), // 33: resource.ListRepositoryObjectsResponse + (*CountRepositoryObjectsRequest)(nil), // 34: resource.CountRepositoryObjectsRequest + (*CountRepositoryObjectsResponse)(nil), // 35: resource.CountRepositoryObjectsResponse + (*HealthCheckRequest)(nil), // 36: resource.HealthCheckRequest + (*HealthCheckResponse)(nil), // 37: resource.HealthCheckResponse + (*ResourceTable)(nil), // 38: resource.ResourceTable + (*ResourceTableColumnDefinition)(nil), // 39: resource.ResourceTableColumnDefinition + (*ResourceTableRow)(nil), // 40: resource.ResourceTableRow + (*RestoreRequest)(nil), // 41: resource.RestoreRequest + (*RestoreResponse)(nil), // 42: resource.RestoreResponse + (*PutBlobRequest)(nil), // 43: resource.PutBlobRequest + (*PutBlobResponse)(nil), // 44: resource.PutBlobResponse + (*GetBlobRequest)(nil), // 45: resource.GetBlobRequest + (*GetBlobResponse)(nil), // 46: resource.GetBlobResponse + (*WatchEvent_Resource)(nil), // 47: resource.WatchEvent.Resource + (*BatchResponse_Summary)(nil), // 48: resource.BatchResponse.Summary + (*BatchResponse_Rejected)(nil), // 49: resource.BatchResponse.Rejected + (*ResourceStatsResponse_Stats)(nil), // 50: resource.ResourceStatsResponse.Stats + (*ResourceSearchRequest_Sort)(nil), // 51: resource.ResourceSearchRequest.Sort + (*ResourceSearchRequest_Facet)(nil), // 52: resource.ResourceSearchRequest.Facet + nil, // 53: resource.ResourceSearchRequest.FacetEntry + (*ResourceSearchResponse_Facet)(nil), // 54: resource.ResourceSearchResponse.Facet + (*ResourceSearchResponse_TermFacet)(nil), // 55: resource.ResourceSearchResponse.TermFacet + nil, // 56: resource.ResourceSearchResponse.FacetEntry + (*ListRepositoryObjectsResponse_Item)(nil), // 57: resource.ListRepositoryObjectsResponse.Item + (*CountRepositoryObjectsResponse_ResourceCount)(nil), // 58: resource.CountRepositoryObjectsResponse.ResourceCount + (*ResourceTableColumnDefinition_Properties)(nil), // 59: resource.ResourceTableColumnDefinition.Properties } var file_resource_proto_depIdxs = []int32{ - 9, // 0: resource.ErrorResult.details:type_name -> resource.ErrorDetails - 10, // 1: resource.ErrorDetails.causes:type_name -> resource.ErrorCause - 6, // 2: resource.CreateRequest.key:type_name -> resource.ResourceKey - 8, // 3: resource.CreateResponse.error:type_name -> resource.ErrorResult - 6, // 4: resource.UpdateRequest.key:type_name -> resource.ResourceKey - 8, // 5: resource.UpdateResponse.error:type_name -> resource.ErrorResult - 6, // 6: resource.DeleteRequest.key:type_name -> resource.ResourceKey - 8, // 7: resource.DeleteResponse.error:type_name -> resource.ErrorResult - 6, // 8: resource.ReadRequest.key:type_name -> resource.ResourceKey - 8, // 9: resource.ReadResponse.error:type_name -> resource.ErrorResult - 6, // 10: resource.ListOptions.key:type_name -> resource.ResourceKey - 19, // 11: resource.ListOptions.labels:type_name -> resource.Requirement - 19, // 12: resource.ListOptions.fields:type_name -> resource.Requirement + 10, // 0: resource.ErrorResult.details:type_name -> resource.ErrorDetails + 11, // 1: resource.ErrorDetails.causes:type_name -> resource.ErrorCause + 7, // 2: resource.CreateRequest.key:type_name -> resource.ResourceKey + 9, // 3: resource.CreateResponse.error:type_name -> resource.ErrorResult + 7, // 4: resource.UpdateRequest.key:type_name -> resource.ResourceKey + 9, // 5: resource.UpdateResponse.error:type_name -> resource.ErrorResult + 7, // 6: resource.DeleteRequest.key:type_name -> resource.ResourceKey + 9, // 7: resource.DeleteResponse.error:type_name -> resource.ErrorResult + 7, // 8: resource.ReadRequest.key:type_name -> resource.ResourceKey + 9, // 9: resource.ReadResponse.error:type_name -> resource.ErrorResult + 7, // 10: resource.ListOptions.key:type_name -> resource.ResourceKey + 20, // 11: resource.ListOptions.labels:type_name -> resource.Requirement + 20, // 12: resource.ListOptions.fields:type_name -> resource.Requirement 0, // 13: resource.ListRequest.version_match:type_name -> resource.ResourceVersionMatch - 20, // 14: resource.ListRequest.options:type_name -> resource.ListOptions + 21, // 14: resource.ListRequest.options:type_name -> resource.ListOptions 1, // 15: resource.ListRequest.source:type_name -> resource.ListRequest.Source - 7, // 16: resource.ListResponse.items:type_name -> resource.ResourceWrapper - 8, // 17: resource.ListResponse.error:type_name -> resource.ErrorResult - 20, // 18: resource.WatchRequest.options:type_name -> resource.ListOptions + 8, // 16: resource.ListResponse.items:type_name -> resource.ResourceWrapper + 9, // 17: resource.ListResponse.error:type_name -> resource.ErrorResult + 21, // 18: resource.WatchRequest.options:type_name -> resource.ListOptions 2, // 19: resource.WatchEvent.type:type_name -> resource.WatchEvent.Type - 44, // 20: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource - 44, // 21: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource - 8, // 22: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult - 45, // 23: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats - 20, // 24: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions - 6, // 25: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey - 46, // 26: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort - 48, // 27: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry - 8, // 28: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult - 6, // 29: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey - 35, // 30: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable - 51, // 31: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry - 52, // 32: resource.ListRepositoryObjectsResponse.items:type_name -> resource.ListRepositoryObjectsResponse.Item - 8, // 33: resource.ListRepositoryObjectsResponse.error:type_name -> resource.ErrorResult - 53, // 34: resource.CountRepositoryObjectsResponse.items:type_name -> resource.CountRepositoryObjectsResponse.ResourceCount - 8, // 35: resource.CountRepositoryObjectsResponse.error:type_name -> resource.ErrorResult - 3, // 36: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus - 36, // 37: resource.ResourceTable.columns:type_name -> resource.ResourceTableColumnDefinition - 37, // 38: resource.ResourceTable.rows:type_name -> resource.ResourceTableRow - 4, // 39: resource.ResourceTableColumnDefinition.type:type_name -> resource.ResourceTableColumnDefinition.ColumnType - 54, // 40: resource.ResourceTableColumnDefinition.properties:type_name -> resource.ResourceTableColumnDefinition.Properties - 6, // 41: resource.ResourceTableRow.key:type_name -> resource.ResourceKey - 6, // 42: resource.RestoreRequest.key:type_name -> resource.ResourceKey - 8, // 43: resource.RestoreResponse.error:type_name -> resource.ErrorResult - 6, // 44: resource.PutBlobRequest.resource:type_name -> resource.ResourceKey - 5, // 45: resource.PutBlobRequest.method:type_name -> resource.PutBlobRequest.Method - 8, // 46: resource.PutBlobResponse.error:type_name -> resource.ErrorResult - 6, // 47: resource.GetBlobRequest.resource:type_name -> resource.ResourceKey - 8, // 48: resource.GetBlobResponse.error:type_name -> resource.ErrorResult - 47, // 49: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet - 50, // 50: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet - 49, // 51: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet - 6, // 52: resource.ListRepositoryObjectsResponse.Item.object:type_name -> resource.ResourceKey - 17, // 53: resource.ResourceStore.Read:input_type -> resource.ReadRequest - 11, // 54: resource.ResourceStore.Create:input_type -> resource.CreateRequest - 13, // 55: resource.ResourceStore.Update:input_type -> resource.UpdateRequest - 15, // 56: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest - 38, // 57: resource.ResourceStore.Restore:input_type -> resource.RestoreRequest - 21, // 58: resource.ResourceStore.List:input_type -> resource.ListRequest - 23, // 59: resource.ResourceStore.Watch:input_type -> resource.WatchRequest - 27, // 60: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest - 25, // 61: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest - 31, // 62: resource.RepositoryIndex.CountRepositoryObjects:input_type -> resource.CountRepositoryObjectsRequest - 29, // 63: resource.RepositoryIndex.ListRepositoryObjects:input_type -> resource.ListRepositoryObjectsRequest - 40, // 64: resource.BlobStore.PutBlob:input_type -> resource.PutBlobRequest - 42, // 65: resource.BlobStore.GetBlob:input_type -> resource.GetBlobRequest - 33, // 66: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest - 18, // 67: resource.ResourceStore.Read:output_type -> resource.ReadResponse - 12, // 68: resource.ResourceStore.Create:output_type -> resource.CreateResponse - 14, // 69: resource.ResourceStore.Update:output_type -> resource.UpdateResponse - 16, // 70: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse - 39, // 71: resource.ResourceStore.Restore:output_type -> resource.RestoreResponse - 22, // 72: resource.ResourceStore.List:output_type -> resource.ListResponse - 24, // 73: resource.ResourceStore.Watch:output_type -> resource.WatchEvent - 28, // 74: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse - 26, // 75: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse - 32, // 76: resource.RepositoryIndex.CountRepositoryObjects:output_type -> resource.CountRepositoryObjectsResponse - 30, // 77: resource.RepositoryIndex.ListRepositoryObjects:output_type -> resource.ListRepositoryObjectsResponse - 41, // 78: resource.BlobStore.PutBlob:output_type -> resource.PutBlobResponse - 43, // 79: resource.BlobStore.GetBlob:output_type -> resource.GetBlobResponse - 34, // 80: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse - 67, // [67:81] is the sub-list for method output_type - 53, // [53:67] is the sub-list for method input_type - 53, // [53:53] is the sub-list for extension type_name - 53, // [53:53] is the sub-list for extension extendee - 0, // [0:53] is the sub-list for field type_name + 47, // 20: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource + 47, // 21: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource + 7, // 22: resource.BatchRequest.key:type_name -> resource.ResourceKey + 3, // 23: resource.BatchRequest.action:type_name -> resource.BatchRequest.Action + 9, // 24: resource.BatchResponse.error:type_name -> resource.ErrorResult + 48, // 25: resource.BatchResponse.summary:type_name -> resource.BatchResponse.Summary + 49, // 26: resource.BatchResponse.rejected:type_name -> resource.BatchResponse.Rejected + 9, // 27: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult + 50, // 28: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats + 21, // 29: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions + 7, // 30: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey + 51, // 31: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort + 53, // 32: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry + 9, // 33: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult + 7, // 34: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey + 38, // 35: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable + 56, // 36: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry + 57, // 37: resource.ListRepositoryObjectsResponse.items:type_name -> resource.ListRepositoryObjectsResponse.Item + 9, // 38: resource.ListRepositoryObjectsResponse.error:type_name -> resource.ErrorResult + 58, // 39: resource.CountRepositoryObjectsResponse.items:type_name -> resource.CountRepositoryObjectsResponse.ResourceCount + 9, // 40: resource.CountRepositoryObjectsResponse.error:type_name -> resource.ErrorResult + 4, // 41: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus + 39, // 42: resource.ResourceTable.columns:type_name -> resource.ResourceTableColumnDefinition + 40, // 43: resource.ResourceTable.rows:type_name -> resource.ResourceTableRow + 5, // 44: resource.ResourceTableColumnDefinition.type:type_name -> resource.ResourceTableColumnDefinition.ColumnType + 59, // 45: resource.ResourceTableColumnDefinition.properties:type_name -> resource.ResourceTableColumnDefinition.Properties + 7, // 46: resource.ResourceTableRow.key:type_name -> resource.ResourceKey + 7, // 47: resource.RestoreRequest.key:type_name -> resource.ResourceKey + 9, // 48: resource.RestoreResponse.error:type_name -> resource.ErrorResult + 7, // 49: resource.PutBlobRequest.resource:type_name -> resource.ResourceKey + 6, // 50: resource.PutBlobRequest.method:type_name -> resource.PutBlobRequest.Method + 9, // 51: resource.PutBlobResponse.error:type_name -> resource.ErrorResult + 7, // 52: resource.GetBlobRequest.resource:type_name -> resource.ResourceKey + 9, // 53: resource.GetBlobResponse.error:type_name -> resource.ErrorResult + 7, // 54: resource.BatchResponse.Rejected.key:type_name -> resource.ResourceKey + 3, // 55: resource.BatchResponse.Rejected.action:type_name -> resource.BatchRequest.Action + 52, // 56: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet + 55, // 57: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet + 54, // 58: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet + 7, // 59: resource.ListRepositoryObjectsResponse.Item.object:type_name -> resource.ResourceKey + 18, // 60: resource.ResourceStore.Read:input_type -> resource.ReadRequest + 12, // 61: resource.ResourceStore.Create:input_type -> resource.CreateRequest + 14, // 62: resource.ResourceStore.Update:input_type -> resource.UpdateRequest + 16, // 63: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest + 41, // 64: resource.ResourceStore.Restore:input_type -> resource.RestoreRequest + 22, // 65: resource.ResourceStore.List:input_type -> resource.ListRequest + 24, // 66: resource.ResourceStore.Watch:input_type -> resource.WatchRequest + 26, // 67: resource.BatchStore.BatchProcess:input_type -> resource.BatchRequest + 30, // 68: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest + 28, // 69: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest + 34, // 70: resource.RepositoryIndex.CountRepositoryObjects:input_type -> resource.CountRepositoryObjectsRequest + 32, // 71: resource.RepositoryIndex.ListRepositoryObjects:input_type -> resource.ListRepositoryObjectsRequest + 43, // 72: resource.BlobStore.PutBlob:input_type -> resource.PutBlobRequest + 45, // 73: resource.BlobStore.GetBlob:input_type -> resource.GetBlobRequest + 36, // 74: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest + 19, // 75: resource.ResourceStore.Read:output_type -> resource.ReadResponse + 13, // 76: resource.ResourceStore.Create:output_type -> resource.CreateResponse + 15, // 77: resource.ResourceStore.Update:output_type -> resource.UpdateResponse + 17, // 78: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse + 42, // 79: resource.ResourceStore.Restore:output_type -> resource.RestoreResponse + 23, // 80: resource.ResourceStore.List:output_type -> resource.ListResponse + 25, // 81: resource.ResourceStore.Watch:output_type -> resource.WatchEvent + 27, // 82: resource.BatchStore.BatchProcess:output_type -> resource.BatchResponse + 31, // 83: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse + 29, // 84: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse + 35, // 85: resource.RepositoryIndex.CountRepositoryObjects:output_type -> resource.CountRepositoryObjectsResponse + 33, // 86: resource.RepositoryIndex.ListRepositoryObjects:output_type -> resource.ListRepositoryObjectsResponse + 44, // 87: resource.BlobStore.PutBlob:output_type -> resource.PutBlobResponse + 46, // 88: resource.BlobStore.GetBlob:output_type -> resource.GetBlobResponse + 37, // 89: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse + 75, // [75:90] is the sub-list for method output_type + 60, // [60:75] is the sub-list for method input_type + 60, // [60:60] is the sub-list for extension type_name + 60, // [60:60] is the sub-list for extension extendee + 0, // [0:60] is the sub-list for field type_name } func init() { file_resource_proto_init() } @@ -4338,11 +4770,11 @@ func file_resource_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_resource_proto_rawDesc, - NumEnums: 6, - NumMessages: 49, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_proto_rawDesc), len(file_resource_proto_rawDesc)), + NumEnums: 7, + NumMessages: 53, NumExtensions: 0, - NumServices: 5, + NumServices: 6, }, GoTypes: file_resource_proto_goTypes, DependencyIndexes: file_resource_proto_depIdxs, @@ -4350,7 +4782,6 @@ func file_resource_proto_init() { MessageInfos: file_resource_proto_msgTypes, }.Build() File_resource_proto = out.File - file_resource_proto_rawDesc = nil file_resource_proto_goTypes = nil file_resource_proto_depIdxs = nil } diff --git a/pkg/storage/unified/resource/resource.proto b/pkg/storage/unified/resource/resource.proto index 2624e36dc94..f16f194d360 100644 --- a/pkg/storage/unified/resource/resource.proto +++ b/pkg/storage/unified/resource/resource.proto @@ -308,7 +308,7 @@ message WatchEvent { // Timestamp the event was sent int64 timestamp = 1; - // Timestamp the event was sent + // The event type Type type = 2; // Resource version for the object @@ -318,6 +318,65 @@ message WatchEvent { Resource previous = 4; } +message BatchRequest { + enum Action { + // will be an error + UNKNOWN = 0; + + // Matches Watch event enum + ADDED = 1; + MODIFIED = 2; + DELETED = 3; + } + + // NOTE everything in the same stream must share the same Namespace/Group/Resource + ResourceKey key = 1; + + // Requested action + Action action = 2; + + // The resource value + bytes value = 3; + + // Hint that a new version will be written on-top of this + string folder = 4; +} + +message BatchResponse { + message Summary { + string namespace = 1; + string group = 2; + string resource = 3; + int64 count = 4; + int64 history = 5; + int64 resource_version = 6; // The max saved RV + + // The previous count + int64 previous_count = 7; + int64 previous_history = 8; + } + + // Collect a few invalid messages + message Rejected { + ResourceKey key = 1; + BatchRequest.Action action = 2; + string error = 3; + } + + // Error details + ErrorResult error = 1; + + // Total events processed + int64 processed = 2; + + // Summary status for the processed values + repeated Summary summary = 3; + + // Rejected + repeated Rejected rejected = 4; +} + + // Get statistics across multiple resources // For these queries, we do not need authorization to see the actual values message ResourceStatsRequest { @@ -754,6 +813,13 @@ service ResourceStore { rpc Watch(WatchRequest) returns (stream WatchEvent); } +service BatchStore { + // Write multiple resources to the same Namespace/Group/Resource + // Events will not be sent until the stream is complete + // Only the *create* permissions is checked + rpc BatchProcess(stream BatchRequest) returns (BatchResponse); +} + // Unlike the ResourceStore, this service can be exposed to clients directly // It should be implemented with efficient indexes and does not need read-after-write semantics service ResourceIndex { diff --git a/pkg/storage/unified/resource/resource_grpc.pb.go b/pkg/storage/unified/resource/resource_grpc.pb.go index cec8f0bfc9c..c8cbb9569a0 100644 --- a/pkg/storage/unified/resource/resource_grpc.pb.go +++ b/pkg/storage/unified/resource/resource_grpc.pb.go @@ -385,6 +385,135 @@ var ResourceStore_ServiceDesc = grpc.ServiceDesc{ Metadata: "resource.proto", } +const ( + BatchStore_BatchProcess_FullMethodName = "/resource.BatchStore/BatchProcess" +) + +// BatchStoreClient is the client API for BatchStore service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BatchStoreClient interface { + // Write multiple resources to the same Namespace/Group/Resource + // Events will not be sent until the stream is complete + // Only the *create* permissions is checked + BatchProcess(ctx context.Context, opts ...grpc.CallOption) (BatchStore_BatchProcessClient, error) +} + +type batchStoreClient struct { + cc grpc.ClientConnInterface +} + +func NewBatchStoreClient(cc grpc.ClientConnInterface) BatchStoreClient { + return &batchStoreClient{cc} +} + +func (c *batchStoreClient) BatchProcess(ctx context.Context, opts ...grpc.CallOption) (BatchStore_BatchProcessClient, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &BatchStore_ServiceDesc.Streams[0], BatchStore_BatchProcess_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &batchStoreBatchProcessClient{ClientStream: stream} + return x, nil +} + +type BatchStore_BatchProcessClient interface { + Send(*BatchRequest) error + CloseAndRecv() (*BatchResponse, error) + grpc.ClientStream +} + +type batchStoreBatchProcessClient struct { + grpc.ClientStream +} + +func (x *batchStoreBatchProcessClient) Send(m *BatchRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *batchStoreBatchProcessClient) CloseAndRecv() (*BatchResponse, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(BatchResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// BatchStoreServer is the server API for BatchStore service. +// All implementations should embed UnimplementedBatchStoreServer +// for forward compatibility +type BatchStoreServer interface { + // Write multiple resources to the same Namespace/Group/Resource + // Events will not be sent until the stream is complete + // Only the *create* permissions is checked + BatchProcess(BatchStore_BatchProcessServer) error +} + +// UnimplementedBatchStoreServer should be embedded to have forward compatible implementations. +type UnimplementedBatchStoreServer struct { +} + +func (UnimplementedBatchStoreServer) BatchProcess(BatchStore_BatchProcessServer) error { + return status.Errorf(codes.Unimplemented, "method BatchProcess not implemented") +} + +// UnsafeBatchStoreServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BatchStoreServer will +// result in compilation errors. +type UnsafeBatchStoreServer interface { + mustEmbedUnimplementedBatchStoreServer() +} + +func RegisterBatchStoreServer(s grpc.ServiceRegistrar, srv BatchStoreServer) { + s.RegisterService(&BatchStore_ServiceDesc, srv) +} + +func _BatchStore_BatchProcess_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(BatchStoreServer).BatchProcess(&batchStoreBatchProcessServer{ServerStream: stream}) +} + +type BatchStore_BatchProcessServer interface { + SendAndClose(*BatchResponse) error + Recv() (*BatchRequest, error) + grpc.ServerStream +} + +type batchStoreBatchProcessServer struct { + grpc.ServerStream +} + +func (x *batchStoreBatchProcessServer) SendAndClose(m *BatchResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *batchStoreBatchProcessServer) Recv() (*BatchRequest, error) { + m := new(BatchRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// BatchStore_ServiceDesc is the grpc.ServiceDesc for BatchStore service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BatchStore_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "resource.BatchStore", + HandlerType: (*BatchStoreServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "BatchProcess", + Handler: _BatchStore_BatchProcess_Handler, + ClientStreams: true, + }, + }, + Metadata: "resource.proto", +} + const ( ResourceIndex_Search_FullMethodName = "/resource.ResourceIndex/Search" ResourceIndex_GetStats_FullMethodName = "/resource.ResourceIndex/GetStats" diff --git a/pkg/storage/unified/resource/search.go b/pkg/storage/unified/resource/search.go index 2f32e5cb440..74d89b8bdb3 100644 --- a/pkg/storage/unified/resource/search.go +++ b/pkg/storage/unified/resource/search.go @@ -107,6 +107,9 @@ func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.A if opts.Backend == nil { return nil, nil } + if tracer == nil { + return nil, fmt.Errorf("missing tracer") + } if opts.WorkerThreads < 1 { opts.WorkerThreads = 1 @@ -384,6 +387,11 @@ func (s *searchSupport) init(ctx context.Context) error { for { v := <-events + // Skip events during batch updates + if v.PreviousRV < 0 { + continue + } + s.handleEvent(watchctx, v) } }() @@ -496,7 +504,7 @@ func (s *searchSupport) getOrCreateIndex(ctx context.Context, key NamespacedReso if idx == nil { idx, _, err = s.build(ctx, key, 10, 0) // unknown size and RV if err != nil { - return nil, err + return nil, fmt.Errorf("error building search index, %w", err) } if idx == nil { return nil, fmt.Errorf("nil index after build") @@ -541,7 +549,8 @@ func (s *searchSupport) build(ctx context.Context, nsr NamespacedResource, size // Convert it to an indexable document doc, err := builder.BuildDocument(ctx, key, iter.ResourceVersion(), iter.Value()) if err != nil { - return err + s.log.Error("error building search document", "key", key.SearchID(), "err", err) + continue } // And finally write it to the index diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index 58d555bd429..48989c6808b 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -26,6 +26,7 @@ import ( // ResourceServer implements all gRPC services type ResourceServer interface { ResourceStoreServer + BatchStoreServer ResourceIndexServer RepositoryIndexServer BlobStoreServer @@ -260,7 +261,7 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) { err := s.Init(ctx) if err != nil { - s.log.Error("error initializing resource server", "error", err) + s.log.Error("resource server init failed", "error", err) return nil, err } @@ -314,7 +315,7 @@ func (s *server) Init(ctx context.Context) error { } if s.initErr != nil { - s.log.Error("error initializing resource server", "error", s.initErr) + s.log.Error("error running resource server init", "error", s.initErr) } }) return s.initErr @@ -921,6 +922,12 @@ func (s *server) initWatcher() error { for { // pipe all events v := <-events + + // Skip events during batch updates + if v.PreviousRV < 0 { + continue + } + s.log.Debug("Server. Streaming Event", "type", v.Type, "previousRV", v.PreviousRV, "group", v.Key.Group, "namespace", v.Key.Namespace, "resource", v.Key.Resource, "name", v.Key.Name) s.mostRecentRV.Store(v.ResourceVersion) out <- v diff --git a/pkg/storage/unified/sql/backend.go b/pkg/storage/unified/sql/backend.go index 1690d129a77..7a7d9990d6a 100644 --- a/pkg/storage/unified/sql/backend.go +++ b/pkg/storage/unified/sql/backend.go @@ -59,6 +59,7 @@ func NewBackend(opts BackendOptions) (Backend, error) { tracer: opts.Tracer, dbProvider: opts.DBProvider, pollingInterval: pollingInterval, + batchLock: &batchLock{running: make(map[string]bool)}, }, nil } @@ -77,6 +78,7 @@ type backend struct { dbProvider db.DBProvider db db.DB dialect sqltemplate.Dialect + batchLock *batchLock // watch streaming //stream chan *resource.WatchEvent @@ -701,7 +703,7 @@ func (b *backend) WatchWriteEvents(ctx context.Context) (<-chan *resource.Writte // Get the latest RV since, err := b.listLatestRVs(ctx) if err != nil { - return nil, fmt.Errorf("get the latest resource version: %w", err) + return nil, fmt.Errorf("watch, get latest resource version: %w", err) } // Start the poller stream := make(chan *resource.WrittenEvent) @@ -713,17 +715,23 @@ func (b *backend) poller(ctx context.Context, since groupResourceRV, stream chan t := time.NewTicker(b.pollingInterval) defer close(stream) defer t.Stop() + isSQLite := b.dialect.DialectName() == "sqlite" for { select { case <-b.done: return case <-t.C: + // Block polling duffing import to avoid database locked issues + if isSQLite && b.batchLock.Active() { + continue + } + ctx, span := b.tracer.Start(ctx, tracePrefix+"poller") // List the latest RVs grv, err := b.listLatestRVs(ctx) if err != nil { - b.log.Error("get the latest resource version", "err", err) + b.log.Error("poller get latest resource version", "err", err) t.Reset(b.pollingInterval) continue } diff --git a/pkg/storage/unified/sql/batch.go b/pkg/storage/unified/sql/batch.go new file mode 100644 index 00000000000..75221c98b32 --- /dev/null +++ b/pkg/storage/unified/sql/batch.go @@ -0,0 +1,338 @@ +package sql + +import ( + "context" + "fmt" + "net/http" + "os" + "sync" + "time" + + "github.com/google/uuid" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/grafana/grafana-app-sdk/logging" + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/storage/unified/parquet" + "github.com/grafana/grafana/pkg/storage/unified/resource" + "github.com/grafana/grafana/pkg/storage/unified/sql/db" + "github.com/grafana/grafana/pkg/storage/unified/sql/dbutil" + "github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate" +) + +var ( + _ resource.BatchProcessingBackend = (*backend)(nil) +) + +type batchRV struct { + max int64 + counter int64 +} + +func newBatchRV() *batchRV { + t := time.Now().Truncate(time.Second * 10) + return &batchRV{ + max: (t.UnixMicro() / 10000000) * 10000000, + counter: 0, + } +} + +func (x *batchRV) next(obj metav1.Object) int64 { + ts := obj.GetCreationTimestamp().UnixMicro() + anno := obj.GetAnnotations() + if anno != nil { + v := anno[utils.AnnoKeyUpdatedTimestamp] + t, err := time.Parse(time.RFC3339, v) + if err == nil { + ts = t.UnixMicro() + } + } + if ts > x.max || ts < 10000000 { + ts = x.max + } + x.counter++ + return (ts/10000000)*10000000 + x.counter +} + +type batchLock struct { + running map[string]bool + mu sync.Mutex +} + +func (x *batchLock) Start(keys []*resource.ResourceKey) error { + x.mu.Lock() + defer x.mu.Unlock() + + // First verify that it is not already running + ids := make([]string, len(keys)) + for i, k := range keys { + id := k.BatchID() + if x.running[id] { + return &apierrors.StatusError{ErrStatus: metav1.Status{ + Code: http.StatusPreconditionFailed, + Message: "batch export is already running", + }} + } + ids[i] = id + } + + // Then add the keys to the lock + for _, k := range ids { + x.running[k] = true + } + return nil +} + +func (x *batchLock) Finish(keys []*resource.ResourceKey) { + x.mu.Lock() + defer x.mu.Unlock() + for _, k := range keys { + delete(x.running, k.BatchID()) + } +} + +func (x *batchLock) Active() bool { + x.mu.Lock() + defer x.mu.Unlock() + return len(x.running) > 0 +} + +func (b *backend) ProcessBatch(ctx context.Context, setting resource.BatchSettings, iter resource.BatchRequestIterator) *resource.BatchResponse { + err := b.batchLock.Start(setting.Collection) + if err != nil { + return &resource.BatchResponse{ + Error: resource.AsErrorResult(err), + } + } + defer b.batchLock.Finish(setting.Collection) + + // We may want to first write parquet, then read parquet + if b.dialect.DialectName() == "sqlite" { + file, err := os.CreateTemp("", "grafana-batch-export-*.parquet") + if err != nil { + return &resource.BatchResponse{ + Error: resource.AsErrorResult(err), + } + } + + writer, err := parquet.NewParquetWriter(file) + if err != nil { + return &resource.BatchResponse{ + Error: resource.AsErrorResult(err), + } + } + + // write batch to parquet + rsp := writer.ProcessBatch(ctx, setting, iter) + if rsp.Error != nil { + return rsp + } + + b.log.Info("using parquet buffer", "parquet", file) + + // Replace the iterator with one from parquet + iter, err = parquet.NewParquetReader(file.Name(), 50) + if err != nil { + return &resource.BatchResponse{ + Error: resource.AsErrorResult(err), + } + } + } + + return b.processBatch(ctx, setting, iter) +} + +// internal batch process +func (b *backend) processBatch(ctx context.Context, setting resource.BatchSettings, iter resource.BatchRequestIterator) *resource.BatchResponse { + rsp := &resource.BatchResponse{} + err := b.db.WithTx(ctx, ReadCommitted, func(ctx context.Context, tx db.Tx) error { + rollbackWithError := func(err error) error { + txerr := tx.Rollback() + if txerr != nil { + b.log.Warn("rollback", "error", txerr) + } else { + b.log.Info("rollback") + } + return err + } + batch := &batchWroker{ + ctx: ctx, + tx: tx, + dialect: b.dialect, + logger: logging.FromContext(ctx), + } + + // Calculate the RV based on incoming request timestamps + rv := newBatchRV() + + summaries := make(map[string]*resource.BatchResponse_Summary, len(setting.Collection)*4) + + // First clear everything in the transaction + if setting.RebuildCollection { + for _, key := range setting.Collection { + summary, err := batch.deleteCollection(key) + if err != nil { + return rollbackWithError(err) + } + summaries[key.BatchID()] = summary + rsp.Summary = append(rsp.Summary, summary) + } + } + + obj := &unstructured.Unstructured{} + + // Write each event into the history + for iter.Next() { + if iter.RollbackRequested() { + return rollbackWithError(nil) + } + req := iter.Request() + if req == nil { + return rollbackWithError(fmt.Errorf("missing request")) + } + rsp.Processed++ + + if req.Action == resource.BatchRequest_UNKNOWN { + rsp.Rejected = append(rsp.Rejected, &resource.BatchResponse_Rejected{ + Key: req.Key, + Action: req.Action, + Error: "unknown action", + }) + continue + } + + err := obj.UnmarshalJSON(req.Value) + if err != nil { + rsp.Rejected = append(rsp.Rejected, &resource.BatchResponse_Rejected{ + Key: req.Key, + Action: req.Action, + Error: "unable to unmarshal json", + }) + continue + } + + // Write the event to history + if _, err := dbutil.Exec(ctx, tx, sqlResourceHistoryInsert, sqlResourceRequest{ + SQLTemplate: sqltemplate.New(b.dialect), + WriteEvent: resource.WriteEvent{ + Key: req.Key, + Type: resource.WatchEvent_Type(req.Action), + Value: req.Value, + PreviousRV: -1, // Used for WATCH, but we want to skip watch events + }, + Folder: req.Folder, + GUID: uuid.NewString(), + ResourceVersion: rv.next(obj), + }); err != nil { + return rollbackWithError(fmt.Errorf("insert into resource history: %w", err)) + } + } + + // Now update the resource table from history + for _, key := range setting.Collection { + k := fmt.Sprintf("%s/%s/%s", key.Namespace, key.Group, key.Resource) + summary := summaries[k] + if summary == nil { + return rollbackWithError(fmt.Errorf("missing summary key for: %s", k)) + } + + err := batch.syncCollection(key, summary) + if err != nil { + return err + } + + // Make sure the collection RV is above our last written event + _, err = b.resourceVersionAtomicInc(ctx, tx, key) + if err != nil { + b.log.Warn("error increasing RV", "error", err) + } + } + return nil + }) + if err != nil { + rsp.Error = resource.AsErrorResult(err) + } + return rsp +} + +type batchWroker struct { + ctx context.Context + tx db.ContextExecer + dialect sqltemplate.Dialect + logger logging.Logger +} + +// This will remove everything from the `resource` and `resource_history` table for a given namespace/group/resource +func (w *batchWroker) deleteCollection(key *resource.ResourceKey) (*resource.BatchResponse_Summary, error) { + summary := &resource.BatchResponse_Summary{ + Namespace: key.Namespace, + Group: key.Group, + Resource: key.Resource, + } + + // First delete history + res, err := dbutil.Exec(w.ctx, w.tx, sqlResourceHistoryDelete, &sqlResourceHistoryDeleteRequest{ + SQLTemplate: sqltemplate.New(w.dialect), + Namespace: key.Namespace, + Group: key.Group, + Resource: key.Resource, + }) + if err != nil { + return nil, err + } + + summary.PreviousHistory, err = res.RowsAffected() + if err != nil { + return nil, err + } + + // Next delete the active resource table + res, err = dbutil.Exec(w.ctx, w.tx, sqlResourceDelete, &sqlResourceRequest{ + SQLTemplate: sqltemplate.New(w.dialect), + WriteEvent: resource.WriteEvent{ + Key: key, + }, + }) + if err != nil { + return nil, err + } + summary.PreviousCount, err = res.RowsAffected() + return summary, err +} + +// Copy the latest value from history into the active resource table +func (w *batchWroker) syncCollection(key *resource.ResourceKey, summary *resource.BatchResponse_Summary) error { + w.logger.Info("synchronize collection", "key", key.BatchID()) + _, err := dbutil.Exec(w.ctx, w.tx, sqlResourceInsertFromHistory, &sqlResourceInsertFromHistoryRequest{ + SQLTemplate: sqltemplate.New(w.dialect), + Key: key, + }) + if err != nil { + return err + } + + w.logger.Info("get stats (still in transaction)", "key", key.BatchID()) + rows, err := dbutil.QueryRows(w.ctx, w.tx, sqlResourceStats, &sqlStatsRequest{ + SQLTemplate: sqltemplate.New(w.dialect), + Namespace: key.Namespace, + Group: key.Group, + Resource: key.Resource, + }) + if err != nil { + return err + } + if rows != nil { + defer func() { + _ = rows.Close() + }() + } + if rows.Next() { + row := resource.ResourceStats{} + return rows.Scan(&row.Namespace, &row.Group, &row.Resource, + &summary.Count, + &summary.ResourceVersion) + } + return err +} diff --git a/pkg/storage/unified/sql/batch_test.go b/pkg/storage/unified/sql/batch_test.go new file mode 100644 index 00000000000..789f9922a12 --- /dev/null +++ b/pkg/storage/unified/sql/batch_test.go @@ -0,0 +1,24 @@ +package sql + +import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestBatch(t *testing.T) { + t.Parallel() + + t.Run("rv iterator", func(t *testing.T) { + t.Parallel() + + rv := newBatchRV() + v0 := rv.next(&unstructured.Unstructured{}) + v1 := rv.next(&unstructured.Unstructured{}) + v2 := rv.next(&unstructured.Unstructured{}) + require.True(t, v0 > 1000) + require.Equal(t, int64(1), v1-v0) + require.Equal(t, int64(1), v2-v1) + }) +} diff --git a/pkg/storage/unified/sql/data/resource_insert_from_history.sql b/pkg/storage/unified/sql/data/resource_insert_from_history.sql new file mode 100644 index 00000000000..2a5f434efd3 --- /dev/null +++ b/pkg/storage/unified/sql/data/resource_insert_from_history.sql @@ -0,0 +1,52 @@ +INSERT INTO {{ .Ident "resource" }} +SELECT + kv.{{ .Ident "guid" }}, + kv.{{ .Ident "resource_version" }}, + kv.{{ .Ident "group" }}, + kv.{{ .Ident "resource" }}, + kv.{{ .Ident "namespace" }}, + kv.{{ .Ident "name" }}, + kv.{{ .Ident "value" }}, + kv.{{ .Ident "action" }}, + kv.{{ .Ident "label_set" }}, + kv.{{ .Ident "previous_resource_version" }}, + kv.{{ .Ident "folder" }} +FROM {{ .Ident "resource_history" }} AS kv + INNER JOIN ( + SELECT {{ .Ident "namespace" }}, {{ .Ident "group" }}, {{ .Ident "resource" }}, {{ .Ident "name" }}, max({{ .Ident "resource_version" }}) AS {{ .Ident "resource_version" }} + FROM {{ .Ident "resource_history" }} AS mkv + WHERE 1 = 1 + {{ if .Key.Namespace }} + AND {{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }} + {{ end }} + {{ if .Key.Group }} + AND {{ .Ident "group" }} = {{ .Arg .Key.Group }} + {{ end }} + {{ if .Key.Resource }} + AND {{ .Ident "resource" }} = {{ .Arg .Key.Resource }} + {{ end }} + {{ if .Key.Name }} + AND {{ .Ident "name" }} = {{ .Arg .Key.Name }} + {{ end }} + GROUP BY mkv.{{ .Ident "namespace" }}, mkv.{{ .Ident "group" }}, mkv.{{ .Ident "resource" }}, mkv.{{ .Ident "name" }} + ) AS maxkv + ON maxkv.{{ .Ident "resource_version" }} = kv.{{ .Ident "resource_version" }} + AND maxkv.{{ .Ident "namespace" }} = kv.{{ .Ident "namespace" }} + AND maxkv.{{ .Ident "group" }} = kv.{{ .Ident "group" }} + AND maxkv.{{ .Ident "resource" }} = kv.{{ .Ident "resource" }} + AND maxkv.{{ .Ident "name" }} = kv.{{ .Ident "name" }} + WHERE kv.{{ .Ident "action" }} != 3 + {{ if .Key.Namespace }} + AND kv.{{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }} + {{ end }} + {{ if .Key.Group }} + AND kv.{{ .Ident "group" }} = {{ .Arg .Key.Group }} + {{ end }} + {{ if .Key.Resource }} + AND kv.{{ .Ident "resource" }} = {{ .Arg .Key.Resource }} + {{ end }} + {{ if .Key.Name }} + AND kv.{{ .Ident "name" }} = {{ .Arg .Key.Name }} + {{ end }} + ORDER BY kv.{{ .Ident "resource_version" }} ASC +; diff --git a/pkg/storage/unified/sql/queries.go b/pkg/storage/unified/sql/queries.go index 0641791fa7f..b91b17791f5 100644 --- a/pkg/storage/unified/sql/queries.go +++ b/pkg/storage/unified/sql/queries.go @@ -44,6 +44,7 @@ var ( sqlResourceHistoryPoll = mustTemplate("resource_history_poll.sql") sqlResourceHistoryGet = mustTemplate("resource_history_get.sql") sqlResourceHistoryDelete = mustTemplate("resource_history_delete.sql") + sqlResourceInsertFromHistory = mustTemplate("resource_insert_from_history.sql") // sqlResourceLabelsInsert = mustTemplate("resource_labels_insert.sql") sqlResourceVersionGet = mustTemplate("resource_version_get.sql") @@ -83,6 +84,18 @@ func (r sqlResourceRequest) Validate() error { return nil // TODO } +type sqlResourceInsertFromHistoryRequest struct { + sqltemplate.SQLTemplate + Key *resource.ResourceKey +} + +func (r sqlResourceInsertFromHistoryRequest) Validate() error { + if r.Key == nil { + return fmt.Errorf("missing key") + } + return nil +} + type sqlStatsRequest struct { sqltemplate.SQLTemplate Namespace string diff --git a/pkg/storage/unified/sql/queries_test.go b/pkg/storage/unified/sql/queries_test.go index ee6e0f45537..e14eaef18d5 100644 --- a/pkg/storage/unified/sql/queries_test.go +++ b/pkg/storage/unified/sql/queries_test.go @@ -385,5 +385,18 @@ func TestUnifiedStorageQueries(t *testing.T) { }, }, }, + sqlResourceInsertFromHistory: { + { + Name: "update", + Data: &sqlResourceInsertFromHistoryRequest{ + SQLTemplate: mocks.NewTestingSQLTemplate(), + Key: &resource.ResourceKey{ + Namespace: "default", + Group: "dashboard.grafana.app", + Resource: "dashboards", + }, + }, + }, + }, }}) } diff --git a/pkg/storage/unified/sql/service.go b/pkg/storage/unified/sql/service.go index 6c05f884b27..6ae039d1c7f 100644 --- a/pkg/storage/unified/sql/service.go +++ b/pkg/storage/unified/sql/service.go @@ -126,7 +126,9 @@ func (s *service) start(ctx context.Context) error { srv := s.handler.GetServer() resource.RegisterResourceStoreServer(srv, server) + resource.RegisterBatchStoreServer(srv, server) resource.RegisterResourceIndexServer(srv, server) + resource.RegisterRepositoryIndexServer(srv, server) resource.RegisterBlobStoreServer(srv, server) resource.RegisterDiagnosticsServer(srv, server) grpc_health_v1.RegisterHealthServer(srv, healthService) diff --git a/pkg/storage/unified/sql/testdata/mysql--resource_insert_from_history-update.sql b/pkg/storage/unified/sql/testdata/mysql--resource_insert_from_history-update.sql new file mode 100755 index 00000000000..a177c394bff --- /dev/null +++ b/pkg/storage/unified/sql/testdata/mysql--resource_insert_from_history-update.sql @@ -0,0 +1,34 @@ +INSERT INTO `resource` +SELECT + kv.`guid`, + kv.`resource_version`, + kv.`group`, + kv.`resource`, + kv.`namespace`, + kv.`name`, + kv.`value`, + kv.`action`, + kv.`label_set`, + kv.`previous_resource_version`, + kv.`folder` +FROM `resource_history` AS kv + INNER JOIN ( + SELECT `namespace`, `group`, `resource`, `name`, max(`resource_version`) AS `resource_version` + FROM `resource_history` AS mkv + WHERE 1 = 1 + AND `namespace` = 'default' + AND `group` = 'dashboard.grafana.app' + AND `resource` = 'dashboards' + GROUP BY mkv.`namespace`, mkv.`group`, mkv.`resource`, mkv.`name` + ) AS maxkv + ON maxkv.`resource_version` = kv.`resource_version` + AND maxkv.`namespace` = kv.`namespace` + AND maxkv.`group` = kv.`group` + AND maxkv.`resource` = kv.`resource` + AND maxkv.`name` = kv.`name` + WHERE kv.`action` != 3 + AND kv.`namespace` = 'default' + AND kv.`group` = 'dashboard.grafana.app' + AND kv.`resource` = 'dashboards' + ORDER BY kv.`resource_version` ASC +; diff --git a/pkg/storage/unified/sql/testdata/postgres--resource_insert_from_history-update.sql b/pkg/storage/unified/sql/testdata/postgres--resource_insert_from_history-update.sql new file mode 100755 index 00000000000..e6439c98074 --- /dev/null +++ b/pkg/storage/unified/sql/testdata/postgres--resource_insert_from_history-update.sql @@ -0,0 +1,34 @@ +INSERT INTO "resource" +SELECT + kv."guid", + kv."resource_version", + kv."group", + kv."resource", + kv."namespace", + kv."name", + kv."value", + kv."action", + kv."label_set", + kv."previous_resource_version", + kv."folder" +FROM "resource_history" AS kv + INNER JOIN ( + SELECT "namespace", "group", "resource", "name", max("resource_version") AS "resource_version" + FROM "resource_history" AS mkv + WHERE 1 = 1 + AND "namespace" = 'default' + AND "group" = 'dashboard.grafana.app' + AND "resource" = 'dashboards' + GROUP BY mkv."namespace", mkv."group", mkv."resource", mkv."name" + ) AS maxkv + ON maxkv."resource_version" = kv."resource_version" + AND maxkv."namespace" = kv."namespace" + AND maxkv."group" = kv."group" + AND maxkv."resource" = kv."resource" + AND maxkv."name" = kv."name" + WHERE kv."action" != 3 + AND kv."namespace" = 'default' + AND kv."group" = 'dashboard.grafana.app' + AND kv."resource" = 'dashboards' + ORDER BY kv."resource_version" ASC +; diff --git a/pkg/storage/unified/sql/testdata/sqlite--resource_insert_from_history-update.sql b/pkg/storage/unified/sql/testdata/sqlite--resource_insert_from_history-update.sql new file mode 100755 index 00000000000..e6439c98074 --- /dev/null +++ b/pkg/storage/unified/sql/testdata/sqlite--resource_insert_from_history-update.sql @@ -0,0 +1,34 @@ +INSERT INTO "resource" +SELECT + kv."guid", + kv."resource_version", + kv."group", + kv."resource", + kv."namespace", + kv."name", + kv."value", + kv."action", + kv."label_set", + kv."previous_resource_version", + kv."folder" +FROM "resource_history" AS kv + INNER JOIN ( + SELECT "namespace", "group", "resource", "name", max("resource_version") AS "resource_version" + FROM "resource_history" AS mkv + WHERE 1 = 1 + AND "namespace" = 'default' + AND "group" = 'dashboard.grafana.app' + AND "resource" = 'dashboards' + GROUP BY mkv."namespace", mkv."group", mkv."resource", mkv."name" + ) AS maxkv + ON maxkv."resource_version" = kv."resource_version" + AND maxkv."namespace" = kv."namespace" + AND maxkv."group" = kv."group" + AND maxkv."resource" = kv."resource" + AND maxkv."name" = kv."name" + WHERE kv."action" != 3 + AND kv."namespace" = 'default' + AND kv."group" = 'dashboard.grafana.app' + AND kv."resource" = 'dashboards' + ORDER BY kv."resource_version" ASC +;