diff --git a/Makefile b/Makefile index 38d2b70e3c7..1d0113e6926 100644 --- a/Makefile +++ b/Makefile @@ -376,8 +376,8 @@ devenv-mysql: .PHONY: protobuf protobuf: ## Compile protobuf definitions bash scripts/protobuf-check.sh - go install google.golang.org/protobuf/cmd/protoc-gen-go - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + # go install google.golang.org/protobuf/cmd/protoc-gen-go + # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest # buf generate pkg/plugins/backendplugin/pluginextensionv2 --template pkg/plugins/backendplugin/pluginextensionv2/buf.gen.yaml # buf generate pkg/plugins/backendplugin/secretsmanagerplugin --template pkg/plugins/backendplugin/secretsmanagerplugin/buf.gen.yaml # buf generate pkg/services/store/entity --template pkg/services/store/entity/buf.gen.yaml diff --git a/pkg/apimachinery/utils/meta.go b/pkg/apimachinery/utils/meta.go index f797cfe9552..71068a950c6 100644 --- a/pkg/apimachinery/utils/meta.go +++ b/pkg/apimachinery/utils/meta.go @@ -1,9 +1,12 @@ package utils import ( + "bytes" "fmt" + "mime" "reflect" "strconv" + "strings" "time" "k8s.io/apimachinery/pkg/api/meta" @@ -20,6 +23,7 @@ const AnnoKeyUpdatedTimestamp = "grafana.app/updatedTimestamp" const AnnoKeyUpdatedBy = "grafana.app/updatedBy" const AnnoKeyFolder = "grafana.app/folder" const AnnoKeySlug = "grafana.app/slug" +const AnnoKeyBlob = "grafana.app/blob" const AnnoKeyMessage = "grafana.app/message" // Identify where values came from @@ -49,6 +53,85 @@ type ResourceOriginInfo struct { _ any `json:"-"` } +type BlobInfo struct { + UID string `json:"uid"` + Size int64 `json:"size,omitempty"` + Hash string `json:"hash,omitempty"` + MimeType string `json:"mime,omitempty"` + Charset string `json:"charset,omitempty"` // content type = mime+charset +} + +// Content type is mime + charset +func (b *BlobInfo) SetContentType(v string) { + var params map[string]string + var err error + + b.Charset = "" + b.MimeType, params, err = mime.ParseMediaType(v) + if err != nil { + return + } + b.Charset = params["charset"] +} + +// Content type is mime + charset +func (b *BlobInfo) ContentType() string { + sb := bytes.NewBufferString(b.MimeType) + if b.Charset != "" { + sb.WriteString("; charset=") + sb.WriteString(b.Charset) + } + return sb.String() +} + +func (b *BlobInfo) String() string { + sb := bytes.NewBufferString(b.UID) + if b.Size > 0 { + sb.WriteString(fmt.Sprintf("; size=%d", b.Size)) + } + if b.Hash != "" { + sb.WriteString("; hash=") + sb.WriteString(b.Hash) + } + if b.MimeType != "" { + sb.WriteString("; mime=") + sb.WriteString(b.MimeType) + } + if b.Charset != "" { + sb.WriteString("; charset=") + sb.WriteString(b.Charset) + } + return sb.String() +} + +func ParseBlobInfo(v string) *BlobInfo { + if v == "" { + return nil + } + info := &BlobInfo{} + for i, part := range strings.Split(v, ";") { + if i == 0 { + info.UID = part + continue + } + kv := strings.Split(strings.TrimSpace(part), "=") + if len(kv) == 2 { + val := kv[1] + switch kv[0] { + case "size": + info.Size, _ = strconv.ParseInt(val, 10, 64) + case "hash": + info.Hash = val + case "mime": + info.MimeType = val + case "charset": + info.Charset = val + } + } + } + return info +} + // Accessor functions for k8s objects type GrafanaMetaAccessor interface { metav1.Object @@ -76,6 +159,9 @@ type GrafanaMetaAccessor interface { GetSlug() string SetSlug(v string) + SetBlob(v *BlobInfo) + GetBlob() *BlobInfo + GetOriginInfo() (*ResourceOriginInfo, error) SetOriginInfo(info *ResourceOriginInfo) GetOriginName() string @@ -195,6 +281,17 @@ func (m *grafanaMetaAccessor) SetUpdatedBy(user string) { m.SetAnnotation(AnnoKeyUpdatedBy, user) } +func (m *grafanaMetaAccessor) GetBlob() *BlobInfo { + return ParseBlobInfo(m.get(AnnoKeyBlob)) +} + +func (m *grafanaMetaAccessor) SetBlob(info *BlobInfo) { + if info == nil { + m.SetAnnotation(AnnoKeyBlob, "") // delete + } + m.SetAnnotation(AnnoKeyBlob, info.String()) +} + func (m *grafanaMetaAccessor) GetFolder() string { return m.get(AnnoKeyFolder) } diff --git a/pkg/apimachinery/utils/meta_test.go b/pkg/apimachinery/utils/meta_test.go index 0e0b81db7f0..3a7965595f0 100644 --- a/pkg/apimachinery/utils/meta_test.go +++ b/pkg/apimachinery/utils/meta_test.go @@ -172,6 +172,14 @@ func TestMetaAccessor(t *testing.T) { require.Equal(t, int64(12345), rv) }) + t.Run("blob info", func(t *testing.T) { + info := &utils.BlobInfo{UID: "AAA", Size: 123, Hash: "xyz", MimeType: "application/json", Charset: "utf-8"} + anno := info.String() + require.Equal(t, "AAA; size=123; hash=xyz; mime=application/json; charset=utf-8", anno) + copy := utils.ParseBlobInfo(anno) + require.Equal(t, info, copy) + }) + t.Run("find titles", func(t *testing.T) { // with a k8s object that has Spec.Title obj := &TestResource{ diff --git a/pkg/services/store/entity/sqlstash/sql_storage_server.go b/pkg/services/store/entity/sqlstash/sql_storage_server.go index 0a7d7cce617..046ed69e4c7 100644 --- a/pkg/services/store/entity/sqlstash/sql_storage_server.go +++ b/pkg/services/store/entity/sqlstash/sql_storage_server.go @@ -25,6 +25,7 @@ import ( "github.com/grafana/grafana/pkg/services/store/entity" "github.com/grafana/grafana/pkg/services/store/entity/db" "github.com/grafana/grafana/pkg/services/store/entity/sqlstash/sqltemplate" + "github.com/grafana/grafana/pkg/storage/unified/resource" ) const entityTable = "entity" @@ -75,7 +76,7 @@ type sqlEntityServer struct { db db.EntityDBInterface // needed to keep xorm engine in scope sess *session.SessionDB dialect migrator.Dialect - broadcaster Broadcaster[*entity.EntityWatchResponse] + broadcaster resource.Broadcaster[*entity.EntityWatchResponse] ctx context.Context // TODO: remove cancel context.CancelFunc tracer trace.Tracer @@ -141,7 +142,7 @@ func (s *sqlEntityServer) init() error { s.dialect = migrator.NewDialect(engine.DriverName()) // set up the broadcaster - s.broadcaster, err = NewBroadcaster(s.ctx, func(stream chan<- *entity.EntityWatchResponse) error { + s.broadcaster, err = resource.NewBroadcaster(s.ctx, func(stream chan<- *entity.EntityWatchResponse) error { // start the poller go s.poller(stream) diff --git a/pkg/storage/unified/entitybridge/entitybridge.go b/pkg/storage/unified/entitybridge/entitybridge.go index a2c216f2118..9b4b8a7504c 100644 --- a/pkg/storage/unified/entitybridge/entitybridge.go +++ b/pkg/storage/unified/entitybridge/entitybridge.go @@ -5,8 +5,7 @@ import ( "fmt" "os" - "github.com/hack-pad/hackpadfs" - hackos "github.com/hack-pad/hackpadfs/os" + "gocloud.dev/blob/fileblob" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/tracing" @@ -16,32 +15,35 @@ import ( "github.com/grafana/grafana/pkg/services/store/entity/sqlstash" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/storage/unified/resource" - "github.com/grafana/grafana/pkg/util" ) // Creates a ResourceServer using the existing entity tables // NOTE: most of the field values are ignored func ProvideResourceServer(db db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer) (resource.ResourceServer, error) { if true { - var root hackpadfs.FS - if false { - tmp, err := os.MkdirTemp("", "xxx-*") - if err != nil { - return nil, err - } + tmp, err := os.MkdirTemp("", "xxx-*") + if err != nil { + return nil, err + } - root, err = hackos.NewFS().Sub(tmp[1:]) - if err != nil { - return nil, err - } + bucket, err := fileblob.OpenBucket(tmp, &fileblob.Options{ + CreateDir: true, + Metadata: fileblob.MetadataDontWrite, // skip + }) + if err != nil { + return nil, err + } - fmt.Printf("ROOT: %s\n", tmp) + fmt.Printf("ROOT: %s\n\n", tmp) + store, err := resource.NewCDKAppendingStore(context.Background(), resource.CDKAppenderOptions{ + Bucket: bucket, + }) + if err != nil { + return nil, err } return resource.NewResourceServer(resource.ResourceServerOptions{ - Store: resource.NewFileSystemStore(resource.FileSystemOptions{ - Root: root, - }), + Store: store, }) } @@ -96,7 +98,7 @@ func (b *entityBridge) WriteEvent(ctx context.Context, event resource.WriteEvent key := toEntityKey(event.Key) // Delete does not need to create an entity first - if event.Event == resource.WatchEvent_DELETED { + if event.Type == resource.WatchEvent_DELETED { rsp, err := b.entity.Delete(ctx, &entity.DeleteEntityRequest{ Key: key, PreviousVersion: event.PreviousRV, @@ -117,15 +119,14 @@ func (b *entityBridge) WriteEvent(ctx context.Context, event resource.WriteEvent Guid: string(event.Object.GetUID()), // Key: fmt.Sprint("%s/%s/%s/%s", ), - Folder: obj.GetFolder(), - Body: event.Value, - Message: event.Message, + Folder: obj.GetFolder(), + Body: event.Value, Labels: obj.GetLabels(), Size: int64(len(event.Value)), } - switch event.Event { + switch event.Type { case resource.WatchEvent_ADDED: msg.Action = entity.Entity_CREATED rsp, err := b.entity.Create(ctx, &entity.CreateEntityRequest{Entity: msg}) @@ -148,15 +149,10 @@ func (b *entityBridge) WriteEvent(ctx context.Context, event resource.WriteEvent default: } - return 0, fmt.Errorf("unsupported operation: %s", event.Event.String()) + return 0, fmt.Errorf("unsupported operation: %s", event.Type.String()) } -// Create new name for a given resource -func (b *entityBridge) GenerateName(_ context.Context, _ *resource.ResourceKey, _ string) (string, error) { - return util.GenerateShortUID(), nil -} - -func (b *entityBridge) Watch(_ context.Context, _ *resource.WatchRequest) (chan *resource.WatchEvent, error) { +func (b *entityBridge) WatchWriteEvents(ctx context.Context) (<-chan *resource.WrittenEvent, error) { return nil, resource.ErrNotImplementedYet } diff --git a/pkg/services/store/entity/sqlstash/broadcaster.go b/pkg/storage/unified/resource/broadcaster.go similarity index 99% rename from pkg/services/store/entity/sqlstash/broadcaster.go rename to pkg/storage/unified/resource/broadcaster.go index 56a010bf2a0..4d459fbefbd 100644 --- a/pkg/services/store/entity/sqlstash/broadcaster.go +++ b/pkg/storage/unified/resource/broadcaster.go @@ -1,4 +1,4 @@ -package sqlstash +package resource import ( "context" diff --git a/pkg/services/store/entity/sqlstash/broadcaster_test.go b/pkg/storage/unified/resource/broadcaster_test.go similarity index 99% rename from pkg/services/store/entity/sqlstash/broadcaster_test.go rename to pkg/storage/unified/resource/broadcaster_test.go index fc6a6369a2d..d6866d7b4c9 100644 --- a/pkg/services/store/entity/sqlstash/broadcaster_test.go +++ b/pkg/storage/unified/resource/broadcaster_test.go @@ -1,4 +1,4 @@ -package sqlstash +package resource import ( "context" diff --git a/pkg/storage/unified/resource/cdk_appender.go b/pkg/storage/unified/resource/cdk_appender.go new file mode 100644 index 00000000000..e97ade7dcd4 --- /dev/null +++ b/pkg/storage/unified/resource/cdk_appender.go @@ -0,0 +1,315 @@ +package resource + +import ( + "bytes" + context "context" + "fmt" + "io" + "math/rand" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/bwmarrin/snowflake" + "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" + "gocloud.dev/blob" + _ "gocloud.dev/blob/fileblob" + _ "gocloud.dev/blob/memblob" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type CDKAppenderOptions struct { + Tracer trace.Tracer + Bucket *blob.Bucket + RootFolder string + + // When running in a cluster, each node should have a different ID + // This is used for snowflake generation and log identification + NodeID int64 + + // Get the next ResourceVersion. When not set, this will default to snowflake IDs + NextResourceVersion func() int64 +} + +func NewCDKAppendingStore(ctx context.Context, opts CDKAppenderOptions) (AppendingStore, error) { + if opts.Tracer == nil { + opts.Tracer = noop.NewTracerProvider().Tracer("cdk-appending-store") + } + + if opts.Bucket == nil { + return nil, fmt.Errorf("missing bucket") + } + + found, _, err := opts.Bucket.ListPage(ctx, blob.FirstPageToken, 1, &blob.ListOptions{ + Prefix: opts.RootFolder, + Delimiter: "/", + }) + if err != nil { + return nil, err + } + if found == nil { + return nil, fmt.Errorf("the root folder does not exist") + } + + // This is not totally safe when running in HA + if opts.NextResourceVersion == nil { + if opts.NodeID == 0 { + opts.NodeID = rand.Int63n(1024) + } + eventNode, err := snowflake.NewNode(opts.NodeID) + if err != nil { + return nil, apierrors.NewInternalError( + fmt.Errorf("error initializing snowflake id generator :: %w", err)) + } + opts.NextResourceVersion = func() int64 { + return eventNode.Generate().Int64() + } + } + + return &cdkAppender{ + tracer: opts.Tracer, + bucket: opts.Bucket, + root: opts.RootFolder, + nextRV: opts.NextResourceVersion, + }, nil +} + +type cdkAppender struct { + tracer trace.Tracer + bucket *blob.Bucket + root string + nextRV func() int64 + mutex sync.Mutex + + // Typically one... the server wrapper + subscribers []chan *WrittenEvent +} + +func (s *cdkAppender) getPath(key *ResourceKey, rv int64) string { + var buffer bytes.Buffer + buffer.WriteString(s.root) + + if key.Group == "" { + return buffer.String() + } + buffer.WriteString(key.Group) + + if key.Resource == "" { + return buffer.String() + } + buffer.WriteString("/") + buffer.WriteString(key.Resource) + + if key.Namespace == "" { + if key.Name == "" { + return buffer.String() + } + buffer.WriteString("/__cluster__") + } else { + buffer.WriteString("/") + buffer.WriteString(key.Namespace) + } + + if key.Name == "" { + return buffer.String() + } + buffer.WriteString("/") + buffer.WriteString(key.Name) + + if rv > 0 { + buffer.WriteString(fmt.Sprintf("/%d.json", rv)) + } + return buffer.String() +} + +func (s *cdkAppender) WriteEvent(ctx context.Context, event WriteEvent) (rv int64, err error) { + // Scope the lock + { + s.mutex.Lock() + defer s.mutex.Unlock() + + rv = s.nextRV() + err = s.bucket.WriteAll(ctx, s.getPath(event.Key, rv), event.Value, &blob.WriterOptions{ + ContentType: "application/json", + }) + } + + // Async notify all subscribers + if s.subscribers != nil { + go func() { + write := &WrittenEvent{ + WriteEvent: event, + + Timestamp: time.Now().UnixMilli(), + ResourceVersion: rv, + } + for _, sub := range s.subscribers { + sub <- write + } + }() + } + + return rv, err +} + +// Read implements ResourceStoreServer. +func (s *cdkAppender) Read(ctx context.Context, req *ReadRequest) (*ReadResponse, error) { + rv := req.ResourceVersion + + path := s.getPath(req.Key, req.ResourceVersion) + if rv < 1 { + iter := s.bucket.List(&blob.ListOptions{Prefix: path + "/", Delimiter: "/"}) + for { + obj, err := iter.Next(ctx) + if err == io.EOF { + break + } + if strings.HasSuffix(obj.Key, ".json") { + idx := strings.LastIndex(obj.Key, "/") + 1 + edx := strings.LastIndex(obj.Key, ".") + if idx > 0 { + v, err := strconv.ParseInt(obj.Key[idx:edx], 10, 64) + if err == nil && v > rv { + rv = v + path = obj.Key // find the path with biggest resource version + } + } + } + } + } + + raw, err := s.bucket.ReadAll(ctx, path) + if err == nil && bytes.Contains(raw, []byte(`"DeletedMarker"`)) { + tmp := &unstructured.Unstructured{} + err = tmp.UnmarshalJSON(raw) + if err == nil && tmp.GetKind() == "DeletedMarker" { + return nil, apierrors.NewNotFound(schema.GroupResource{ + Group: req.Key.Group, + Resource: req.Key.Resource, + }, req.Key.Name) + } + } + + return &ReadResponse{ + ResourceVersion: rv, + Value: raw, + }, err +} + +// List implements AppendingStore. +func (s *cdkAppender) List(ctx context.Context, req *ListRequest) (*ListResponse, error) { + resources, err := buildTree(ctx, s, req.Options.Key) + if err != nil { + return nil, err + } + + rsp := &ListResponse{} + for _, item := range resources { + latest := item.versions[0] + raw, err := s.bucket.ReadAll(ctx, latest.key) + if err != nil { + return nil, err + } + rsp.Items = append(rsp.Items, &ResourceWrapper{ + ResourceVersion: latest.rv, + Value: raw, + }) + } + return rsp, nil +} + +// Watch implements AppendingStore. +func (s *cdkAppender) WatchWriteEvents(ctx context.Context) (<-chan *WrittenEvent, error) { + stream := make(chan *WrittenEvent, 10) + { + s.mutex.Lock() + defer s.mutex.Unlock() + + // Add the event stream + s.subscribers = append(s.subscribers, stream) + } + + // Wait for context done + go func() { + // Wait till the context is done + <-ctx.Done() + + // Then remove the subscription + s.mutex.Lock() + defer s.mutex.Unlock() + + // Copy all streams without our listener + subs := []chan *WrittenEvent{} + for _, sub := range s.subscribers { + if sub != stream { + subs = append(subs, sub) + } + } + s.subscribers = subs + }() + return stream, nil +} + +// group > resource > namespace > name > versions +type cdkResource struct { + prefix string + versions []cdkVersion +} +type cdkVersion struct { + rv int64 + key string +} + +func buildTree(ctx context.Context, s *cdkAppender, key *ResourceKey) ([]cdkResource, error) { + byPrefix := make(map[string]*cdkResource) + + path := s.getPath(key, 0) + iter := s.bucket.List(&blob.ListOptions{Prefix: path, Delimiter: ""}) // "" is recursive + for { + obj, err := iter.Next(ctx) + if err == io.EOF { + break + } + if strings.HasSuffix(obj.Key, ".json") { + idx := strings.LastIndex(obj.Key, "/") + 1 + edx := strings.LastIndex(obj.Key, ".") + if idx > 0 { + rv, err := strconv.ParseInt(obj.Key[idx:edx], 10, 64) + if err == nil { + prefix := obj.Key[:idx] + res, ok := byPrefix[prefix] + if !ok { + res = &cdkResource{prefix: prefix} + byPrefix[prefix] = res + } + + res.versions = append(res.versions, cdkVersion{ + rv: rv, + key: obj.Key, + }) + } + } + } + } + + // Now sort all versions + resources := make([]cdkResource, 0, len(byPrefix)) + for _, res := range byPrefix { + sort.Slice(res.versions, func(i, j int) bool { + return res.versions[i].rv > res.versions[j].rv + }) + resources = append(resources, *res) + } + sort.Slice(resources, func(i, j int) bool { + a := resources[i].versions[0].rv + b := resources[j].versions[0].rv + return a > b + }) + + return resources, nil +} diff --git a/pkg/storage/unified/resource/cdk_blob.go b/pkg/storage/unified/resource/cdk_blob.go new file mode 100644 index 00000000000..e97682e999a --- /dev/null +++ b/pkg/storage/unified/resource/cdk_blob.go @@ -0,0 +1,176 @@ +package resource + +import ( + "bytes" + context "context" + "crypto/md5" + "encoding/hex" + "fmt" + "mime" + "time" + + "github.com/google/uuid" + "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" + "gocloud.dev/blob" + _ "gocloud.dev/blob/fileblob" + _ "gocloud.dev/blob/memblob" + + "github.com/grafana/grafana/pkg/apimachinery/utils" +) + +type CDKBlobStoreOptions struct { + Tracer trace.Tracer + Bucket *blob.Bucket + RootFolder string + URLExpiration time.Duration +} + +func NewCDKBlobStore(ctx context.Context, opts CDKBlobStoreOptions) (BlobStore, error) { + if opts.Tracer == nil { + opts.Tracer = noop.NewTracerProvider().Tracer("cdk-blob-store") + } + + if opts.Bucket == nil { + return nil, fmt.Errorf("missing bucket") + } + if opts.URLExpiration < 1 { + opts.URLExpiration = time.Minute * 10 // 10 min default + } + + found, _, err := opts.Bucket.ListPage(ctx, blob.FirstPageToken, 1, &blob.ListOptions{ + Prefix: opts.RootFolder, + Delimiter: "/", + }) + if err != nil { + return nil, err + } + if found == nil { + return nil, fmt.Errorf("the root folder does not exist") + } + + return &cdkBlobStore{ + tracer: opts.Tracer, + bucket: opts.Bucket, + root: opts.RootFolder, + cansignurls: false, // TODO depends on the implementation + expiration: opts.URLExpiration, + }, nil +} + +type cdkBlobStore struct { + tracer trace.Tracer + bucket *blob.Bucket + root string + cansignurls bool + expiration time.Duration +} + +func (s *cdkBlobStore) getBlobPath(key *ResourceKey, info *utils.BlobInfo) (string, error) { + var buffer bytes.Buffer + buffer.WriteString(s.root) + + if key.Namespace == "" { + buffer.WriteString("__cluster__/") + } else { + buffer.WriteString(key.Namespace) + buffer.WriteString("/") + } + + if key.Group == "" { + return "", fmt.Errorf("missing group") + } + buffer.WriteString(key.Group) + buffer.WriteString("/") + + if key.Resource == "" { + return "", fmt.Errorf("missing resource") + } + buffer.WriteString(key.Resource) + buffer.WriteString("/") + + if key.Name == "" { + return "", fmt.Errorf("missing name") + } + buffer.WriteString(key.Name) + buffer.WriteString("/") + buffer.WriteString(info.UID) + + ext, err := mime.ExtensionsByType(info.MimeType) + if err != nil { + return "", err + } + if len(ext) > 0 { + buffer.WriteString(ext[0]) + } + return buffer.String(), nil +} + +func (s *cdkBlobStore) SupportsSignedURLs() bool { + return s.cansignurls +} + +func (s *cdkBlobStore) PutBlob(ctx context.Context, req *PutBlobRequest) (*PutBlobResponse, error) { + info := &utils.BlobInfo{ + UID: uuid.New().String(), + } + info.SetContentType(req.ContentType) + path, err := s.getBlobPath(req.Resource, info) + if err != nil { + return nil, err + } + + rsp := &PutBlobResponse{Uid: info.UID, MimeType: info.MimeType, Charset: info.Charset} + if req.Method == PutBlobRequest_HTTP { + rsp.Url, err = s.bucket.SignedURL(ctx, path, &blob.SignedURLOptions{ + Method: "PUT", + Expiry: s.expiration, + ContentType: req.ContentType, + }) + return rsp, err + } + if len(req.Value) < 1 { + return nil, fmt.Errorf("missing content value") + } + + // Write the value + err = s.bucket.WriteAll(ctx, path, req.Value, &blob.WriterOptions{ + ContentType: req.ContentType, + }) + if err != nil { + return nil, err + } + + attrs, err := s.bucket.Attributes(ctx, path) + if err != nil { + return nil, err + } + rsp.Size = attrs.Size + + // Set the MD5 hash if missing + if len(attrs.MD5) == 0 { + h := md5.New() + _, _ = h.Write(req.Value) + attrs.MD5 = h.Sum(nil) + } + rsp.Hash = hex.EncodeToString(attrs.MD5[:]) + return rsp, err +} + +func (s *cdkBlobStore) GetBlob(ctx context.Context, resource *ResourceKey, info *utils.BlobInfo, mustProxy bool) (*GetBlobResponse, error) { + path, err := s.getBlobPath(resource, info) + if err != nil { + return nil, err + } + rsp := &GetBlobResponse{ContentType: info.ContentType()} + if mustProxy || !s.cansignurls { + rsp.Value, err = s.bucket.ReadAll(ctx, path) + return rsp, err + } + rsp.Url, err = s.bucket.SignedURL(ctx, path, &blob.SignedURLOptions{ + Method: "GET", + Expiry: s.expiration, + ContentType: rsp.ContentType, + }) + return rsp, err +} diff --git a/pkg/storage/unified/resource/cdk_blob_test.go b/pkg/storage/unified/resource/cdk_blob_test.go new file mode 100644 index 00000000000..a0ce25a3ff8 --- /dev/null +++ b/pkg/storage/unified/resource/cdk_blob_test.go @@ -0,0 +1,67 @@ +package resource + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" + "gocloud.dev/blob/fileblob" + "gocloud.dev/blob/memblob" + + "github.com/grafana/grafana/pkg/apimachinery/utils" +) + +func TestCDKBlobStore(t *testing.T) { + bucket := memblob.OpenBucket(nil) + if false { + tmp, err := os.MkdirTemp("", "xxx-*") + require.NoError(t, err) + + bucket, err = fileblob.OpenBucket(tmp, &fileblob.Options{ + CreateDir: true, + Metadata: fileblob.MetadataDontWrite, // skip + }) + require.NoError(t, err) + + fmt.Printf("ROOT: %s\n\n", tmp) + } + ctx := context.Background() + + store, err := NewCDKBlobStore(ctx, CDKBlobStoreOptions{ + Bucket: bucket, + //RootFolder: "xyz", + }) + require.NoError(t, err) + + t.Run("can write then read a blob", func(t *testing.T) { + raw := testdata(t, "01_create_playlist.json") + key := &ResourceKey{ + Group: "playlist.grafana.app", + Resource: "rrrr", // can be anything + Namespace: "default", + Name: "fdgsv37qslr0ga", + } + + rsp, err := store.PutBlob(ctx, &PutBlobRequest{ + Resource: key, + Method: PutBlobRequest_GRPC, + ContentType: "application/json", + Value: raw, + }) + require.NoError(t, err) + require.Equal(t, "4933beea0c6d6dfd73150451098c70f0", rsp.Hash) + + found, err := store.GetBlob(ctx, key, &utils.BlobInfo{ + UID: rsp.Uid, + Size: rsp.Size, + Hash: rsp.Hash, + MimeType: rsp.MimeType, + Charset: rsp.Charset, + }, false) + require.NoError(t, err) + require.Equal(t, raw, found.Value) + require.Equal(t, "application/json", found.ContentType) + }) +} diff --git a/pkg/storage/unified/resource/event.go b/pkg/storage/unified/resource/event.go index eca6488682b..3c7a448e435 100644 --- a/pkg/storage/unified/resource/event.go +++ b/pkg/storage/unified/resource/event.go @@ -11,16 +11,26 @@ import ( type WriteEvent struct { EventID int64 - Event WatchEvent_Type // ADDED, MODIFIED, DELETED + Type WatchEvent_Type // ADDED, MODIFIED, DELETED Key *ResourceKey // the request key PreviousRV int64 // only for Update+Delete - Message string // commit message - - // Access to raw metadata - Object utils.GrafanaMetaAccessor // The json payload (without resourceVersion) Value []byte + + // Access real fields + Object utils.GrafanaMetaAccessor +} + +// WriteEvents after they include a resource version +type WrittenEvent struct { + WriteEvent + + // The resource version + ResourceVersion int64 + + // Timestamp when the event is created + Timestamp int64 } // A function to write events @@ -29,7 +39,7 @@ type EventAppender = func(context.Context, *WriteEvent) (int64, error) type writeEventBuilder struct { EventID int64 Key *ResourceKey // the request key - Event WatchEvent_Type + Type WatchEvent_Type Requester identity.Requester Object *unstructured.Unstructured @@ -53,9 +63,9 @@ func newEventFromBytes(value, oldValue []byte) (*writeEventBuilder, error) { } if oldValue == nil { - builder.Event = WatchEvent_ADDED + builder.Type = WatchEvent_ADDED } else { - builder.Event = WatchEvent_MODIFIED + builder.Type = WatchEvent_MODIFIED temp := &unstructured.Unstructured{} err = temp.UnmarshalJSON(oldValue) @@ -73,7 +83,7 @@ func newEventFromBytes(value, oldValue []byte) (*writeEventBuilder, error) { func (b *writeEventBuilder) toEvent() (event WriteEvent, err error) { event.EventID = b.EventID event.Key = b.Key - event.Event = b.Event + event.Type = b.Type event.Object = b.Meta event.Value, err = b.Object.MarshalJSON() return // includes the named values diff --git a/pkg/storage/unified/resource/fs.go b/pkg/storage/unified/resource/fs.go deleted file mode 100644 index cfab188a4cc..00000000000 --- a/pkg/storage/unified/resource/fs.go +++ /dev/null @@ -1,159 +0,0 @@ -package resource - -import ( - "context" - "encoding/json" - "fmt" - "path/filepath" - "sort" - "strconv" - "strings" - - "github.com/google/uuid" - "github.com/hack-pad/hackpadfs" - "github.com/hack-pad/hackpadfs/mem" - "go.opentelemetry.io/otel/trace" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/grafana/grafana/pkg/apimachinery/utils" -) - -type FileSystemOptions struct { - // OTel tracer - Tracer trace.Tracer - - // Root file system -- null will be in memory - Root hackpadfs.FS -} - -func NewFileSystemStore(opts FileSystemOptions) AppendingStore { - root := opts.Root - if root == nil { - root, _ = mem.NewFS() - } - - return &fsStore{ - root: root, - keys: &simpleConverter{}, // not tenant isolated - } -} - -type fsStore struct { - root hackpadfs.FS - keys KeyConversions -} - -// The only write command -func (f *fsStore) WriteEvent(ctx context.Context, event WriteEvent) (int64, error) { - // For this case, we will treat them the same - dir, err := f.keys.KeyToPath(event.Key, 0) - if err != nil { - return 0, err - } - err = hackpadfs.MkdirAll(f.root, dir, 0750) - if err != nil { - return 0, err - } - - fpath := filepath.Join(dir, fmt.Sprintf("%d.json", event.EventID)) - file, err := hackpadfs.OpenFile(f.root, fpath, hackpadfs.FlagWriteOnly|hackpadfs.FlagCreate, 0750) - if err != nil { - return 0, err - } - _, err = hackpadfs.WriteFile(file, event.Value) - return event.EventID, err -} - -// Create new name for a given resource -func (f *fsStore) GenerateName(ctx context.Context, key *ResourceKey, prefix string) (string, error) { - // TODO... shorter and make sure it does not exist - return prefix + "x" + uuid.New().String(), nil -} - -// Read implements ResourceStoreServer. -func (f *fsStore) Read(ctx context.Context, req *ReadRequest) (*ReadResponse, error) { - rv := req.ResourceVersion - - fname := "--x--" - dir, err := f.keys.KeyToPath(req.Key, 0) - if err != nil { - return nil, err - } - if rv > 0 { - fname = fmt.Sprintf("%d.json", rv) - } else { - files, err := hackpadfs.ReadDir(f.root, dir) - if err != nil { - return nil, err - } - - // Sort by name - sort.Slice(files, func(i, j int) bool { - a := files[i].Name() - b := files[j].Name() - return a > b // ?? should we parse the numbers ??? - }) - - // The first matching file - for _, v := range files { - fname = v.Name() - if strings.HasSuffix(fname, ".json") { - // The RV is encoded in the path - rv, err = strconv.ParseInt(strings.TrimSuffix(fname, ".json"), 10, 64) - if err != nil { - return nil, err - } - - break - } - } - } - - raw, err := hackpadfs.ReadFile(f.root, filepath.Join(dir, fname)) - if err != nil { - return nil, err - } - if req.ResourceVersion < 1 { - // The operation is inside the body, so need to inspect :( - tmp := &metav1.PartialObjectMetadata{} - err = json.Unmarshal(raw, tmp) - if err != nil { - return nil, err - } - meta, err := utils.MetaAccessor(tmp) - if err != nil { - return nil, err - } - if meta.GetGroupVersionKind().Kind == "DeletedMarker" { - return nil, apierrors.NewNotFound(schema.GroupResource{ - Group: req.Key.Group, - Resource: req.Key.Resource, - }, req.Key.Name) - } - } - - return &ReadResponse{ - ResourceVersion: rv, - Value: raw, - }, nil -} - -// List implements AppendingStore. -func (f *fsStore) List(ctx context.Context, req *ListRequest) (*ListResponse, error) { - tree := eventTree{ - group: req.Options.Key.Group, - resource: req.Options.Key.Resource, - } - _ = tree.read(f.root, req.Options.Key) - // if err != nil { - // return nil, err - // } - return tree.list(f, req.ResourceVersion) -} - -// Watch implements AppendingStore. -func (f *fsStore) Watch(context.Context, *WatchRequest) (chan *WatchEvent, error) { - panic("unimplemented") -} diff --git a/pkg/storage/unified/resource/fs_tree.go b/pkg/storage/unified/resource/fs_tree.go deleted file mode 100644 index 85aa98c3696..00000000000 --- a/pkg/storage/unified/resource/fs_tree.go +++ /dev/null @@ -1,195 +0,0 @@ -package resource - -import ( - "fmt" - "io/fs" - "sort" - "strconv" - "strings" - - "github.com/hack-pad/hackpadfs" -) - -// VERY VERY early, hacky file system reader -type eventTree struct { - path string - group string - resource string - namespaces []namespaceEvents -} - -func (t *eventTree) list(fs *fsStore, rv int64) (*ListResponse, error) { - rsp := &ListResponse{} - for idx, ns := range t.namespaces { - if idx == 0 { - rsp.ResourceVersion = ns.version() - } - err := ns.append(fs, rv, rsp) - if err != nil { - return rsp, err - } - } - return rsp, nil -} - -func (t *eventTree) read(root fs.FS, key *ResourceKey) error { - t.group = key.Group - t.resource = key.Resource - t.path = fmt.Sprintf("%s/%s", t.group, t.resource) - - // Cluster scoped, with an explicit name - if key.Namespace == "" { - if key.Name != "" { - ns := namespaceEvents{ - path: t.path + "/__cluster__", - namespace: "", - } - err := ns.read(root, key) - if err == nil { - t.namespaces = append(t.namespaces, ns) - } - return err - } - } - - files, err := hackpadfs.ReadDir(root, t.path) - if err != nil { - return err - } - for _, file := range files { - ns := namespaceEvents{ - path: t.path + "/" + file.Name(), - namespace: file.Name(), - } - err = ns.read(root, key) - if err != nil { - return err - } - t.namespaces = append(t.namespaces, ns) - } - - return nil -} - -type namespaceEvents struct { - path string - namespace string - names []nameEvents -} - -func (t *namespaceEvents) version() int64 { - if len(t.names) > 0 { - return t.names[0].version() - } - return 0 -} - -func (t *namespaceEvents) append(fs *fsStore, rv int64, rsp *ListResponse) error { - for _, name := range t.names { - err := name.append(fs, rv, rsp) - if err != nil { - return err - } - } - return nil -} - -func (t *namespaceEvents) read(root fs.FS, key *ResourceKey) error { - if key.Name != "" { - vv := nameEvents{ - path: t.path + "/" + key.Name, - name: key.Name, - } - err := vv.read(root) - if err != nil { - return err - } - t.names = []nameEvents{vv} - } - - files, err := hackpadfs.ReadDir(root, t.path) - if err != nil { - return err - } - for _, file := range files { - ns := nameEvents{ - path: t.path + "/" + file.Name(), - name: file.Name(), - } - err = ns.read(root) - if err != nil { - return err - } - t.names = append(t.names, ns) - } - return nil -} - -type nameEvents struct { - path string - name string - versions []resourceEvent -} - -func (t *nameEvents) version() int64 { - if len(t.versions) > 0 { - return t.versions[0].rv - } - return 0 -} - -func (t *nameEvents) append(fs *fsStore, rv int64, rsp *ListResponse) error { - if rv > 0 { - fmt.Printf("TODO... check explicit rv") - } - - for _, rev := range t.versions { - raw, err := hackpadfs.ReadFile(fs.root, t.path+"/"+rev.file) - if err != nil { - return err - } - - wrapper := &ResourceWrapper{ - ResourceVersion: rev.rv, - Value: raw, - // Operation: val.Operation, - } - rsp.Items = append(rsp.Items, wrapper) - if true { - return nil - } - } - return nil -} - -func (t *nameEvents) read(root fs.FS) error { - var err error - files, err := hackpadfs.ReadDir(root, t.path) - if err != nil { - return err - } - for _, file := range files { - p := file.Name() - if file.IsDir() || !strings.HasSuffix(p, ".json") { - continue - } - - base := strings.TrimSuffix(p, ".json") - base = strings.TrimPrefix(base, "rv") - rr := resourceEvent{file: p} - rr.rv, err = strconv.ParseInt(base, 10, 64) - if err != nil { - return err - } - t.versions = append(t.versions, rr) - } - sort.Slice(t.versions, func(i int, j int) bool { - return t.versions[i].rv > t.versions[j].rv - }) - return err -} - -type resourceEvent struct { - file string // path to the actual file - rv int64 -} diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 92a726e54e8..1f6433f16df 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -5,27 +5,32 @@ go 1.21.10 require ( github.com/bwmarrin/snowflake v0.3.0 github.com/fullstorydev/grpchan v1.1.1 + github.com/google/uuid v1.6.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240613114114-5e2f08de316d github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/prometheus/client_golang v1.19.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/otel/trace v1.26.0 + gocloud.dev v0.25.0 google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.1 k8s.io/apimachinery v0.29.3 - github.com/hack-pad/hackpadfs v0.2.1 ) require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/storage v1.38.0 // indirect + github.com/aws/aws-sdk-go v1.51.31 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bufbuild/protocompile v0.4.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/jhump/protoreflect v1.15.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kr/text v0.2.0 // indirect @@ -35,11 +40,18 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.14.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect go.opentelemetry.io/otel v1.26.0 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/api v0.176.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -48,4 +60,5 @@ require ( k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 3a15b5ffe19..b73e80b3f4e 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -1,19 +1,51 @@ +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go/auth v0.2.2 h1:gmxNJs4YZYcw6YvKRtVBaF2fyUE6UrWPyzU8jHvYfmI= +cloud.google.com/go/auth/oauth2adapt v0.2.1 h1:VSPmMmUlT8CkIZ2PzD9AlLN+R3+D1clXMWHHa6vG/Ag= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +github.com/aws/aws-sdk-go v1.51.31 h1:4TM+sNc+Dzs7wY1sJ0+J8i60c6rkgnKP1pvPx8ghsSY= +github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU= +github.com/aws/aws-sdk-go-v2/config v1.15.3 h1:5AlQD0jhVXlGzwo+VORKiUuogkG7pQcLJNzIzK7eodw= +github.com/aws/aws-sdk-go-v2/credentials v1.11.2 h1:RQQ5fzclAKJyY5TvF+fkjJEwzK4hnxQCLOu5JXzDmQo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3 h1:LWPg5zjHV9oz/myQr4wMs0gi4CjnDN/ILmyZUFYXZsU= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.3 h1:ir7iEq78s4txFGgwcLqD6q9IIPzTQNRJXulJd9h/zQo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9 h1:onz/VaaxZ7Z4V+WIN9Txly9XLTmoOh1oJ8XcAC3pako= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3 h1:9stUQR/u2KXU6HkFJYlqnZEjBnbgrVbG6I5HN09xZh0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10 h1:by9P+oy3P/CwggN4ClnW2D4oL91QV7pBzBICi1chZvQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1 h1:T4pFel53bkHjL2mMo+4DKE6r6AuoZnM0fg7k1/ratr4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3 h1:I0dcwWitE752hVSMrsLCxqNQ+UdEp3nACx2bYNMQq+k= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3 h1:Gh1Gpyh01Yvn7ilO/b/hr01WgNpaszfbKMUgqM186xQ= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3 h1:BKjwCJPnANbkwQ8vzSbaZDKawwagDubrH/z/c0X+kbQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3 h1:rMPtwA7zzkSQZhhz9U3/SoIDz/NZ7Q+iRn4EIO8rSyU= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.3 h1:frW4ikGcxfAEDfmQqWgMLp+F1n4nRo9sF39OcIb5BkQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.3 h1:cJGRyzCSVwZC7zZZ1xbx9m32UnrKydRYhOvcD1NYP9Q= +github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6TZQas= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240613114114-5e2f08de316d h1:/UE5JdF+0hxll7EuuO7zRzAxXrvAxQo5M9eqOepc2mQ= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= -github.com/hack-pad/hackpadfs v0.2.1 h1:FelFhIhv26gyjujoA/yeFO+6YGlqzmc9la/6iKMIxMw= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -27,13 +59,24 @@ github.com/prometheus/procfs v0.14.0 h1:Lw4VdGGoKEZilJsayHf0B+9YgLGREba2C6xr+Fdf github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +gocloud.dev v0.25.0 h1:Y7vDq8xj7SyM848KXf32Krda2e6jQ4CLh/mTeCSqXtk= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +google.golang.org/api v0.176.0 h1:dHj1/yv5Dm/eQTXiP9hNCRT3xzJHWXeNdRq29XbMxoE= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= @@ -46,4 +89,4 @@ k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/pkg/storage/unified/resource/keys.go b/pkg/storage/unified/resource/keys.go index 3e3eea3d9b3..319cf107f5b 100644 --- a/pkg/storage/unified/resource/keys.go +++ b/pkg/storage/unified/resource/keys.go @@ -1,109 +1,17 @@ package resource -import ( - "bytes" - "fmt" - "strconv" - "strings" -) - -type KeyConversions interface { - KeyToPath(k *ResourceKey, rv int64) (string, error) - PathToKey(p string) (k *ResourceKey, rv int64, err error) - PathPrefix(k *ResourceKey) string -} - -var _ KeyConversions = &simpleConverter{} - -// group/resource/namespace/name -type simpleConverter struct{} - -// KeyToPath implements KeyConversions. -func (s *simpleConverter) KeyToPath(x *ResourceKey, rv int64) (string, error) { - var buffer bytes.Buffer - - if x.Group == "" { - return "", fmt.Errorf("missing group") - } - buffer.WriteString(x.Group) - buffer.WriteString("/") - - if x.Resource == "" { - return "", fmt.Errorf("missing resource") - } - buffer.WriteString(x.Resource) - buffer.WriteString("/") - - if x.Namespace == "" { - buffer.WriteString("__cluster__") - } else { - buffer.WriteString(x.Namespace) - } - - if x.Name == "" { - return buffer.String(), nil - } - buffer.WriteString("/") - buffer.WriteString(x.Name) - - if rv > 0 { - buffer.WriteString("/") - buffer.WriteString(fmt.Sprintf("%.20d", rv)) - } - - return buffer.String(), nil -} - -// KeyToPath implements KeyConversions. -func (s *simpleConverter) PathPrefix(x *ResourceKey) string { - var buffer bytes.Buffer - if x.Group == "" { - return "" - } - buffer.WriteString(x.Group) - - if x.Resource == "" { - return buffer.String() - } - buffer.WriteString("/") - buffer.WriteString(x.Resource) - - if x.Namespace == "" { - if x.Name == "" { - return buffer.String() - } - buffer.WriteString("/__cluster__") - } else { - buffer.WriteString("/") - buffer.WriteString(x.Namespace) - } - - if x.Name == "" { - return buffer.String() - } - buffer.WriteString("/") - buffer.WriteString(x.Name) - return buffer.String() -} - -// PathToKey implements KeyConversions. -func (s *simpleConverter) PathToKey(p string) (k *ResourceKey, rv int64, err error) { - key := &ResourceKey{} - parts := strings.Split(p, "/") - if len(parts) < 2 { - return nil, 0, fmt.Errorf("expecting at least group/resource") - } - key.Group = parts[0] - key.Resource = parts[1] - if len(parts) > 2 { - key.Namespace = parts[2] - } - if len(parts) > 3 { - key.Name = parts[3] - } - if len(parts) > 4 { - parts = strings.Split(parts[4], ".") - rv, err = strconv.ParseInt(parts[0], 10, 64) - } - return key, rv, err +func matchesQueryKey(query *ResourceKey, key *ResourceKey) bool { + if query.Group != key.Group { + return false + } + if query.Resource != key.Resource { + return false + } + if query.Namespace != "" && query.Namespace != key.Namespace { + return false + } + if query.Name != "" && query.Name != key.Name { + return false + } + return true } diff --git a/pkg/storage/unified/resource/keys_test.go b/pkg/storage/unified/resource/keys_test.go index f65fc4f1f7b..deaaff5bedb 100644 --- a/pkg/storage/unified/resource/keys_test.go +++ b/pkg/storage/unified/resource/keys_test.go @@ -6,25 +6,16 @@ import ( "github.com/stretchr/testify/require" ) -func TestKeyConversions(t *testing.T) { - t.Run("key namespaced path", func(t *testing.T) { - conv := &simpleConverter{} - key := &ResourceKey{ +func TestKeyMatching(t *testing.T) { + t.Run("key matching", func(t *testing.T) { + require.True(t, matchesQueryKey(&ResourceKey{ + Group: "ggg", + Resource: "rrr", + Namespace: "ns", + }, &ResourceKey{ Group: "ggg", Resource: "rrr", Namespace: "ns", - } - p, err := conv.KeyToPath(key, 0) - require.NoError(t, err) - require.Equal(t, "ggg/rrr/ns", p) - - key.Name = "name" - p, err = conv.KeyToPath(key, 0) - require.NoError(t, err) - require.Equal(t, "ggg/rrr/ns/name", p) - require.Equal(t, "ggg/rrr", conv.PathPrefix(&ResourceKey{ - Group: "ggg", - Resource: "rrr", })) }) } diff --git a/pkg/storage/unified/resource/noop.go b/pkg/storage/unified/resource/noop.go index 38ae8348f3b..ee204fd791a 100644 --- a/pkg/storage/unified/resource/noop.go +++ b/pkg/storage/unified/resource/noop.go @@ -1,6 +1,10 @@ package resource -import "context" +import ( + "context" + + "github.com/grafana/grafana/pkg/apimachinery/utils" +) var ( _ ResourceSearchServer = &noopService{} @@ -39,11 +43,6 @@ func (n *noopService) List(context.Context, *ListRequest) (*ListResponse, error) return nil, ErrNotImplementedYet } -// GetBlob implements ResourceServer. -func (n *noopService) GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) { - return nil, ErrNotImplementedYet -} - // History implements ResourceServer. func (n *noopService) History(context.Context, *HistoryRequest) (*HistoryResponse, error) { return nil, ErrNotImplementedYet @@ -53,3 +52,15 @@ func (n *noopService) History(context.Context, *HistoryRequest) (*HistoryRespons func (n *noopService) Origin(context.Context, *OriginRequest) (*OriginResponse, error) { return nil, ErrNotImplementedYet } + +func (n *noopService) SupportsSignedURLs() bool { + return false +} + +func (n *noopService) PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error) { + return nil, ErrNotImplementedYet +} + +func (n *noopService) GetBlob(ctx context.Context, resource *ResourceKey, info *utils.BlobInfo, mustProxy bool) (*GetBlobResponse, error) { + return nil, ErrNotImplementedYet +} diff --git a/pkg/storage/unified/resource/resource.pb.go b/pkg/storage/unified/resource/resource.pb.go index b83507604b0..54849657f00 100644 --- a/pkg/storage/unified/resource/resource.pb.go +++ b/pkg/storage/unified/resource/resource.pb.go @@ -66,61 +66,6 @@ func (ResourceVersionMatch) EnumDescriptor() ([]byte, []int) { return file_resource_proto_rawDescGZIP(), []int{0} } -type LinkBlob_Action int32 - -const ( - LinkBlob_UNKNOWN LinkBlob_Action = 0 - // Upload raw bytes - LinkBlob_UPLOAD LinkBlob_Action = 1 - // Keep the existing blob (valid for updates) - LinkBlob_KEEP LinkBlob_Action = 2 - // Do not keep the existing version (same as not sending LinkBlob, only valid for updates) - LinkBlob_REMOVE LinkBlob_Action = 3 // TODO... support presigned uploads -) - -// Enum value maps for LinkBlob_Action. -var ( - LinkBlob_Action_name = map[int32]string{ - 0: "UNKNOWN", - 1: "UPLOAD", - 2: "KEEP", - 3: "REMOVE", - } - LinkBlob_Action_value = map[string]int32{ - "UNKNOWN": 0, - "UPLOAD": 1, - "KEEP": 2, - "REMOVE": 3, - } -) - -func (x LinkBlob_Action) Enum() *LinkBlob_Action { - p := new(LinkBlob_Action) - *p = x - return p -} - -func (x LinkBlob_Action) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (LinkBlob_Action) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[1].Descriptor() -} - -func (LinkBlob_Action) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[1] -} - -func (x LinkBlob_Action) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use LinkBlob_Action.Descriptor instead. -func (LinkBlob_Action) EnumDescriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{5, 0} -} - type Sort_Order int32 const ( @@ -151,11 +96,11 @@ func (x Sort_Order) String() string { } func (Sort_Order) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[2].Descriptor() + return file_resource_proto_enumTypes[1].Descriptor() } func (Sort_Order) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[2] + return &file_resource_proto_enumTypes[1] } func (x Sort_Order) Number() protoreflect.EnumNumber { @@ -164,7 +109,7 @@ func (x Sort_Order) Number() protoreflect.EnumNumber { // Deprecated: Use Sort_Order.Descriptor instead. func (Sort_Order) EnumDescriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{17, 0} + return file_resource_proto_rawDescGZIP(), []int{13, 0} } type WatchEvent_Type int32 @@ -209,11 +154,11 @@ func (x WatchEvent_Type) String() string { } func (WatchEvent_Type) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[3].Descriptor() + return file_resource_proto_enumTypes[2].Descriptor() } func (WatchEvent_Type) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[3] + return &file_resource_proto_enumTypes[2] } func (x WatchEvent_Type) Number() protoreflect.EnumNumber { @@ -222,7 +167,7 @@ func (x WatchEvent_Type) Number() protoreflect.EnumNumber { // Deprecated: Use WatchEvent_Type.Descriptor instead. func (WatchEvent_Type) EnumDescriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{22, 0} + return file_resource_proto_rawDescGZIP(), []int{18, 0} } type HealthCheckResponse_ServingStatus int32 @@ -261,11 +206,11 @@ func (x HealthCheckResponse_ServingStatus) String() string { } func (HealthCheckResponse_ServingStatus) Descriptor() protoreflect.EnumDescriptor { - return file_resource_proto_enumTypes[4].Descriptor() + return file_resource_proto_enumTypes[3].Descriptor() } func (HealthCheckResponse_ServingStatus) Type() protoreflect.EnumType { - return &file_resource_proto_enumTypes[4] + return &file_resource_proto_enumTypes[3] } func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber { @@ -274,7 +219,55 @@ 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{29, 0} + return file_resource_proto_rawDescGZIP(), []int{25, 0} +} + +type PutBlobRequest_Method int32 + +const ( + // Use the inline raw []byte + PutBlobRequest_GRPC PutBlobRequest_Method = 0 + // Get a signed URL and PUT the value + PutBlobRequest_HTTP PutBlobRequest_Method = 1 +) + +// Enum value maps for PutBlobRequest_Method. +var ( + PutBlobRequest_Method_name = map[int32]string{ + 0: "GRPC", + 1: "HTTP", + } + PutBlobRequest_Method_value = map[string]int32{ + "GRPC": 0, + "HTTP": 1, + } +) + +func (x PutBlobRequest_Method) Enum() *PutBlobRequest_Method { + p := new(PutBlobRequest_Method) + *p = x + return p +} + +func (x PutBlobRequest_Method) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PutBlobRequest_Method) Descriptor() protoreflect.EnumDescriptor { + return file_resource_proto_enumTypes[4].Descriptor() +} + +func (PutBlobRequest_Method) Type() protoreflect.EnumType { + return &file_resource_proto_enumTypes[4] +} + +func (x PutBlobRequest_Method) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PutBlobRequest_Method.Descriptor instead. +func (PutBlobRequest_Method) EnumDescriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{26, 0} } type ResourceKey struct { @@ -361,8 +354,6 @@ type ResourceWrapper struct { ResourceVersion int64 `protobuf:"varint,1,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` // Full kubernetes json bytes (although the resource version may not be accurate) Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - // The resource has an attached blob - HasBlob bool `protobuf:"varint,4,opt,name=has_blob,json=hasBlob,proto3" json:"has_blob,omitempty"` } func (x *ResourceWrapper) Reset() { @@ -411,13 +402,6 @@ func (x *ResourceWrapper) GetValue() []byte { return nil } -func (x *ResourceWrapper) GetHasBlob() bool { - if x != nil { - return x.HasBlob - } - return false -} - // The history and trash commands need access to commit messages type ResourceMeta struct { state protoimpl.MessageState @@ -433,8 +417,6 @@ type ResourceMeta struct { // The kubernetes metadata section (not the full resource) // https://github.com/kubernetes/kubernetes/blob/v1.30.2/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go#L1496 PartialObjectMeta []byte `protobuf:"bytes,6,opt,name=partial_object_meta,json=partialObjectMeta,proto3" json:"partial_object_meta,omitempty"` - // The resource has an attached blob - HasBlob bool `protobuf:"varint,7,opt,name=has_blob,json=hasBlob,proto3" json:"has_blob,omitempty"` } func (x *ResourceMeta) Reset() { @@ -497,89 +479,6 @@ func (x *ResourceMeta) GetPartialObjectMeta() []byte { return nil } -func (x *ResourceMeta) GetHasBlob() bool { - if x != nil { - return x.HasBlob - } - return false -} - -// Basic blob metadata -type BlobInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // System identifier - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - // Content Length - Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` - // Content type header - ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` - // Content hash used for an etag - Hash string `protobuf:"bytes,4,opt,name=hash,proto3" json:"hash,omitempty"` -} - -func (x *BlobInfo) Reset() { - *x = BlobInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BlobInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BlobInfo) ProtoMessage() {} - -func (x *BlobInfo) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BlobInfo.ProtoReflect.Descriptor instead. -func (*BlobInfo) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{3} -} - -func (x *BlobInfo) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *BlobInfo) GetSize() int64 { - if x != nil { - return x.Size - } - return 0 -} - -func (x *BlobInfo) GetContentType() string { - if x != nil { - return x.ContentType - } - return "" -} - -func (x *BlobInfo) GetHash() string { - if x != nil { - return x.Hash - } - return "" -} - // Status structure is copied from: // https://github.com/kubernetes/apimachinery/blob/v0.30.1/pkg/apis/meta/v1/generated.proto#L979 type StatusResult struct { @@ -609,7 +508,7 @@ type StatusResult struct { func (x *StatusResult) Reset() { *x = StatusResult{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[4] + mi := &file_resource_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -622,7 +521,7 @@ func (x *StatusResult) String() string { func (*StatusResult) ProtoMessage() {} func (x *StatusResult) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[4] + mi := &file_resource_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -635,7 +534,7 @@ func (x *StatusResult) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusResult.ProtoReflect.Descriptor instead. func (*StatusResult) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{4} + return file_resource_proto_rawDescGZIP(), []int{3} } func (x *StatusResult) GetStatus() string { @@ -666,63 +565,6 @@ func (x *StatusResult) GetCode() int32 { return 0 } -type LinkBlob struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Content type header - ContentType string `protobuf:"bytes,1,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` - // Raw value to write - Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *LinkBlob) Reset() { - *x = LinkBlob{} - if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LinkBlob) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LinkBlob) ProtoMessage() {} - -func (x *LinkBlob) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LinkBlob.ProtoReflect.Descriptor instead. -func (*LinkBlob) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{5} -} - -func (x *LinkBlob) GetContentType() string { - if x != nil { - return x.ContentType - } - return "" -} - -func (x *LinkBlob) GetValue() []byte { - if x != nil { - return x.Value - } - return nil -} - type CreateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -734,14 +576,12 @@ type CreateRequest struct { Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // The resource JSON. Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - // Optionally include a large binary object - Blob *LinkBlob `protobuf:"bytes,4,opt,name=blob,proto3" json:"blob,omitempty"` } func (x *CreateRequest) Reset() { *x = CreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[6] + mi := &file_resource_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -754,7 +594,7 @@ func (x *CreateRequest) String() string { func (*CreateRequest) ProtoMessage() {} func (x *CreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[6] + mi := &file_resource_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -767,7 +607,7 @@ func (x *CreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateRequest.ProtoReflect.Descriptor instead. func (*CreateRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{6} + return file_resource_proto_rawDescGZIP(), []int{4} } func (x *CreateRequest) GetKey() *ResourceKey { @@ -784,13 +624,6 @@ func (x *CreateRequest) GetValue() []byte { return nil } -func (x *CreateRequest) GetBlob() *LinkBlob { - if x != nil { - return x.Blob - } - return nil -} - type CreateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -807,7 +640,7 @@ type CreateResponse struct { func (x *CreateResponse) Reset() { *x = CreateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[7] + mi := &file_resource_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -820,7 +653,7 @@ func (x *CreateResponse) String() string { func (*CreateResponse) ProtoMessage() {} func (x *CreateResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[7] + mi := &file_resource_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -833,7 +666,7 @@ func (x *CreateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateResponse.ProtoReflect.Descriptor instead. func (*CreateResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{7} + return file_resource_proto_rawDescGZIP(), []int{5} } func (x *CreateResponse) GetStatus() *StatusResult { @@ -868,14 +701,12 @@ type UpdateRequest struct { ResourceVersion int64 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` // The resource JSON. Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - // Optionally link a resource object - Blob *LinkBlob `protobuf:"bytes,5,opt,name=blob,proto3" json:"blob,omitempty"` } func (x *UpdateRequest) Reset() { *x = UpdateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[8] + mi := &file_resource_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -888,7 +719,7 @@ func (x *UpdateRequest) String() string { func (*UpdateRequest) ProtoMessage() {} func (x *UpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[8] + mi := &file_resource_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -901,7 +732,7 @@ func (x *UpdateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead. func (*UpdateRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{8} + return file_resource_proto_rawDescGZIP(), []int{6} } func (x *UpdateRequest) GetKey() *ResourceKey { @@ -925,13 +756,6 @@ func (x *UpdateRequest) GetValue() []byte { return nil } -func (x *UpdateRequest) GetBlob() *LinkBlob { - if x != nil { - return x.Blob - } - return nil -} - type UpdateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -948,7 +772,7 @@ type UpdateResponse struct { func (x *UpdateResponse) Reset() { *x = UpdateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[9] + mi := &file_resource_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -961,7 +785,7 @@ func (x *UpdateResponse) String() string { func (*UpdateResponse) ProtoMessage() {} func (x *UpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[9] + mi := &file_resource_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -974,7 +798,7 @@ func (x *UpdateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateResponse.ProtoReflect.Descriptor instead. func (*UpdateResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{9} + return file_resource_proto_rawDescGZIP(), []int{7} } func (x *UpdateResponse) GetStatus() *StatusResult { @@ -1014,7 +838,7 @@ type DeleteRequest struct { func (x *DeleteRequest) Reset() { *x = DeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[10] + mi := &file_resource_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1027,7 +851,7 @@ func (x *DeleteRequest) String() string { func (*DeleteRequest) ProtoMessage() {} func (x *DeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[10] + mi := &file_resource_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1040,7 +864,7 @@ func (x *DeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead. func (*DeleteRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{10} + return file_resource_proto_rawDescGZIP(), []int{8} } func (x *DeleteRequest) GetKey() *ResourceKey { @@ -1080,7 +904,7 @@ type DeleteResponse struct { func (x *DeleteResponse) Reset() { *x = DeleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[11] + mi := &file_resource_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1093,7 +917,7 @@ func (x *DeleteResponse) String() string { func (*DeleteResponse) ProtoMessage() {} func (x *DeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[11] + mi := &file_resource_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1106,7 +930,7 @@ func (x *DeleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteResponse.ProtoReflect.Descriptor instead. func (*DeleteResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{11} + return file_resource_proto_rawDescGZIP(), []int{9} } func (x *DeleteResponse) GetStatus() *StatusResult { @@ -1138,14 +962,12 @@ type ReadRequest struct { Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // Optionally pick an explicit resource version ResourceVersion int64 `protobuf:"varint,3,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - // Include a signed blob_url (if exists) - IncludeBlobUrl bool `protobuf:"varint,2,opt,name=include_blob_url,json=includeBlobUrl,proto3" json:"include_blob_url,omitempty"` } func (x *ReadRequest) Reset() { *x = ReadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[12] + mi := &file_resource_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1158,7 +980,7 @@ func (x *ReadRequest) String() string { func (*ReadRequest) ProtoMessage() {} func (x *ReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[12] + mi := &file_resource_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1171,7 +993,7 @@ func (x *ReadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. func (*ReadRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{12} + return file_resource_proto_rawDescGZIP(), []int{10} } func (x *ReadRequest) GetKey() *ResourceKey { @@ -1188,13 +1010,6 @@ func (x *ReadRequest) GetResourceVersion() int64 { return 0 } -func (x *ReadRequest) GetIncludeBlobUrl() bool { - if x != nil { - return x.IncludeBlobUrl - } - return false -} - type ReadResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1206,15 +1021,12 @@ type ReadResponse struct { ResourceVersion int64 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` // The properties Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - // A Signed URL that will let you fetch the blob - // If this value starts with # you must read the bytes using the GetResourceBlob request - BlobUrl string `protobuf:"bytes,5,opt,name=blob_url,json=blobUrl,proto3" json:"blob_url,omitempty"` } func (x *ReadResponse) Reset() { *x = ReadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[13] + mi := &file_resource_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1227,7 +1039,7 @@ func (x *ReadResponse) String() string { func (*ReadResponse) ProtoMessage() {} func (x *ReadResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[13] + mi := &file_resource_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1240,7 +1052,7 @@ func (x *ReadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadResponse.ProtoReflect.Descriptor instead. func (*ReadResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{13} + return file_resource_proto_rawDescGZIP(), []int{11} } func (x *ReadResponse) GetStatus() *StatusResult { @@ -1264,135 +1076,6 @@ func (x *ReadResponse) GetValue() []byte { return nil } -func (x *ReadResponse) GetBlobUrl() string { - if x != nil { - return x.BlobUrl - } - return "" -} - -type GetBlobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - // The new resource version - ResourceVersion int64 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` -} - -func (x *GetBlobRequest) Reset() { - *x = GetBlobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBlobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBlobRequest) ProtoMessage() {} - -func (x *GetBlobRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBlobRequest.ProtoReflect.Descriptor instead. -func (*GetBlobRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{14} -} - -func (x *GetBlobRequest) GetKey() *ResourceKey { - if x != nil { - return x.Key - } - return nil -} - -func (x *GetBlobRequest) GetResourceVersion() int64 { - if x != nil { - return x.ResourceVersion - } - return 0 -} - -type GetBlobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Status code - Status *StatusResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - // Headers - Info *BlobInfo `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` - // The raw object value - Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *GetBlobResponse) Reset() { - *x = GetBlobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBlobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBlobResponse) ProtoMessage() {} - -func (x *GetBlobResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBlobResponse.ProtoReflect.Descriptor instead. -func (*GetBlobResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{15} -} - -func (x *GetBlobResponse) GetStatus() *StatusResult { - if x != nil { - return x.Status - } - return nil -} - -func (x *GetBlobResponse) GetInfo() *BlobInfo { - if x != nil { - return x.Info - } - return nil -} - -func (x *GetBlobResponse) GetValue() []byte { - if x != nil { - return x.Value - } - return nil -} - // The label filtering requirements: // https://github.com/kubernetes/kubernetes/blob/v1.30.1/staging/src/k8s.io/apimachinery/pkg/labels/selector.go#L141 type Requirement struct { @@ -1408,7 +1091,7 @@ type Requirement struct { func (x *Requirement) Reset() { *x = Requirement{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[16] + mi := &file_resource_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1421,7 +1104,7 @@ func (x *Requirement) String() string { func (*Requirement) ProtoMessage() {} func (x *Requirement) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[16] + mi := &file_resource_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1434,7 +1117,7 @@ func (x *Requirement) ProtoReflect() protoreflect.Message { // Deprecated: Use Requirement.ProtoReflect.Descriptor instead. func (*Requirement) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{16} + return file_resource_proto_rawDescGZIP(), []int{12} } func (x *Requirement) GetKey() string { @@ -1470,7 +1153,7 @@ type Sort struct { func (x *Sort) Reset() { *x = Sort{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[17] + mi := &file_resource_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1483,7 +1166,7 @@ func (x *Sort) String() string { func (*Sort) ProtoMessage() {} func (x *Sort) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[17] + mi := &file_resource_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1496,7 +1179,7 @@ func (x *Sort) ProtoReflect() protoreflect.Message { // Deprecated: Use Sort.ProtoReflect.Descriptor instead. func (*Sort) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{17} + return file_resource_proto_rawDescGZIP(), []int{13} } func (x *Sort) GetField() string { @@ -1530,7 +1213,7 @@ type ListOptions struct { func (x *ListOptions) Reset() { *x = ListOptions{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[18] + mi := &file_resource_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1543,7 +1226,7 @@ func (x *ListOptions) String() string { func (*ListOptions) ProtoMessage() {} func (x *ListOptions) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[18] + mi := &file_resource_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1556,7 +1239,7 @@ func (x *ListOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use ListOptions.ProtoReflect.Descriptor instead. func (*ListOptions) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{18} + return file_resource_proto_rawDescGZIP(), []int{14} } func (x *ListOptions) GetKey() *ResourceKey { @@ -1603,7 +1286,7 @@ type ListRequest struct { func (x *ListRequest) Reset() { *x = ListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[19] + mi := &file_resource_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1616,7 +1299,7 @@ func (x *ListRequest) String() string { func (*ListRequest) ProtoMessage() {} func (x *ListRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[19] + mi := &file_resource_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1629,7 +1312,7 @@ func (x *ListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRequest.ProtoReflect.Descriptor instead. func (*ListRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{19} + return file_resource_proto_rawDescGZIP(), []int{15} } func (x *ListRequest) GetNextPageToken() string { @@ -1700,7 +1383,7 @@ type ListResponse struct { func (x *ListResponse) Reset() { *x = ListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[20] + mi := &file_resource_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1713,7 +1396,7 @@ func (x *ListResponse) String() string { func (*ListResponse) ProtoMessage() {} func (x *ListResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[20] + mi := &file_resource_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1726,7 +1409,7 @@ func (x *ListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. func (*ListResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{20} + return file_resource_proto_rawDescGZIP(), []int{16} } func (x *ListResponse) GetItems() []*ResourceWrapper { @@ -1775,7 +1458,7 @@ type WatchRequest struct { func (x *WatchRequest) Reset() { *x = WatchRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[21] + mi := &file_resource_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1788,7 +1471,7 @@ func (x *WatchRequest) String() string { func (*WatchRequest) ProtoMessage() {} func (x *WatchRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[21] + mi := &file_resource_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1801,7 +1484,7 @@ func (x *WatchRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead. func (*WatchRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{21} + return file_resource_proto_rawDescGZIP(), []int{17} } func (x *WatchRequest) GetSince() int64 { @@ -1850,7 +1533,7 @@ type WatchEvent struct { func (x *WatchEvent) Reset() { *x = WatchEvent{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[22] + mi := &file_resource_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1863,7 +1546,7 @@ func (x *WatchEvent) String() string { func (*WatchEvent) ProtoMessage() {} func (x *WatchEvent) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[22] + mi := &file_resource_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1876,7 +1559,7 @@ func (x *WatchEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchEvent.ProtoReflect.Descriptor instead. func (*WatchEvent) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{22} + return file_resource_proto_rawDescGZIP(), []int{18} } func (x *WatchEvent) GetTimestamp() int64 { @@ -1925,7 +1608,7 @@ type HistoryRequest struct { func (x *HistoryRequest) Reset() { *x = HistoryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[23] + mi := &file_resource_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1938,7 +1621,7 @@ func (x *HistoryRequest) String() string { func (*HistoryRequest) ProtoMessage() {} func (x *HistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[23] + mi := &file_resource_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1951,7 +1634,7 @@ func (x *HistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HistoryRequest.ProtoReflect.Descriptor instead. func (*HistoryRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{23} + return file_resource_proto_rawDescGZIP(), []int{19} } func (x *HistoryRequest) GetNextPageToken() string { @@ -1997,7 +1680,7 @@ type HistoryResponse struct { func (x *HistoryResponse) Reset() { *x = HistoryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[24] + mi := &file_resource_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2010,7 +1693,7 @@ func (x *HistoryResponse) String() string { func (*HistoryResponse) ProtoMessage() {} func (x *HistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[24] + mi := &file_resource_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2023,7 +1706,7 @@ func (x *HistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HistoryResponse.ProtoReflect.Descriptor instead. func (*HistoryResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{24} + return file_resource_proto_rawDescGZIP(), []int{20} } func (x *HistoryResponse) GetItems() []*ResourceMeta { @@ -2065,7 +1748,7 @@ type OriginRequest struct { func (x *OriginRequest) Reset() { *x = OriginRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[25] + mi := &file_resource_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2078,7 +1761,7 @@ func (x *OriginRequest) String() string { func (*OriginRequest) ProtoMessage() {} func (x *OriginRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[25] + mi := &file_resource_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2091,7 +1774,7 @@ func (x *OriginRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OriginRequest.ProtoReflect.Descriptor instead. func (*OriginRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{25} + return file_resource_proto_rawDescGZIP(), []int{21} } func (x *OriginRequest) GetNextPageToken() string { @@ -2146,7 +1829,7 @@ type ResourceOriginInfo struct { func (x *ResourceOriginInfo) Reset() { *x = ResourceOriginInfo{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[26] + mi := &file_resource_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2159,7 +1842,7 @@ func (x *ResourceOriginInfo) String() string { func (*ResourceOriginInfo) ProtoMessage() {} func (x *ResourceOriginInfo) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[26] + mi := &file_resource_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2172,7 +1855,7 @@ func (x *ResourceOriginInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceOriginInfo.ProtoReflect.Descriptor instead. func (*ResourceOriginInfo) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{26} + return file_resource_proto_rawDescGZIP(), []int{22} } func (x *ResourceOriginInfo) GetKey() *ResourceKey { @@ -2239,7 +1922,7 @@ type OriginResponse struct { func (x *OriginResponse) Reset() { *x = OriginResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[27] + mi := &file_resource_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2252,7 +1935,7 @@ func (x *OriginResponse) String() string { func (*OriginResponse) ProtoMessage() {} func (x *OriginResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[27] + mi := &file_resource_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2265,7 +1948,7 @@ func (x *OriginResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OriginResponse.ProtoReflect.Descriptor instead. func (*OriginResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{27} + return file_resource_proto_rawDescGZIP(), []int{23} } func (x *OriginResponse) GetItems() []*ResourceOriginInfo { @@ -2300,7 +1983,7 @@ type HealthCheckRequest struct { func (x *HealthCheckRequest) Reset() { *x = HealthCheckRequest{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[28] + mi := &file_resource_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2313,7 +1996,7 @@ func (x *HealthCheckRequest) String() string { func (*HealthCheckRequest) ProtoMessage() {} func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[28] + mi := &file_resource_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2326,7 +2009,7 @@ func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. func (*HealthCheckRequest) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{28} + return file_resource_proto_rawDescGZIP(), []int{24} } func (x *HealthCheckRequest) GetService() string { @@ -2347,7 +2030,7 @@ type HealthCheckResponse struct { func (x *HealthCheckResponse) Reset() { *x = HealthCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_resource_proto_msgTypes[29] + mi := &file_resource_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2360,7 +2043,7 @@ func (x *HealthCheckResponse) String() string { func (*HealthCheckResponse) ProtoMessage() {} func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_resource_proto_msgTypes[29] + mi := &file_resource_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2373,7 +2056,7 @@ func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. func (*HealthCheckResponse) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{29} + return file_resource_proto_rawDescGZIP(), []int{25} } func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus { @@ -2383,6 +2066,327 @@ func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus { return HealthCheckResponse_UNKNOWN } +type PutBlobRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The resource that will use this blob + // NOTE: the name may not yet exist, but group+resource are required + Resource *ResourceKey `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + // How to upload + Method PutBlobRequest_Method `protobuf:"varint,2,opt,name=method,proto3,enum=resource.PutBlobRequest_Method" json:"method,omitempty"` + // Content type header + ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + // Raw value to write + // Not valid when method == HTTP + Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *PutBlobRequest) Reset() { + *x = PutBlobRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PutBlobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PutBlobRequest) ProtoMessage() {} + +func (x *PutBlobRequest) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PutBlobRequest.ProtoReflect.Descriptor instead. +func (*PutBlobRequest) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{26} +} + +func (x *PutBlobRequest) GetResource() *ResourceKey { + if x != nil { + return x.Resource + } + return nil +} + +func (x *PutBlobRequest) GetMethod() PutBlobRequest_Method { + if x != nil { + return x.Method + } + return PutBlobRequest_GRPC +} + +func (x *PutBlobRequest) GetContentType() string { + if x != nil { + return x.ContentType + } + return "" +} + +func (x *PutBlobRequest) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +type PutBlobResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Status code + Status *StatusResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + // The blob uid. This must be saved into the resource to support access + Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"` + // The URL where this value can be PUT + Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"` + // Size of the uploaded blob + Size int64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` + // Content hash used for an etag + Hash string `protobuf:"bytes,5,opt,name=hash,proto3" json:"hash,omitempty"` + // Validated mimetype (from content_type) + MimeType string `protobuf:"bytes,6,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"` + // Validated charset (from content_type) + Charset string `protobuf:"bytes,7,opt,name=charset,proto3" json:"charset,omitempty"` +} + +func (x *PutBlobResponse) Reset() { + *x = PutBlobResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PutBlobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PutBlobResponse) ProtoMessage() {} + +func (x *PutBlobResponse) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PutBlobResponse.ProtoReflect.Descriptor instead. +func (*PutBlobResponse) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{27} +} + +func (x *PutBlobResponse) GetStatus() *StatusResult { + if x != nil { + return x.Status + } + return nil +} + +func (x *PutBlobResponse) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +func (x *PutBlobResponse) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *PutBlobResponse) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *PutBlobResponse) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +func (x *PutBlobResponse) GetMimeType() string { + if x != nil { + return x.MimeType + } + return "" +} + +func (x *PutBlobResponse) GetCharset() string { + if x != nil { + return x.Charset + } + return "" +} + +type GetBlobRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resource *ResourceKey `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + // The new resource version + ResourceVersion int64 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` + // Do not return a pre-signed URL (when possible) + MustProxyBytes bool `protobuf:"varint,3,opt,name=must_proxy_bytes,json=mustProxyBytes,proto3" json:"must_proxy_bytes,omitempty"` +} + +func (x *GetBlobRequest) Reset() { + *x = GetBlobRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlobRequest) ProtoMessage() {} + +func (x *GetBlobRequest) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlobRequest.ProtoReflect.Descriptor instead. +func (*GetBlobRequest) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{28} +} + +func (x *GetBlobRequest) GetResource() *ResourceKey { + if x != nil { + return x.Resource + } + return nil +} + +func (x *GetBlobRequest) GetResourceVersion() int64 { + if x != nil { + return x.ResourceVersion + } + return 0 +} + +func (x *GetBlobRequest) GetMustProxyBytes() bool { + if x != nil { + return x.MustProxyBytes + } + return false +} + +type GetBlobResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Status code sent on errors + Status *StatusResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + // (optional) When possible, the system will return a presigned URL + // that can be used to actually read the full blob+metadata + // When this is set, neither info nor value will be set + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + // Content type + ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + // The raw object value + Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *GetBlobResponse) Reset() { + *x = GetBlobResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlobResponse) ProtoMessage() {} + +func (x *GetBlobResponse) ProtoReflect() protoreflect.Message { + mi := &file_resource_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlobResponse.ProtoReflect.Descriptor instead. +func (*GetBlobResponse) Descriptor() ([]byte, []int) { + return file_resource_proto_rawDescGZIP(), []int{29} +} + +func (x *GetBlobResponse) GetStatus() *StatusResult { + if x != nil { + return x.Status + } + return nil +} + +func (x *GetBlobResponse) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *GetBlobResponse) GetContentType() string { + if x != nil { + return x.ContentType + } + return "" +} + +func (x *GetBlobResponse) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + type WatchEvent_Resource struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2421,7 +2425,7 @@ func (x *WatchEvent_Resource) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchEvent_Resource.ProtoReflect.Descriptor instead. func (*WatchEvent_Resource) Descriptor() ([]byte, []int) { - return file_resource_proto_rawDescGZIP(), []int{22, 0} + return file_resource_proto_rawDescGZIP(), []int{18, 0} } func (x *WatchEvent_Resource) GetVersion() int64 { @@ -2449,73 +2453,50 @@ var file_resource_proto_rawDesc = []byte{ 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, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x6d, 0x0a, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x52, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 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, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0xac, 0x01, 0x0a, - 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x29, 0x0a, - 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, - 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x70, - 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, - 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0x69, 0x0a, 0x08, 0x42, - 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 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, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x6c, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, - 0x63, 0x6f, 0x64, 0x65, 0x22, 0x7c, 0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x6c, 0x6f, 0x62, - 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 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, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x37, 0x0a, 0x06, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, - 0x4b, 0x45, 0x45, 0x50, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, - 0x10, 0x03, 0x22, 0x76, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 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, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, - 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x22, 0x81, 0x01, 0x0a, 0x0e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa1, - 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 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, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x62, 0x6c, - 0x6f, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x04, 0x62, 0x6c, - 0x6f, 0x62, 0x22, 0x81, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x65, 0x22, 0x91, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, + 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x11, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x4d, 0x65, 0x74, 0x61, 0x22, 0x6c, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x22, 0x4e, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 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, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x79, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, + 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, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, @@ -2539,243 +2520,273 @@ var file_resource_proto_rawDesc = []byte{ 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, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 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, 0x03, - 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, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x55, 0x72, 0x6c, 0x22, - 0x9a, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 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, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x62, 0x55, 0x72, 0x6c, 0x22, 0x64, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 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, 0x7f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x42, - 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x53, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x64, 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, 0x2a, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x22, 0x1a, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x07, 0x0a, 0x03, 0x41, - 0x53, 0x43, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x45, 0x53, 0x43, 0x10, 0x01, 0x22, 0x94, - 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x2d, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 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, 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, 0x43, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, - 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, 0x22, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, - 0x72, 0x74, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x22, 0xc4, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x69, 0x74, 0x65, - 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x72, 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, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 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, 0x04, 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, - 0xb9, 0x01, 0x0a, 0x0c, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x03, 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, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x6e, 0x64, 0x5f, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x61, 0x74, - 0x63, 0x68, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x22, 0xdf, 0x02, 0x0a, 0x0a, - 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x08, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x1a, 0x3a, 0x0a, - 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x52, 0x0a, 0x04, 0x54, 0x79, 0x70, - 0x65, 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, 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, 0x9a, 0x01, - 0x0a, 0x0e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 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, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x27, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 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, 0x21, 0x0a, 0x0c, 0x73, 0x68, 0x6f, 0x77, 0x5f, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, - 0x68, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x92, 0x01, 0x0a, 0x0f, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, - 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x65, 0x22, 0x61, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 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, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x0f, + 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, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7f, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 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, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x53, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x64, 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, 0x2a, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x22, 0x1a, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x07, 0x0a, + 0x03, 0x41, 0x53, 0x43, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x45, 0x53, 0x43, 0x10, 0x01, + 0x22, 0x94, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 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, 0x2d, 0x0a, 0x06, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, + 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, + 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, 0x43, 0x0a, 0x0d, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x05, 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, 0x22, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x53, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x22, 0xc4, 0x01, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x69, + 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x72, 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, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, - 0x8e, 0x01, 0x0a, 0x0d, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 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, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, - 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 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, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 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, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, + 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, 0x04, 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, 0xb9, 0x01, 0x0a, 0x0c, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x03, 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, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x6e, + 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, + 0x6b, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, + 0x61, 0x74, 0x63, 0x68, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x22, 0xdf, 0x02, + 0x0a, 0x0a, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x1a, + 0x3a, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x52, 0x0a, 0x04, 0x54, + 0x79, 0x70, 0x65, 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, 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, + 0x9a, 0x01, 0x0a, 0x0e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 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, 0x14, 0x0a, 0x05, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 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, 0x21, 0x0a, 0x0c, 0x73, 0x68, 0x6f, + 0x77, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x92, 0x01, 0x0a, + 0x0f, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2c, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 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, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0x8e, 0x01, 0x0a, 0x0d, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 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, 0x14, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 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, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x97, 0x01, 0x0a, 0x0e, 0x4f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x69, - 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x72, - 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 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, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 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, - 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, 0xed, 0x02, 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, + 0x69, 0x6e, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 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, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x97, 0x01, 0x0a, 0x0e, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, + 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 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, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 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, 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, 0xc4, 0x01, 0x0a, 0x0f, 0x50, 0x75, 0x74, + 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0x8c, 0x01, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0xed, + 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, 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, 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, 0x84, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 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, - 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, 0x12, - 0x3e, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3b, 0x0a, 0x06, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, 0x72, - 0x69, 0x67, 0x69, 0x6e, 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, + 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, 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, 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, 0xc4, + 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, + 0x68, 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, 0x3e, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x4f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 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 ( @@ -2794,98 +2805,100 @@ var file_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 5) var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 31) var file_resource_proto_goTypes = []interface{}{ (ResourceVersionMatch)(0), // 0: resource.ResourceVersionMatch - (LinkBlob_Action)(0), // 1: resource.LinkBlob.Action - (Sort_Order)(0), // 2: resource.Sort.Order - (WatchEvent_Type)(0), // 3: resource.WatchEvent.Type - (HealthCheckResponse_ServingStatus)(0), // 4: resource.HealthCheckResponse.ServingStatus + (Sort_Order)(0), // 1: resource.Sort.Order + (WatchEvent_Type)(0), // 2: resource.WatchEvent.Type + (HealthCheckResponse_ServingStatus)(0), // 3: resource.HealthCheckResponse.ServingStatus + (PutBlobRequest_Method)(0), // 4: resource.PutBlobRequest.Method (*ResourceKey)(nil), // 5: resource.ResourceKey (*ResourceWrapper)(nil), // 6: resource.ResourceWrapper (*ResourceMeta)(nil), // 7: resource.ResourceMeta - (*BlobInfo)(nil), // 8: resource.BlobInfo - (*StatusResult)(nil), // 9: resource.StatusResult - (*LinkBlob)(nil), // 10: resource.LinkBlob - (*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 - (*GetBlobRequest)(nil), // 19: resource.GetBlobRequest - (*GetBlobResponse)(nil), // 20: resource.GetBlobResponse - (*Requirement)(nil), // 21: resource.Requirement - (*Sort)(nil), // 22: resource.Sort - (*ListOptions)(nil), // 23: resource.ListOptions - (*ListRequest)(nil), // 24: resource.ListRequest - (*ListResponse)(nil), // 25: resource.ListResponse - (*WatchRequest)(nil), // 26: resource.WatchRequest - (*WatchEvent)(nil), // 27: resource.WatchEvent - (*HistoryRequest)(nil), // 28: resource.HistoryRequest - (*HistoryResponse)(nil), // 29: resource.HistoryResponse - (*OriginRequest)(nil), // 30: resource.OriginRequest - (*ResourceOriginInfo)(nil), // 31: resource.ResourceOriginInfo - (*OriginResponse)(nil), // 32: resource.OriginResponse - (*HealthCheckRequest)(nil), // 33: resource.HealthCheckRequest - (*HealthCheckResponse)(nil), // 34: resource.HealthCheckResponse + (*StatusResult)(nil), // 8: resource.StatusResult + (*CreateRequest)(nil), // 9: resource.CreateRequest + (*CreateResponse)(nil), // 10: resource.CreateResponse + (*UpdateRequest)(nil), // 11: resource.UpdateRequest + (*UpdateResponse)(nil), // 12: resource.UpdateResponse + (*DeleteRequest)(nil), // 13: resource.DeleteRequest + (*DeleteResponse)(nil), // 14: resource.DeleteResponse + (*ReadRequest)(nil), // 15: resource.ReadRequest + (*ReadResponse)(nil), // 16: resource.ReadResponse + (*Requirement)(nil), // 17: resource.Requirement + (*Sort)(nil), // 18: resource.Sort + (*ListOptions)(nil), // 19: resource.ListOptions + (*ListRequest)(nil), // 20: resource.ListRequest + (*ListResponse)(nil), // 21: resource.ListResponse + (*WatchRequest)(nil), // 22: resource.WatchRequest + (*WatchEvent)(nil), // 23: resource.WatchEvent + (*HistoryRequest)(nil), // 24: resource.HistoryRequest + (*HistoryResponse)(nil), // 25: resource.HistoryResponse + (*OriginRequest)(nil), // 26: resource.OriginRequest + (*ResourceOriginInfo)(nil), // 27: resource.ResourceOriginInfo + (*OriginResponse)(nil), // 28: resource.OriginResponse + (*HealthCheckRequest)(nil), // 29: resource.HealthCheckRequest + (*HealthCheckResponse)(nil), // 30: resource.HealthCheckResponse + (*PutBlobRequest)(nil), // 31: resource.PutBlobRequest + (*PutBlobResponse)(nil), // 32: resource.PutBlobResponse + (*GetBlobRequest)(nil), // 33: resource.GetBlobRequest + (*GetBlobResponse)(nil), // 34: resource.GetBlobResponse (*WatchEvent_Resource)(nil), // 35: resource.WatchEvent.Resource } var file_resource_proto_depIdxs = []int32{ 5, // 0: resource.CreateRequest.key:type_name -> resource.ResourceKey - 10, // 1: resource.CreateRequest.blob:type_name -> resource.LinkBlob - 9, // 2: resource.CreateResponse.status:type_name -> resource.StatusResult - 5, // 3: resource.UpdateRequest.key:type_name -> resource.ResourceKey - 10, // 4: resource.UpdateRequest.blob:type_name -> resource.LinkBlob - 9, // 5: resource.UpdateResponse.status:type_name -> resource.StatusResult - 5, // 6: resource.DeleteRequest.key:type_name -> resource.ResourceKey - 9, // 7: resource.DeleteResponse.status:type_name -> resource.StatusResult - 5, // 8: resource.ReadRequest.key:type_name -> resource.ResourceKey - 9, // 9: resource.ReadResponse.status:type_name -> resource.StatusResult - 5, // 10: resource.GetBlobRequest.key:type_name -> resource.ResourceKey - 9, // 11: resource.GetBlobResponse.status:type_name -> resource.StatusResult - 8, // 12: resource.GetBlobResponse.info:type_name -> resource.BlobInfo - 2, // 13: resource.Sort.order:type_name -> resource.Sort.Order - 5, // 14: resource.ListOptions.key:type_name -> resource.ResourceKey - 21, // 15: resource.ListOptions.labels:type_name -> resource.Requirement - 21, // 16: resource.ListOptions.fields:type_name -> resource.Requirement - 0, // 17: resource.ListRequest.version_match:type_name -> resource.ResourceVersionMatch - 23, // 18: resource.ListRequest.options:type_name -> resource.ListOptions - 22, // 19: resource.ListRequest.sort:type_name -> resource.Sort - 6, // 20: resource.ListResponse.items:type_name -> resource.ResourceWrapper - 23, // 21: resource.WatchRequest.options:type_name -> resource.ListOptions - 3, // 22: resource.WatchEvent.type:type_name -> resource.WatchEvent.Type - 35, // 23: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource - 35, // 24: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource - 5, // 25: resource.HistoryRequest.key:type_name -> resource.ResourceKey - 7, // 26: resource.HistoryResponse.items:type_name -> resource.ResourceMeta - 5, // 27: resource.OriginRequest.key:type_name -> resource.ResourceKey - 5, // 28: resource.ResourceOriginInfo.key:type_name -> resource.ResourceKey - 31, // 29: resource.OriginResponse.items:type_name -> resource.ResourceOriginInfo - 4, // 30: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus - 17, // 31: resource.ResourceStore.Read:input_type -> resource.ReadRequest - 11, // 32: resource.ResourceStore.Create:input_type -> resource.CreateRequest - 13, // 33: resource.ResourceStore.Update:input_type -> resource.UpdateRequest - 15, // 34: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest - 24, // 35: resource.ResourceStore.List:input_type -> resource.ListRequest - 26, // 36: resource.ResourceStore.Watch:input_type -> resource.WatchRequest - 17, // 37: resource.ResourceSearch.Read:input_type -> resource.ReadRequest - 19, // 38: resource.ResourceSearch.GetBlob:input_type -> resource.GetBlobRequest - 28, // 39: resource.ResourceSearch.History:input_type -> resource.HistoryRequest - 30, // 40: resource.ResourceSearch.Origin:input_type -> resource.OriginRequest - 33, // 41: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest - 18, // 42: resource.ResourceStore.Read:output_type -> resource.ReadResponse - 12, // 43: resource.ResourceStore.Create:output_type -> resource.CreateResponse - 14, // 44: resource.ResourceStore.Update:output_type -> resource.UpdateResponse - 16, // 45: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse - 25, // 46: resource.ResourceStore.List:output_type -> resource.ListResponse - 27, // 47: resource.ResourceStore.Watch:output_type -> resource.WatchEvent - 18, // 48: resource.ResourceSearch.Read:output_type -> resource.ReadResponse - 20, // 49: resource.ResourceSearch.GetBlob:output_type -> resource.GetBlobResponse - 29, // 50: resource.ResourceSearch.History:output_type -> resource.HistoryResponse - 32, // 51: resource.ResourceSearch.Origin:output_type -> resource.OriginResponse - 34, // 52: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse - 42, // [42:53] is the sub-list for method output_type - 31, // [31:42] is the sub-list for method input_type + 8, // 1: resource.CreateResponse.status:type_name -> resource.StatusResult + 5, // 2: resource.UpdateRequest.key:type_name -> resource.ResourceKey + 8, // 3: resource.UpdateResponse.status:type_name -> resource.StatusResult + 5, // 4: resource.DeleteRequest.key:type_name -> resource.ResourceKey + 8, // 5: resource.DeleteResponse.status:type_name -> resource.StatusResult + 5, // 6: resource.ReadRequest.key:type_name -> resource.ResourceKey + 8, // 7: resource.ReadResponse.status:type_name -> resource.StatusResult + 1, // 8: resource.Sort.order:type_name -> resource.Sort.Order + 5, // 9: resource.ListOptions.key:type_name -> resource.ResourceKey + 17, // 10: resource.ListOptions.labels:type_name -> resource.Requirement + 17, // 11: resource.ListOptions.fields:type_name -> resource.Requirement + 0, // 12: resource.ListRequest.version_match:type_name -> resource.ResourceVersionMatch + 19, // 13: resource.ListRequest.options:type_name -> resource.ListOptions + 18, // 14: resource.ListRequest.sort:type_name -> resource.Sort + 6, // 15: resource.ListResponse.items:type_name -> resource.ResourceWrapper + 19, // 16: resource.WatchRequest.options:type_name -> resource.ListOptions + 2, // 17: resource.WatchEvent.type:type_name -> resource.WatchEvent.Type + 35, // 18: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource + 35, // 19: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource + 5, // 20: resource.HistoryRequest.key:type_name -> resource.ResourceKey + 7, // 21: resource.HistoryResponse.items:type_name -> resource.ResourceMeta + 5, // 22: resource.OriginRequest.key:type_name -> resource.ResourceKey + 5, // 23: resource.ResourceOriginInfo.key:type_name -> resource.ResourceKey + 27, // 24: resource.OriginResponse.items:type_name -> resource.ResourceOriginInfo + 3, // 25: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus + 5, // 26: resource.PutBlobRequest.resource:type_name -> resource.ResourceKey + 4, // 27: resource.PutBlobRequest.method:type_name -> resource.PutBlobRequest.Method + 8, // 28: resource.PutBlobResponse.status:type_name -> resource.StatusResult + 5, // 29: resource.GetBlobRequest.resource:type_name -> resource.ResourceKey + 8, // 30: resource.GetBlobResponse.status:type_name -> resource.StatusResult + 15, // 31: resource.ResourceStore.Read:input_type -> resource.ReadRequest + 9, // 32: resource.ResourceStore.Create:input_type -> resource.CreateRequest + 11, // 33: resource.ResourceStore.Update:input_type -> resource.UpdateRequest + 13, // 34: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest + 20, // 35: resource.ResourceStore.List:input_type -> resource.ListRequest + 22, // 36: resource.ResourceStore.Watch:input_type -> resource.WatchRequest + 31, // 37: resource.ResourceStore.PutBlob:input_type -> resource.PutBlobRequest + 33, // 38: resource.ResourceStore.GetBlob:input_type -> resource.GetBlobRequest + 15, // 39: resource.ResourceSearch.Read:input_type -> resource.ReadRequest + 24, // 40: resource.ResourceSearch.History:input_type -> resource.HistoryRequest + 26, // 41: resource.ResourceSearch.Origin:input_type -> resource.OriginRequest + 29, // 42: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest + 16, // 43: resource.ResourceStore.Read:output_type -> resource.ReadResponse + 10, // 44: resource.ResourceStore.Create:output_type -> resource.CreateResponse + 12, // 45: resource.ResourceStore.Update:output_type -> resource.UpdateResponse + 14, // 46: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse + 21, // 47: resource.ResourceStore.List:output_type -> resource.ListResponse + 23, // 48: resource.ResourceStore.Watch:output_type -> resource.WatchEvent + 32, // 49: resource.ResourceStore.PutBlob:output_type -> resource.PutBlobResponse + 34, // 50: resource.ResourceStore.GetBlob:output_type -> resource.GetBlobResponse + 16, // 51: resource.ResourceSearch.Read:output_type -> resource.ReadResponse + 25, // 52: resource.ResourceSearch.History:output_type -> resource.HistoryResponse + 28, // 53: resource.ResourceSearch.Origin:output_type -> resource.OriginResponse + 30, // 54: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse + 43, // [43:55] is the sub-list for method output_type + 31, // [31:43] is the sub-list for method input_type 31, // [31:31] is the sub-list for extension type_name 31, // [31:31] is the sub-list for extension extendee 0, // [0:31] is the sub-list for field type_name @@ -2934,18 +2947,6 @@ func file_resource_proto_init() { } } file_resource_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlobInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_resource_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatusResult); i { case 0: return &v.state @@ -2957,19 +2958,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LinkBlob); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_resource_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateRequest); i { case 0: return &v.state @@ -2981,7 +2970,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateResponse); i { case 0: return &v.state @@ -2993,7 +2982,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateRequest); i { case 0: return &v.state @@ -3005,7 +2994,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateResponse); i { case 0: return &v.state @@ -3017,7 +3006,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteRequest); i { case 0: return &v.state @@ -3029,7 +3018,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteResponse); i { case 0: return &v.state @@ -3041,7 +3030,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadRequest); i { case 0: return &v.state @@ -3053,7 +3042,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResponse); i { case 0: return &v.state @@ -3065,31 +3054,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_resource_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_resource_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Requirement); i { case 0: return &v.state @@ -3101,7 +3066,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Sort); i { case 0: return &v.state @@ -3113,7 +3078,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListOptions); i { case 0: return &v.state @@ -3125,7 +3090,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListRequest); i { case 0: return &v.state @@ -3137,7 +3102,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListResponse); i { case 0: return &v.state @@ -3149,7 +3114,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WatchRequest); i { case 0: return &v.state @@ -3161,7 +3126,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WatchEvent); i { case 0: return &v.state @@ -3173,7 +3138,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HistoryRequest); i { case 0: return &v.state @@ -3185,7 +3150,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HistoryResponse); i { case 0: return &v.state @@ -3197,7 +3162,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OriginRequest); i { case 0: return &v.state @@ -3209,7 +3174,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResourceOriginInfo); i { case 0: return &v.state @@ -3221,7 +3186,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OriginResponse); i { case 0: return &v.state @@ -3233,7 +3198,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HealthCheckRequest); i { case 0: return &v.state @@ -3245,7 +3210,7 @@ func file_resource_proto_init() { return nil } } - file_resource_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_resource_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HealthCheckResponse); i { case 0: return &v.state @@ -3257,6 +3222,54 @@ func file_resource_proto_init() { return nil } } + file_resource_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PutBlobRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PutBlobResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlobRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlobResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } file_resource_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WatchEvent_Resource); i { case 0: diff --git a/pkg/storage/unified/resource/resource.proto b/pkg/storage/unified/resource/resource.proto index c2c4312bca8..236267e70fb 100644 --- a/pkg/storage/unified/resource/resource.proto +++ b/pkg/storage/unified/resource/resource.proto @@ -20,9 +20,6 @@ message ResourceWrapper { // Full kubernetes json bytes (although the resource version may not be accurate) bytes value = 2; - - // The resource has an attached blob - bool has_blob = 4; } // The history and trash commands need access to commit messages @@ -39,24 +36,6 @@ message ResourceMeta { // The kubernetes metadata section (not the full resource) // https://github.com/kubernetes/kubernetes/blob/v1.30.2/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go#L1496 bytes partial_object_meta = 6; - - // The resource has an attached blob - bool has_blob = 7; -} - -// Basic blob metadata -message BlobInfo { - // System identifier - string path = 1; - - // Content Length - int64 size = 2; - - // Content type header - string content_type = 3; - - // Content hash used for an etag - string hash = 4; } // Status structure is copied from: @@ -81,25 +60,6 @@ message StatusResult { int32 code = 4; } -message LinkBlob { - enum Action { - UNKNOWN = 0; - // Upload raw bytes - UPLOAD = 1; - // Keep the existing blob (valid for updates) - KEEP = 2; - // Do not keep the existing version (same as not sending LinkBlob, only valid for updates) - REMOVE = 3; - // TODO... support presigned uploads - } - - // Content type header - string content_type = 1; - - // Raw value to write - bytes value = 2; -} - // ---------------------------------- // CRUD Objects // ---------------------------------- @@ -112,9 +72,6 @@ message CreateRequest { // The resource JSON. bytes value = 2; - - // Optionally include a large binary object - LinkBlob blob = 4; } message CreateResponse { @@ -137,9 +94,6 @@ message UpdateRequest { // The resource JSON. bytes value = 3; - - // Optionally link a resource object - LinkBlob blob = 5; } message UpdateResponse { @@ -180,9 +134,6 @@ message ReadRequest { // Optionally pick an explicit resource version int64 resource_version = 3; - - // Include a signed blob_url (if exists) - bool include_blob_url = 2; } message ReadResponse { @@ -194,28 +145,6 @@ message ReadResponse { // The properties bytes value = 3; - - // A Signed URL that will let you fetch the blob - // If this value starts with # you must read the bytes using the GetResourceBlob request - string blob_url = 5; -} - -message GetBlobRequest { - ResourceKey key = 1; - - // The new resource version - int64 resource_version = 2; -} - -message GetBlobResponse { - // Status code - StatusResult status = 1; - - // Headers - BlobInfo info = 2; - - // The raw object value - bytes value = 3; } // ---------------------------------- @@ -426,6 +355,85 @@ message HealthCheckResponse { ServingStatus status = 1; } + +//---------------------------- +// Blob Support +//---------------------------- + +message PutBlobRequest { + enum Method { + // Use the inline raw []byte + GRPC = 0; + + // Get a signed URL and PUT the value + HTTP = 1; + } + + // The resource that will use this blob + // NOTE: the name may not yet exist, but group+resource are required + ResourceKey resource = 1; + + // How to upload + Method method = 2; + + // Content type header + string content_type = 3; + + // Raw value to write + // Not valid when method == HTTP + bytes value = 4; +} + +message PutBlobResponse { + // Status code + StatusResult status = 1; + + // The blob uid. This must be saved into the resource to support access + string uid = 2; + + // The URL where this value can be PUT + string url = 3; + + // Size of the uploaded blob + int64 size = 4; + + // Content hash used for an etag + string hash = 5; + + // Validated mimetype (from content_type) + string mime_type = 6; + + // Validated charset (from content_type) + string charset = 7; +} + +message GetBlobRequest { + ResourceKey resource = 1; + + // The new resource version + int64 resource_version = 2; + + // Do not return a pre-signed URL (when possible) + bool must_proxy_bytes = 3; +} + +message GetBlobResponse { + // Status code sent on errors + StatusResult status = 1; + + // (optional) When possible, the system will return a presigned URL + // that can be used to actually read the full blob+metadata + // When this is set, neither info nor value will be set + string url = 2; + + // Content type + string content_type = 3; + + // The raw object value + bytes value = 4; +} + + // This provides the CRUD+List+Watch support needed for a k8s apiserver // The semantics and behaviors of this service are constrained by kubernetes // This does not understand the resource schemas, only deals with json bytes @@ -437,6 +445,16 @@ service ResourceStore { rpc Delete(DeleteRequest) returns (DeleteResponse); rpc List(ListRequest) returns (ListResponse); rpc Watch(WatchRequest) returns (stream WatchEvent); + + // Upload a blob that will be saved in a resource + rpc PutBlob(PutBlobRequest) returns (PutBlobResponse); + + // Get blob contents. When possible, this will return a signed URL + // For large payloads, signed URLs are required to avoid protobuf message size limits + rpc GetBlob(GetBlobRequest) returns (GetBlobResponse); + + // NOTE: there is no direct access to delete blobs + // >> cleanup will be managed via garbage collection or direct access to the underlying storage } // Clients can use this service directly @@ -446,9 +464,6 @@ service ResourceSearch { rpc Read(ReadRequest) returns (ReadResponse); // Duplicated -- for client read only usage - // Get the raw blob bytes and metadata - rpc GetBlob(GetBlobRequest) returns (GetBlobResponse); - // Show resource history (and trash) rpc History(HistoryRequest) returns (HistoryResponse); diff --git a/pkg/storage/unified/resource/resource_grpc.pb.go b/pkg/storage/unified/resource/resource_grpc.pb.go index ec22fb995da..2f01e3355ad 100644 --- a/pkg/storage/unified/resource/resource_grpc.pb.go +++ b/pkg/storage/unified/resource/resource_grpc.pb.go @@ -19,12 +19,14 @@ import ( const _ = grpc.SupportPackageIsVersion8 const ( - ResourceStore_Read_FullMethodName = "/resource.ResourceStore/Read" - ResourceStore_Create_FullMethodName = "/resource.ResourceStore/Create" - ResourceStore_Update_FullMethodName = "/resource.ResourceStore/Update" - ResourceStore_Delete_FullMethodName = "/resource.ResourceStore/Delete" - ResourceStore_List_FullMethodName = "/resource.ResourceStore/List" - ResourceStore_Watch_FullMethodName = "/resource.ResourceStore/Watch" + ResourceStore_Read_FullMethodName = "/resource.ResourceStore/Read" + ResourceStore_Create_FullMethodName = "/resource.ResourceStore/Create" + ResourceStore_Update_FullMethodName = "/resource.ResourceStore/Update" + ResourceStore_Delete_FullMethodName = "/resource.ResourceStore/Delete" + ResourceStore_List_FullMethodName = "/resource.ResourceStore/List" + ResourceStore_Watch_FullMethodName = "/resource.ResourceStore/Watch" + ResourceStore_PutBlob_FullMethodName = "/resource.ResourceStore/PutBlob" + ResourceStore_GetBlob_FullMethodName = "/resource.ResourceStore/GetBlob" ) // ResourceStoreClient is the client API for ResourceStore service. @@ -42,6 +44,11 @@ type ResourceStoreClient interface { Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (ResourceStore_WatchClient, error) + // Upload a blob that will be saved in a resource + PutBlob(ctx context.Context, in *PutBlobRequest, opts ...grpc.CallOption) (*PutBlobResponse, error) + // Get blob contents. When possible, this will return a signed URL + // For large payloads, signed URLs are required to avoid protobuf message size limits + GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) } type resourceStoreClient struct { @@ -135,6 +142,26 @@ func (x *resourceStoreWatchClient) Recv() (*WatchEvent, error) { return m, nil } +func (c *resourceStoreClient) PutBlob(ctx context.Context, in *PutBlobRequest, opts ...grpc.CallOption) (*PutBlobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(PutBlobResponse) + err := c.cc.Invoke(ctx, ResourceStore_PutBlob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceStoreClient) GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetBlobResponse) + err := c.cc.Invoke(ctx, ResourceStore_GetBlob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // ResourceStoreServer is the server API for ResourceStore service. // All implementations should embed UnimplementedResourceStoreServer // for forward compatibility @@ -150,6 +177,11 @@ type ResourceStoreServer interface { Delete(context.Context, *DeleteRequest) (*DeleteResponse, error) List(context.Context, *ListRequest) (*ListResponse, error) Watch(*WatchRequest, ResourceStore_WatchServer) error + // Upload a blob that will be saved in a resource + PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error) + // Get blob contents. When possible, this will return a signed URL + // For large payloads, signed URLs are required to avoid protobuf message size limits + GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) } // UnimplementedResourceStoreServer should be embedded to have forward compatible implementations. @@ -174,6 +206,12 @@ func (UnimplementedResourceStoreServer) List(context.Context, *ListRequest) (*Li func (UnimplementedResourceStoreServer) Watch(*WatchRequest, ResourceStore_WatchServer) error { return status.Errorf(codes.Unimplemented, "method Watch not implemented") } +func (UnimplementedResourceStoreServer) PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PutBlob not implemented") +} +func (UnimplementedResourceStoreServer) GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBlob not implemented") +} // UnsafeResourceStoreServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ResourceStoreServer will @@ -297,6 +335,42 @@ func (x *resourceStoreWatchServer) Send(m *WatchEvent) error { return x.ServerStream.SendMsg(m) } +func _ResourceStore_PutBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PutBlobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceStoreServer).PutBlob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ResourceStore_PutBlob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceStoreServer).PutBlob(ctx, req.(*PutBlobRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceStore_GetBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBlobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceStoreServer).GetBlob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ResourceStore_GetBlob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceStoreServer).GetBlob(ctx, req.(*GetBlobRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ResourceStore_ServiceDesc is the grpc.ServiceDesc for ResourceStore service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -324,6 +398,14 @@ var ResourceStore_ServiceDesc = grpc.ServiceDesc{ MethodName: "List", Handler: _ResourceStore_List_Handler, }, + { + MethodName: "PutBlob", + Handler: _ResourceStore_PutBlob_Handler, + }, + { + MethodName: "GetBlob", + Handler: _ResourceStore_GetBlob_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -337,7 +419,6 @@ var ResourceStore_ServiceDesc = grpc.ServiceDesc{ const ( ResourceSearch_Read_FullMethodName = "/resource.ResourceSearch/Read" - ResourceSearch_GetBlob_FullMethodName = "/resource.ResourceSearch/GetBlob" ResourceSearch_History_FullMethodName = "/resource.ResourceSearch/History" ResourceSearch_Origin_FullMethodName = "/resource.ResourceSearch/Origin" ) @@ -350,8 +431,6 @@ const ( // NOTE: This is read only, and no read afer write guarantees type ResourceSearchClient interface { Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) - // Get the raw blob bytes and metadata - GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) // Show resource history (and trash) History(ctx context.Context, in *HistoryRequest, opts ...grpc.CallOption) (*HistoryResponse, error) // Used for efficient provisioning @@ -376,16 +455,6 @@ func (c *resourceSearchClient) Read(ctx context.Context, in *ReadRequest, opts . return out, nil } -func (c *resourceSearchClient) GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetBlobResponse) - err := c.cc.Invoke(ctx, ResourceSearch_GetBlob_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *resourceSearchClient) History(ctx context.Context, in *HistoryRequest, opts ...grpc.CallOption) (*HistoryResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(HistoryResponse) @@ -414,8 +483,6 @@ func (c *resourceSearchClient) Origin(ctx context.Context, in *OriginRequest, op // NOTE: This is read only, and no read afer write guarantees type ResourceSearchServer interface { Read(context.Context, *ReadRequest) (*ReadResponse, error) - // Get the raw blob bytes and metadata - GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) // Show resource history (and trash) History(context.Context, *HistoryRequest) (*HistoryResponse, error) // Used for efficient provisioning @@ -429,9 +496,6 @@ type UnimplementedResourceSearchServer struct { func (UnimplementedResourceSearchServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Read not implemented") } -func (UnimplementedResourceSearchServer) GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetBlob not implemented") -} func (UnimplementedResourceSearchServer) History(context.Context, *HistoryRequest) (*HistoryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method History not implemented") } @@ -468,24 +532,6 @@ func _ResourceSearch_Read_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } -func _ResourceSearch_GetBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetBlobRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ResourceSearchServer).GetBlob(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ResourceSearch_GetBlob_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ResourceSearchServer).GetBlob(ctx, req.(*GetBlobRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _ResourceSearch_History_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(HistoryRequest) if err := dec(in); err != nil { @@ -533,10 +579,6 @@ var ResourceSearch_ServiceDesc = grpc.ServiceDesc{ MethodName: "Read", Handler: _ResourceSearch_Read_Handler, }, - { - MethodName: "GetBlob", - Handler: _ResourceSearch_GetBlob_Handler, - }, { MethodName: "History", Handler: _ResourceSearch_History_Handler, diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index 3a7a4461ccd..8a622985726 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "math/rand" "sync" "time" @@ -40,23 +41,40 @@ type ResourceServer interface { LifecycleHooks } +// This store is not exposed directly to users, it is a helper to implement writing +// resources as a stream of WriteEvents type AppendingStore interface { // Write a Create/Update/Delete, // NOTE: the contents of WriteEvent have been validated // Return the revisionVersion for this event or error WriteEvent(context.Context, WriteEvent) (int64, error) - // Create new name for a given resource - GenerateName(ctx context.Context, key *ResourceKey, prefix string) (string, error) - // Read a value from storage Read(context.Context, *ReadRequest) (*ReadResponse, error) // Implement List -- this expects the read after write semantics List(context.Context, *ListRequest) (*ListResponse, error) - // Watch for events - Watch(context.Context, *WatchRequest) (chan *WatchEvent, error) + // Get all events from the store + // For HA setups, this will be more events than the local WriteEvent above! + WatchWriteEvents(ctx context.Context) (<-chan *WrittenEvent, error) +} + +// This interface is not exposed to end users directly +// Access to this interface is already gated by access control +type BlobStore interface { + // Indicates if storage layer supports signed urls + SupportsSignedURLs() bool + + // Get the raw blob bytes and metadata -- limited to protobuf message size + // For larger payloads, we should use presigned URLs to upload from the client + PutBlob(context.Context, *PutBlobRequest) (*PutBlobResponse, error) + + // Get blob contents. When possible, this will return a signed URL + // For large payloads, signed URLs are required to avoid protobuf message size limits + GetBlob(ctx context.Context, resource *ResourceKey, info *utils.BlobInfo, mustProxy bool) (*GetBlobResponse, error) + + // TODO? List+Delete? This is for admin access } type ResourceServerOptions struct { @@ -73,6 +91,9 @@ type ResourceServerOptions struct { // Real storage backend Store AppendingStore + // The blob storage engine + Blob BlobStore + // Real storage backend Search ResourceSearchServer @@ -115,6 +136,9 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) { if opts.Search == nil { opts.Search = &noopService{} } + if opts.Blob == nil { + opts.Blob = &noopService{} + } if opts.Diagnostics == nil { opts.Search = &noopService{} } @@ -124,8 +148,11 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) { } } + // Make this cancelable + ctx, cancel := context.WithCancel(context.Background()) return &server{ tracer: opts.Tracer, + log: slog.Default().With("logger", "resource-server"), nextEventID: opts.NextEventID, store: opts.Store, search: opts.Search, @@ -133,6 +160,8 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) { access: opts.WriteAccess, lifecycle: opts.Lifecycle, now: opts.Now, + ctx: ctx, + cancel: cancel, }, nil } @@ -140,14 +169,21 @@ var _ ResourceServer = &server{} type server struct { tracer trace.Tracer + log *slog.Logger nextEventID func() int64 store AppendingStore search ResourceSearchServer + blob BlobStore diagnostics DiagnosticsServer access WriteAccessHooks lifecycle LifecycleHooks now func() int64 + // Background watch task + ctx context.Context + cancel context.CancelFunc + broadcaster Broadcaster[*WrittenEvent] + // init checking once sync.Once initErr error @@ -156,8 +192,6 @@ type server struct { // Init implements ResourceServer. func (s *server) Init() error { s.once.Do(func() { - // TODO, setup a broadcaster for watch - // Call lifecycle hooks if s.lifecycle != nil { err := s.lifecycle.Init() @@ -165,15 +199,24 @@ func (s *server) Init() error { s.initErr = fmt.Errorf("initialize Resource Server: %w", err) } } + + // Start listening for changes + s.initWatcher() }) return s.initErr } func (s *server) Stop() { s.initErr = fmt.Errorf("service is stopping") + if s.lifecycle != nil { s.lifecycle.Stop() } + + // Stops the streaming + s.cancel() + + // mark the value as done s.initErr = fmt.Errorf("service is stopped") } @@ -191,9 +234,6 @@ func (s *server) newEventBuilder(ctx context.Context, key *ResourceKey, value, o } obj := event.Meta - if key.Name != obj.GetName() { - return nil, apierrors.NewBadRequest("key/name do not match") - } if key.Namespace != obj.GetNamespace() { return nil, apierrors.NewBadRequest("key/namespace do not match") } @@ -213,27 +253,20 @@ func (s *server) newEventBuilder(ctx context.Context, key *ResourceKey, value, o // This needs to be a create function if key.Name == "" { - prefix := obj.GetGenerateName() - if prefix == "" { - return nil, apierrors.NewBadRequest("must have name or generate name set") + if obj.GetName() == "" { + return nil, apierrors.NewBadRequest("missing name") } - key.Name, err = s.store.GenerateName(ctx, key, prefix) - if err != nil { - return nil, err - } - obj.SetName(key.Name) - obj.SetGenerateName("") - } else if obj.GetGenerateName() != "" { - return nil, apierrors.NewBadRequest("values with a name must not include generate name") + key.Name = obj.GetName() + } else if key.Name != obj.GetName() { + return nil, apierrors.NewBadRequest( + fmt.Sprintf("key/name do not match (key: %s, name: %s)", key.Name, obj.GetName())) } + obj.SetGenerateName("") err = validateName(obj.GetName()) if err != nil { return nil, err } - if obj.GetName() != key.Name { - return nil, apierrors.NewBadRequest("key name does not match the name in the body") - } folder := obj.GetFolder() if folder != "" { err = s.access.CanWriteFolder(ctx, event.Requester, folder) @@ -376,7 +409,7 @@ func (s *server) Update(ctx context.Context, req *UpdateRequest) (*UpdateRespons return rsp, err } - event.Event = WatchEvent_MODIFIED + event.Type = WatchEvent_MODIFIED event.PreviousRV = latest.ResourceVersion rsp.ResourceVersion, err = s.store.WriteEvent(ctx, event) @@ -416,7 +449,7 @@ func (s *server) Delete(ctx context.Context, req *DeleteRequest) (*DeleteRespons event := WriteEvent{ EventID: s.nextEventID(), Key: req.Key, - Event: WatchEvent_DELETED, + Type: WatchEvent_DELETED, PreviousRV: latest.ResourceVersion, } requester, err := identity.GetRequester(ctx) @@ -440,7 +473,7 @@ func (s *server) Delete(ctx context.Context, req *DeleteRequest) (*DeleteRespons obj.SetUpdatedBy(requester.GetUID().String()) marker.TypeMeta = metav1.TypeMeta{ Kind: "DeletedMarker", - APIVersion: "storage.grafana.app/v0alpha1", // ?? or can we stick this in common? + APIVersion: "common.grafana.app/v0alpha1", // ?? or can we stick this in common? } marker.Annotations["RestoreResourceVersion"] = fmt.Sprintf("%d", event.PreviousRV) event.Value, err = json.Marshal(marker) @@ -488,20 +521,110 @@ func (s *server) List(ctx context.Context, req *ListRequest) (*ListResponse, err return rsp, err } +func (s *server) initWatcher() error { + var err error + s.broadcaster, err = NewBroadcaster(s.ctx, func(out chan<- *WrittenEvent) error { + events, err := s.store.WatchWriteEvents(s.ctx) + if err != nil { + return err + } + go func() { + for { + // pipe all events + v := <-events + out <- v + } + }() + return nil + }) + return err +} + func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error { if err := s.Init(); err != nil { return err } - // TODO??? can we move any of the common processing here? - stream, err := s.store.Watch(srv.Context(), req) + fmt.Printf("WATCH %v\n", req.Options.Key) + + ctx := srv.Context() + + // Start listening -- this will buffer any changes that happen while we backfill + stream, err := s.broadcaster.Subscribe(ctx) if err != nil { return err } - for event := range stream { - srv.Send(event) + defer s.broadcaster.Unsubscribe(stream) + + since := req.Since + if req.SendInitialEvents { + fmt.Printf("TODO... query\n") + + if req.AllowWatchBookmarks { + fmt.Printf("TODO... send bookmark\n") + } } - return nil + + for { + select { + case <-ctx.Done(): + return nil + + case event, ok := <-stream: + if !ok { + s.log.Debug("watch events closed") + return nil + } + + if event.ResourceVersion > since && matchesQueryKey(req.Options.Key, event.Key) { + srv.Send(&WatchEvent{ + Timestamp: event.Timestamp, + Type: event.Type, + Resource: &WatchEvent_Resource{ + Value: event.Value, + Version: event.ResourceVersion, + }, + // TODO... previous??? + }) + } + } + } +} + +// GetBlob implements ResourceServer. +func (s *server) PutBlob(ctx context.Context, req *PutBlobRequest) (*PutBlobResponse, error) { + if err := s.Init(); err != nil { + return nil, err + } + rsp, err := s.blob.PutBlob(ctx, req) + rsp.Status, err = errToStatus(err) + return rsp, err +} + +func (s *server) getPartialObject(ctx context.Context, key *ResourceKey, rv int64) (utils.GrafanaMetaAccessor, *StatusResult) { + rsp, err := s.store.Read(ctx, &ReadRequest{ + Key: key, + ResourceVersion: rv, + }) + if err != nil { + rsp.Status, _ = errToStatus(err) + } + if rsp.Status != nil { + return nil, rsp.Status + } + + partial := &metav1.PartialObjectMetadata{} + err = json.Unmarshal(rsp.Value, partial) + if err != nil { + rsp.Status, _ = errToStatus(fmt.Errorf("error reading body %w", err)) + return nil, rsp.Status + } + obj, err := utils.MetaAccessor(partial) + if err != nil { + rsp.Status, _ = errToStatus(fmt.Errorf("error getting accessor %w", err)) + return nil, rsp.Status + } + return obj, nil } // GetBlob implements ResourceServer. @@ -509,7 +632,23 @@ func (s *server) GetBlob(ctx context.Context, req *GetBlobRequest) (*GetBlobResp if err := s.Init(); err != nil { return nil, err } - rsp, err := s.search.GetBlob(ctx, req) + + // NOTE: in SQL... this could be simple select rather than a full fetch and extract + obj, status := s.getPartialObject(ctx, req.Resource, req.ResourceVersion) + if status != nil { + return &GetBlobResponse{Status: status}, nil + } + + info := obj.GetBlob() + if info == nil || info.UID == "" { + return &GetBlobResponse{Status: &StatusResult{ + Status: "Failure", + Message: "Resource does not have a linked blob", + Code: 404, + }}, nil + } + + rsp, err := s.blob.GetBlob(ctx, req.Resource, info, req.MustProxyBytes) rsp.Status, err = errToStatus(err) return rsp, err } diff --git a/pkg/storage/unified/resource/server_test.go b/pkg/storage/unified/resource/server_test.go index 1c5e24d66b1..5b69495a145 100644 --- a/pkg/storage/unified/resource/server_test.go +++ b/pkg/storage/unified/resource/server_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" - "github.com/hack-pad/hackpadfs" - hackos "github.com/hack-pad/hackpadfs/os" "github.com/stretchr/testify/require" + "gocloud.dev/blob/fileblob" + "gocloud.dev/blob/memblob" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -28,20 +28,26 @@ func TestSimpleServer(t *testing.T) { } ctx := identity.WithRequester(context.Background(), testUserA) - var root hackpadfs.FS - if true { + bucket := memblob.OpenBucket(nil) + if false { tmp, err := os.MkdirTemp("", "xxx-*") require.NoError(t, err) - root, err = hackos.NewFS().Sub(tmp[1:]) + bucket, err = fileblob.OpenBucket(tmp, &fileblob.Options{ + CreateDir: true, + Metadata: fileblob.MetadataDontWrite, // skip + }) require.NoError(t, err) + fmt.Printf("ROOT: %s\n\n", tmp) } + store, err := NewCDKAppendingStore(ctx, CDKAppenderOptions{ + Bucket: bucket, + }) + require.NoError(t, err) server, err := NewResourceServer(ResourceServerOptions{ - Store: NewFileSystemStore(FileSystemOptions{ - Root: root, - }), + Store: store, }) require.NoError(t, err) diff --git a/pkg/storage/unified/sqlnext/resource_mig.go b/pkg/storage/unified/sqlnext/resource_mig.go index 03152fa2604..8a9d1d776c0 100644 --- a/pkg/storage/unified/sqlnext/resource_mig.go +++ b/pkg/storage/unified/sqlnext/resource_mig.go @@ -7,7 +7,7 @@ import ( ) func InitResourceTables(mg *migrator.Migrator) string { - marker := "Initialize resource tables (v0)" // changing this key wipe+rewrite everything + marker := "Initialize resource tables (vX)" // changing this key wipe+rewrite everything mg.AddMigration(marker, &migrator.RawSQLMigration{}) tables := []migrator.Table{} @@ -65,7 +65,7 @@ func InitResourceTables(mg *migrator.Migrator) string { {Name: "hash", Type: migrator.DB_NVarchar, Length: 32, Nullable: false}, // Path to linked blob (or null). This blob may be saved in SQL, or in an object store - {Name: "blob_path_hash", Type: migrator.DB_NVarchar, Length: 60, Nullable: true}, + {Name: "blob_uid", Type: migrator.DB_NVarchar, Length: 60, Nullable: true}, }, Indices: []*migrator.Index{ {Cols: []string{"rv"}, Type: migrator.UniqueIndex}, @@ -74,7 +74,7 @@ func InitResourceTables(mg *migrator.Migrator) string { {Cols: []string{"operation"}, Type: migrator.IndexType}, {Cols: []string{"namespace"}, Type: migrator.IndexType}, {Cols: []string{"group", "resource", "name"}, Type: migrator.IndexType}, - {Cols: []string{"blob_path_hash"}, Type: migrator.IndexType}, + {Cols: []string{"blob_uid"}, Type: migrator.IndexType}, }, }) @@ -111,19 +111,28 @@ func InitResourceTables(mg *migrator.Migrator) string { }, }) - // This table is optional -- values can be saved in blob storage + // This table is optional, blobs can also be saved to object store or disk + // This is an append only store tables = append(tables, migrator.Table{ Name: "resource_blob", // even things that failed? Columns: []*migrator.Column{ - {Name: "path_hash", Type: migrator.DB_NVarchar, Length: 60, Nullable: false, IsPrimaryKey: true}, - {Name: "path", Type: migrator.DB_Text, Nullable: false}, - {Name: "body", Type: migrator.DB_Blob, Nullable: true}, + {Name: "uid", Type: migrator.DB_NVarchar, Length: 60, Nullable: false, IsPrimaryKey: true}, + {Name: "value", Type: migrator.DB_Blob, Nullable: true}, {Name: "etag", Type: migrator.DB_NVarchar, Length: 64, Nullable: false}, {Name: "size", Type: migrator.DB_BigInt, Nullable: false}, {Name: "content_type", Type: migrator.DB_NVarchar, Length: 255, Nullable: false}, + + // These is used for auditing and cleanup (could be path?) + {Name: "namespace", Type: migrator.DB_NVarchar, Length: 63, Nullable: true}, + {Name: "group", Type: migrator.DB_NVarchar, Length: 190, Nullable: false}, + {Name: "resource", Type: migrator.DB_NVarchar, Length: 190, Nullable: false}, + {Name: "name", Type: migrator.DB_NVarchar, Length: 190, Nullable: false}, }, Indices: []*migrator.Index{ - {Cols: []string{"path_hash"}, Type: migrator.UniqueIndex}, + {Cols: []string{"uid"}, Type: migrator.UniqueIndex}, + + // Used for auditing + {Cols: []string{"namespace", "group", "resource", "name"}, Type: migrator.IndexType}, }, }) diff --git a/pkg/storage/unified/sqlnext/sql_resources.go b/pkg/storage/unified/sqlnext/sql_resources.go index d40f14d90a1..9d525a700df 100644 --- a/pkg/storage/unified/sqlnext/sql_resources.go +++ b/pkg/storage/unified/sqlnext/sql_resources.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "strings" "github.com/prometheus/client_golang/prometheus" @@ -139,15 +140,10 @@ func (s *sqlResourceStore) WriteEvent(ctx context.Context, event resource.WriteE return 0, ErrNotImplementedYet } -func (s *sqlResourceStore) Watch(context.Context, *resource.WatchRequest) (chan *resource.WatchEvent, error) { +func (s *sqlResourceStore) WatchWriteEvents(ctx context.Context) (<-chan *resource.WrittenEvent, error) { return nil, ErrNotImplementedYet } -// Create new name for a given resource -func (s *sqlResourceStore) GenerateName(_ context.Context, _ *resource.ResourceKey, _ string) (string, error) { - return util.GenerateShortUID(), nil -} - func (s *sqlResourceStore) Read(ctx context.Context, req *resource.ReadRequest) (*resource.ReadResponse, error) { _, span := s.tracer.Start(ctx, "storage_server.GetResource") defer span.End() @@ -166,13 +162,25 @@ func (s *sqlResourceStore) List(ctx context.Context, req *resource.ListRequest) return nil, ErrNotImplementedYet } -// Get the raw blob bytes and metadata -func (s *sqlResourceStore) GetBlob(ctx context.Context, req *resource.GetBlobRequest) (*resource.GetBlobResponse, error) { - _, span := s.tracer.Start(ctx, "storage_server.List") - defer span.End() +func (s *sqlResourceStore) PutBlob(ctx context.Context, req *resource.PutBlobRequest) (*resource.PutBlobResponse, error) { + if req.Method == resource.PutBlobRequest_HTTP { + return &resource.PutBlobResponse{ + Status: &resource.StatusResult{ + Status: "Failure", + Message: "http upload not supported", + Code: http.StatusNotImplemented, + }, + }, nil + } - fmt.Printf("TODO, GET BLOB: %+v", req.Key) + uid := util.GenerateShortUID() + fmt.Printf("TODO, UPLOAD: %s // %+v", uid, req) + + return nil, ErrNotImplementedYet +} + +func (s *sqlResourceStore) GetBlob(ctx context.Context, uid string, mustProxy bool) (*resource.GetBlobResponse, error) { return nil, ErrNotImplementedYet }