From 830c4ea81c867746e4f4bdc3b9817eaf87756aa0 Mon Sep 17 00:00:00 2001 From: Semir Patel Date: Tue, 10 Oct 2023 13:30:23 -0500 Subject: [PATCH] v2tenancy: cluster scoped reads (#19082) --- agent/grpc-external/services/resource/read.go | 19 +- .../services/resource/read_test.go | 25 +- .../services/resource/testing/testing.go | 91 ++++--- internal/resource/demo/demo.go | 49 +++- internal/resource/http/http_test.go | 8 +- proto/private/pbdemo/v1/demo.pb.binary.go | 10 + proto/private/pbdemo/v1/demo.pb.go | 223 +++++++++++------- proto/private/pbdemo/v1/demo.proto | 7 + 8 files changed, 303 insertions(+), 129 deletions(-) diff --git a/agent/grpc-external/services/resource/read.go b/agent/grpc-external/services/resource/read.go index 9f95995ddc..351a503856 100644 --- a/agent/grpc-external/services/resource/read.go +++ b/agent/grpc-external/services/resource/read.go @@ -111,6 +111,23 @@ func (s *Server) validateReadRequest(req *pbresource.ReadRequest) (*resource.Reg req.Id.Tenancy.Namespace, ) } - + if reg.Scope == resource.ScopeCluster { + if req.Id.Tenancy.Partition != "" { + return nil, status.Errorf( + codes.InvalidArgument, + "cluster scoped resource %s cannot have a partition: %s", + resource.ToGVK(req.Id.Type), + req.Id.Tenancy.Partition, + ) + } + if req.Id.Tenancy.Namespace != "" { + return nil, status.Errorf( + codes.InvalidArgument, + "cluster scoped resource %s cannot have a namespace: %s", + resource.ToGVK(req.Id.Type), + req.Id.Tenancy.Namespace, + ) + } + } return reg, nil } diff --git a/agent/grpc-external/services/resource/read_test.go b/agent/grpc-external/services/resource/read_test.go index f2ce1e4497..2601689bc6 100644 --- a/agent/grpc-external/services/resource/read_test.go +++ b/agent/grpc-external/services/resource/read_test.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource/demo" "github.com/hashicorp/consul/internal/storage" + "github.com/hashicorp/consul/internal/tenancy" "github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto/private/prototest" "github.com/hashicorp/consul/sdk/testutil" @@ -30,22 +31,31 @@ import ( func TestRead_InputValidation(t *testing.T) { server := testServer(t) client := testClient(t, server) + tenancy.RegisterTypes(server.Registry) demo.RegisterTypes(server.Registry) - testCases := map[string]func(artistId, recordlabelId *pbresource.ID) *pbresource.ID{ - "no id": func(artistId, recordLabelId *pbresource.ID) *pbresource.ID { return nil }, - "no type": func(artistId, _ *pbresource.ID) *pbresource.ID { + testCases := map[string]func(artistId, recordlabelId, executiveId *pbresource.ID) *pbresource.ID{ + "no id": func(_, _, _ *pbresource.ID) *pbresource.ID { return nil }, + "no type": func(artistId, _, _ *pbresource.ID) *pbresource.ID { artistId.Type = nil return artistId }, - "no name": func(artistId, _ *pbresource.ID) *pbresource.ID { + "no name": func(artistId, _, _ *pbresource.ID) *pbresource.ID { artistId.Name = "" return artistId }, - "partition scope with non-empty namespace": func(_, recordLabelId *pbresource.ID) *pbresource.ID { + "partition scope with non-empty namespace": func(_, recordLabelId, _ *pbresource.ID) *pbresource.ID { recordLabelId.Tenancy.Namespace = "ishouldnothaveanamespace" return recordLabelId }, + "cluster scope with non-empty partition": func(_, _, executiveId *pbresource.ID) *pbresource.ID { + executiveId.Tenancy = &pbresource.Tenancy{Partition: resource.DefaultPartitionName} + return executiveId + }, + "cluster scope with non-empty namespace": func(_, _, executiveId *pbresource.ID) *pbresource.ID { + executiveId.Tenancy = &pbresource.Tenancy{Namespace: resource.DefaultNamespaceName} + return executiveId + }, } for desc, modFn := range testCases { t.Run(desc, func(t *testing.T) { @@ -55,8 +65,11 @@ func TestRead_InputValidation(t *testing.T) { recordLabel, err := demo.GenerateV1RecordLabel("LoonyTunes") require.NoError(t, err) + executive, err := demo.GenerateV1Executive("MusicMan", "CEO") + require.NoError(t, err) + // Each test case picks which resource to use based on the resource type's scope. - req := &pbresource.ReadRequest{Id: modFn(artist.Id, recordLabel.Id)} + req := &pbresource.ReadRequest{Id: modFn(artist.Id, recordLabel.Id, executive.Id)} _, err = client.Read(testContext(t), req) require.Error(t, err) diff --git a/agent/grpc-external/services/resource/testing/testing.go b/agent/grpc-external/services/resource/testing/testing.go index ea06526fd1..16eedd3a96 100644 --- a/agent/grpc-external/services/resource/testing/testing.go +++ b/agent/grpc-external/services/resource/testing/testing.go @@ -54,59 +54,78 @@ func AuthorizerFrom(t *testing.T, policyStrs ...string) resolver.Result { // returns a client to interact with it. ACLs will be disabled and only the // default partition and namespace are available. func RunResourceService(t *testing.T, registerFns ...func(resource.Registry)) pbresource.ResourceServiceClient { - // Provide a resolver which will default partition and namespace when not provided. This is similar to user - // initiated requests. - // - // Controllers under test should be providing full tenancy since they will run with the DANGER_NO_AUTH. - mockACLResolver := &svc.MockACLResolver{} - mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything). - Return(testutils.ACLsDisabled(t), nil). - Run(func(args mock.Arguments) { - // Caller expecting passed in tokenEntMeta and authorizerContext to be filled in. - tokenEntMeta := args.Get(1).(*acl.EnterpriseMeta) - if tokenEntMeta != nil { - FillEntMeta(tokenEntMeta) - } - - authzContext := args.Get(2).(*acl.AuthorizerContext) - if authzContext != nil { - FillAuthorizerContext(authzContext) - } - }) - - return RunResourceServiceWithACL(t, mockACLResolver, registerFns...) + return RunResourceServiceWithConfig(t, svc.Config{}, registerFns...) } -func RunResourceServiceWithACL(t *testing.T, aclResolver svc.ACLResolver, registerFns ...func(resource.Registry)) pbresource.ResourceServiceClient { +// RunResourceServiceWithConfig runs a ResourceService with caller injectable config to ease mocking dependencies. +// Any nil config field is replaced with a reasonable default with the following behavior: +// +// config.Backend - cannot be configured and must be nil +// config.Registry - empty registry +// config.TenancyBridge - mock provided with only the default partition and namespace +// config.ACLResolver - mock provided with ACLs disabled. Fills entMeta and authzContext with default partition and namespace +func RunResourceServiceWithConfig(t *testing.T, config svc.Config, registerFns ...func(resource.Registry)) pbresource.ResourceServiceClient { t.Helper() + if config.Backend != nil { + panic("backend can not be configured") + } + backend, err := inmem.NewBackend() require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) go backend.Run(ctx) + config.Backend = backend + + if config.Registry == nil { + config.Registry = resource.NewRegistry() + } - registry := resource.NewRegistry() for _, fn := range registerFns { - fn(registry) + fn(config.Registry) } server := grpc.NewServer() - mockTenancyBridge := &svc.MockTenancyBridge{} - mockTenancyBridge.On("PartitionExists", resource.DefaultPartitionName).Return(true, nil) - mockTenancyBridge.On("NamespaceExists", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(true, nil) - mockTenancyBridge.On("IsPartitionMarkedForDeletion", resource.DefaultPartitionName).Return(false, nil) - mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil) + if config.TenancyBridge == nil { + mockTenancyBridge := &svc.MockTenancyBridge{} + mockTenancyBridge.On("PartitionExists", resource.DefaultPartitionName).Return(true, nil) + mockTenancyBridge.On("NamespaceExists", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(true, nil) + mockTenancyBridge.On("IsPartitionMarkedForDeletion", resource.DefaultPartitionName).Return(false, nil) + mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil) + config.TenancyBridge = mockTenancyBridge + } - svc.NewServer(svc.Config{ - Backend: backend, - Registry: registry, - Logger: testutil.Logger(t), - ACLResolver: aclResolver, - TenancyBridge: mockTenancyBridge, - }).Register(server) + if config.ACLResolver == nil { + // Provide a resolver which will default partition and namespace when not provided. This is similar to user + // initiated requests. + // + // Controllers under test should be providing full tenancy since they will run with the DANGER_NO_AUTH. + mockACLResolver := &svc.MockACLResolver{} + mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything). + Return(testutils.ACLsDisabled(t), nil). + Run(func(args mock.Arguments) { + // Caller expecting passed in tokenEntMeta and authorizerContext to be filled in. + tokenEntMeta := args.Get(1).(*acl.EnterpriseMeta) + if tokenEntMeta != nil { + FillEntMeta(tokenEntMeta) + } + + authzContext := args.Get(2).(*acl.AuthorizerContext) + if authzContext != nil { + FillAuthorizerContext(authzContext) + } + }) + config.ACLResolver = mockACLResolver + } + + if config.Logger == nil { + config.Logger = testutil.Logger(t) + } + + svc.NewServer(config).Register(server) pipe := internal.NewPipeListener() go server.Serve(pipe) diff --git a/internal/resource/demo/demo.go b/internal/resource/demo/demo.go index a6d15407a5..b6a9263842 100644 --- a/internal/resource/demo/demo.go +++ b/internal/resource/demo/demo.go @@ -22,8 +22,16 @@ import ( ) var ( + // TypeV1Executive represents a a C-suite executive of the company. + // Used as a resource to test cluster scope. + TypeV1Executive = &pbresource.Type{ + Group: "demo", + GroupVersion: "v1", + Kind: "Executive", + } + // TypeV1RecordLabel represents a record label which artists are signed to. - // Used specifically as a resource to test partition only scoped resources. + // Used as a resource to test partiion scope. TypeV1RecordLabel = &pbresource.Type{ Group: "demo", GroupVersion: "v1", @@ -72,8 +80,12 @@ const ( ArtistV2ReadPolicy = `key_prefix "resource/demo.v2.Artist/" { policy = "read" }` ArtistV2WritePolicy = `key_prefix "resource/demo.v2.Artist/" { policy = "write" }` ArtistV2ListPolicy = `key_prefix "resource/" { policy = "list" }` - LabelV1ReadPolicy = `key_prefix "resource/demo.v1.Label/" { policy = "read" }` - LabelV1WritePolicy = `key_prefix "resource/demo.v1.Label/" { policy = "write" }` + + ExecutiveV1ReadPolicy = `key_prefix "resource/demo.v1.Executive/" { policy = "read" }` + ExecutiveV1WritePolicy = `key_prefix "resource/demo.v1.Executive/" { policy = "write" }` + + LabelV1ReadPolicy = `key_prefix "resource/demo.v1.Label/" { policy = "read" }` + LabelV1WritePolicy = `key_prefix "resource/demo.v1.Label/" { policy = "write" }` ) // RegisterTypes registers the demo types. Should only be called in tests and @@ -139,6 +151,17 @@ func RegisterTypes(r resource.Registry) { return nil } + r.Register(resource.Registration{ + Type: TypeV1Executive, + Proto: &pbdemov1.Executive{}, + Scope: resource.ScopeCluster, + ACLs: &resource.ACLHooks{ + Read: readACL, + Write: writeACL, + List: makeListACL(TypeV1Executive), + }, + }) + r.Register(resource.Registration{ Type: TypeV1RecordLabel, Proto: &pbdemov1.RecordLabel{}, @@ -209,6 +232,26 @@ func RegisterTypes(r resource.Registry) { }) } +// GenerateV1Executive generates a named Executive resource. +func GenerateV1Executive(name, position string) (*pbresource.Resource, error) { + data, err := anypb.New(&pbdemov1.Executive{Position: position}) + if err != nil { + return nil, err + } + + return &pbresource.Resource{ + Id: &pbresource.ID{ + Type: TypeV1Executive, + Tenancy: resource.DefaultClusteredTenancy(), + Name: name, + }, + Data: data, + Metadata: map[string]string{ + "generated_at": time.Now().Format(time.RFC3339), + }, + }, nil +} + // GenerateV1RecordLabel generates a named RecordLabel resource. func GenerateV1RecordLabel(name string) (*pbresource.Resource, error) { data, err := anypb.New(&pbdemov1.RecordLabel{Name: name}) diff --git a/internal/resource/http/http_test.go b/internal/resource/http/http_test.go index 7510650237..50f50fbe39 100644 --- a/internal/resource/http/http_test.go +++ b/internal/resource/http/http_test.go @@ -131,7 +131,7 @@ func TestResourceWriteHandler(t *testing.T) { aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything). Return(svctest.AuthorizerFrom(t, demo.ArtistV1WritePolicy, demo.ArtistV2WritePolicy), nil) - client := svctest.RunResourceServiceWithACL(t, aclResolver, demo.RegisterTypes) + client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes) r := resource.NewRegistry() demo.RegisterTypes(r) @@ -359,7 +359,7 @@ func TestResourceReadHandler(t *testing.T) { aclResolver.On("ResolveTokenAndDefaultMeta", fakeToken, mock.Anything, mock.Anything). Return(svctest.AuthorizerFrom(t, ""), nil) - client := svctest.RunResourceServiceWithACL(t, aclResolver, demo.RegisterTypes) + client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes) r := resource.NewRegistry() demo.RegisterTypes(r) @@ -412,7 +412,7 @@ func TestResourceDeleteHandler(t *testing.T) { aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything). Return(svctest.AuthorizerFrom(t, demo.ArtistV2WritePolicy), nil) - client := svctest.RunResourceServiceWithACL(t, aclResolver, demo.RegisterTypes) + client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes) r := resource.NewRegistry() demo.RegisterTypes(r) @@ -489,7 +489,7 @@ func TestResourceListHandler(t *testing.T) { aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything). Return(svctest.AuthorizerFrom(t, demo.ArtistV2WritePolicy), nil) - client := svctest.RunResourceServiceWithACL(t, aclResolver, demo.RegisterTypes) + client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes) r := resource.NewRegistry() demo.RegisterTypes(r) diff --git a/proto/private/pbdemo/v1/demo.pb.binary.go b/proto/private/pbdemo/v1/demo.pb.binary.go index 7f20c0ef00..649e4c5fbb 100644 --- a/proto/private/pbdemo/v1/demo.pb.binary.go +++ b/proto/private/pbdemo/v1/demo.pb.binary.go @@ -7,6 +7,16 @@ import ( "google.golang.org/protobuf/proto" ) +// MarshalBinary implements encoding.BinaryMarshaler +func (msg *Executive) MarshalBinary() ([]byte, error) { + return proto.Marshal(msg) +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (msg *Executive) UnmarshalBinary(b []byte) error { + return proto.Unmarshal(b, msg) +} + // MarshalBinary implements encoding.BinaryMarshaler func (msg *RecordLabel) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) diff --git a/proto/private/pbdemo/v1/demo.pb.go b/proto/private/pbdemo/v1/demo.pb.go index 34bfabd7ec..cb3e2b8cd3 100644 --- a/proto/private/pbdemo/v1/demo.pb.go +++ b/proto/private/pbdemo/v1/demo.pb.go @@ -105,6 +105,55 @@ func (Genre) EnumDescriptor() ([]byte, []int) { return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{0} } +// Cluster scoped resource. +type Executive struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Position string `protobuf:"bytes,1,opt,name=position,proto3" json:"position,omitempty"` +} + +func (x *Executive) Reset() { + *x = Executive{} + if protoimpl.UnsafeEnabled { + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Executive) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Executive) ProtoMessage() {} + +func (x *Executive) ProtoReflect() protoreflect.Message { + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[0] + 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 Executive.ProtoReflect.Descriptor instead. +func (*Executive) Descriptor() ([]byte, []int) { + return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{0} +} + +func (x *Executive) GetPosition() string { + if x != nil { + return x.Position + } + return "" +} + +// Partition scoped resource. type RecordLabel struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -117,7 +166,7 @@ type RecordLabel struct { func (x *RecordLabel) Reset() { *x = RecordLabel{} if protoimpl.UnsafeEnabled { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[0] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -130,7 +179,7 @@ func (x *RecordLabel) String() string { func (*RecordLabel) ProtoMessage() {} func (x *RecordLabel) ProtoReflect() protoreflect.Message { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[0] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -143,7 +192,7 @@ func (x *RecordLabel) ProtoReflect() protoreflect.Message { // Deprecated: Use RecordLabel.ProtoReflect.Descriptor instead. func (*RecordLabel) Descriptor() ([]byte, []int) { - return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{0} + return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{1} } func (x *RecordLabel) GetName() string { @@ -160,6 +209,7 @@ func (x *RecordLabel) GetDescription() string { return "" } +// Namespace scoped resource. type Artist struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -174,7 +224,7 @@ type Artist struct { func (x *Artist) Reset() { *x = Artist{} if protoimpl.UnsafeEnabled { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[1] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -187,7 +237,7 @@ func (x *Artist) String() string { func (*Artist) ProtoMessage() {} func (x *Artist) ProtoReflect() protoreflect.Message { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[1] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -200,7 +250,7 @@ func (x *Artist) ProtoReflect() protoreflect.Message { // Deprecated: Use Artist.ProtoReflect.Descriptor instead. func (*Artist) Descriptor() ([]byte, []int) { - return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{1} + return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{2} } func (x *Artist) GetName() string { @@ -245,7 +295,7 @@ type Album struct { func (x *Album) Reset() { *x = Album{} if protoimpl.UnsafeEnabled { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[2] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -258,7 +308,7 @@ func (x *Album) String() string { func (*Album) ProtoMessage() {} func (x *Album) ProtoReflect() protoreflect.Message { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[2] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -271,7 +321,7 @@ func (x *Album) ProtoReflect() protoreflect.Message { // Deprecated: Use Album.ProtoReflect.Descriptor instead. func (*Album) Descriptor() ([]byte, []int) { - return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{2} + return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{3} } func (x *Album) GetName() string { @@ -311,7 +361,7 @@ type Concept struct { func (x *Concept) Reset() { *x = Concept{} if protoimpl.UnsafeEnabled { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[3] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -324,7 +374,7 @@ func (x *Concept) String() string { func (*Concept) ProtoMessage() {} func (x *Concept) ProtoReflect() protoreflect.Message { - mi := &file_private_pbdemo_v1_demo_proto_msgTypes[3] + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -337,7 +387,7 @@ func (x *Concept) ProtoReflect() protoreflect.Message { // Deprecated: Use Concept.ProtoReflect.Descriptor instead. func (*Concept) Descriptor() ([]byte, []int) { - return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{3} + return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{4} } var File_private_pbdemo_v1_demo_proto protoreflect.FileDescriptor @@ -347,64 +397,66 @@ var file_private_pbdemo_v1_demo_proto_rawDesc = []byte{ 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, - 0x31, 0x22, 0x43, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa3, 0x01, 0x0a, 0x06, 0x41, 0x72, 0x74, 0x69, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, - 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, - 0x05, 0x41, 0x6c, 0x62, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x79, 0x65, - 0x61, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0d, 0x79, 0x65, 0x61, 0x72, 0x4f, 0x66, 0x52, 0x65, 0x6c, 0x65, 0x61, - 0x73, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, - 0x5f, 0x61, 0x63, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x13, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x6c, - 0x61, 0x69, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x22, 0x09, 0x0a, - 0x07, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, - 0x72, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x4a, 0x41, 0x5a, 0x5a, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x46, 0x4f, 0x4c, 0x4b, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x50, 0x4f, 0x50, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, - 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x50, 0x55, 0x4e, 0x4b, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x42, 0x4c, 0x55, 0x45, 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, - 0x4e, 0x52, 0x45, 0x5f, 0x52, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, - 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, - 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, - 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, - 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, - 0x50, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, - 0x49, 0x45, 0x10, 0x0c, 0x42, 0x97, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x31, 0x42, 0x09, - 0x44, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x31, - 0x3b, 0x64, 0x65, 0x6d, 0x6f, 0x76, 0x31, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, - 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, - 0x56, 0x31, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, - 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x44, 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x22, 0x27, 0x0a, 0x09, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x0b, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0xa3, 0x01, 0x0a, 0x06, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x3e, 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x28, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x62, 0x75, 0x6d, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x79, 0x65, 0x61, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x79, 0x65, + 0x61, 0x72, 0x4f, 0x66, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, + 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x63, 0x72, 0x69, 0x74, 0x69, + 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x12, 0x16, + 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x22, 0x09, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x70, + 0x74, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, + 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4a, 0x41, 0x5a, 0x5a, + 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x46, 0x4f, 0x4c, 0x4b, + 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x4f, 0x50, 0x10, + 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x55, 0x4e, 0x4b, + 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x42, 0x4c, 0x55, 0x45, + 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x52, 0x5f, 0x41, + 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, + 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, + 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, + 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x45, 0x10, 0x0c, 0x42, 0x97, 0x02, + 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, + 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x44, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, + 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x65, 0x6d, 0x6f, 0x76, 0x31, + 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x21, 0x48, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0xe2, + 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x44, + 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -420,13 +472,14 @@ func file_private_pbdemo_v1_demo_proto_rawDescGZIP() []byte { } var file_private_pbdemo_v1_demo_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_private_pbdemo_v1_demo_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_private_pbdemo_v1_demo_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_private_pbdemo_v1_demo_proto_goTypes = []interface{}{ (Genre)(0), // 0: hashicorp.consul.internal.demo.v1.Genre - (*RecordLabel)(nil), // 1: hashicorp.consul.internal.demo.v1.RecordLabel - (*Artist)(nil), // 2: hashicorp.consul.internal.demo.v1.Artist - (*Album)(nil), // 3: hashicorp.consul.internal.demo.v1.Album - (*Concept)(nil), // 4: hashicorp.consul.internal.demo.v1.Concept + (*Executive)(nil), // 1: hashicorp.consul.internal.demo.v1.Executive + (*RecordLabel)(nil), // 2: hashicorp.consul.internal.demo.v1.RecordLabel + (*Artist)(nil), // 3: hashicorp.consul.internal.demo.v1.Artist + (*Album)(nil), // 4: hashicorp.consul.internal.demo.v1.Album + (*Concept)(nil), // 5: hashicorp.consul.internal.demo.v1.Concept } var file_private_pbdemo_v1_demo_proto_depIdxs = []int32{ 0, // 0: hashicorp.consul.internal.demo.v1.Artist.genre:type_name -> hashicorp.consul.internal.demo.v1.Genre @@ -444,7 +497,7 @@ func file_private_pbdemo_v1_demo_proto_init() { } if !protoimpl.UnsafeEnabled { file_private_pbdemo_v1_demo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RecordLabel); i { + switch v := v.(*Executive); i { case 0: return &v.state case 1: @@ -456,7 +509,7 @@ func file_private_pbdemo_v1_demo_proto_init() { } } file_private_pbdemo_v1_demo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Artist); i { + switch v := v.(*RecordLabel); i { case 0: return &v.state case 1: @@ -468,7 +521,7 @@ func file_private_pbdemo_v1_demo_proto_init() { } } file_private_pbdemo_v1_demo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Album); i { + switch v := v.(*Artist); i { case 0: return &v.state case 1: @@ -480,6 +533,18 @@ func file_private_pbdemo_v1_demo_proto_init() { } } file_private_pbdemo_v1_demo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Album); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_private_pbdemo_v1_demo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Concept); i { case 0: return &v.state @@ -498,7 +563,7 @@ func file_private_pbdemo_v1_demo_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_private_pbdemo_v1_demo_proto_rawDesc, NumEnums: 1, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/private/pbdemo/v1/demo.proto b/proto/private/pbdemo/v1/demo.proto index 3b6e978477..be2f393aaf 100644 --- a/proto/private/pbdemo/v1/demo.proto +++ b/proto/private/pbdemo/v1/demo.proto @@ -7,11 +7,18 @@ syntax = "proto3"; // Consul's generic storage APIs. package hashicorp.consul.internal.demo.v1; +// Cluster scoped resource. +message Executive { + string position = 1; +} + +// Partition scoped resource. message RecordLabel { string name = 1; string description = 2; } +// Namespace scoped resource. message Artist { string name = 1; string description = 2;