mirror of https://github.com/status-im/consul.git
Resource `Write` endpoint (#16786)
This commit is contained in:
parent
ad3a68a040
commit
4fa2537b3b
|
@ -51,7 +51,7 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/services/connectca"
|
"github.com/hashicorp/consul/agent/grpc-external/services/connectca"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/services/dataplane"
|
"github.com/hashicorp/consul/agent/grpc-external/services/dataplane"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/services/peerstream"
|
"github.com/hashicorp/consul/agent/grpc-external/services/peerstream"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
resourcegrpc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/services/serverdiscovery"
|
"github.com/hashicorp/consul/agent/grpc-external/services/serverdiscovery"
|
||||||
agentgrpc "github.com/hashicorp/consul/agent/grpc-internal"
|
agentgrpc "github.com/hashicorp/consul/agent/grpc-internal"
|
||||||
"github.com/hashicorp/consul/agent/grpc-internal/services/subscribe"
|
"github.com/hashicorp/consul/agent/grpc-internal/services/subscribe"
|
||||||
|
@ -65,6 +65,8 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/rpc/peering"
|
"github.com/hashicorp/consul/agent/rpc/peering"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/agent/token"
|
"github.com/hashicorp/consul/agent/token"
|
||||||
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
raftstorage "github.com/hashicorp/consul/internal/storage/raft"
|
raftstorage "github.com/hashicorp/consul/internal/storage/raft"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
|
@ -1196,7 +1198,6 @@ func (s *Server) setupRPC() error {
|
||||||
|
|
||||||
// Initialize and register services on external gRPC server.
|
// Initialize and register services on external gRPC server.
|
||||||
func (s *Server) setupExternalGRPC(config *Config, backend storage.Backend, logger hclog.Logger) {
|
func (s *Server) setupExternalGRPC(config *Config, backend storage.Backend, logger hclog.Logger) {
|
||||||
|
|
||||||
s.externalACLServer = aclgrpc.NewServer(aclgrpc.Config{
|
s.externalACLServer = aclgrpc.NewServer(aclgrpc.Config{
|
||||||
ACLsEnabled: s.config.ACLsEnabled,
|
ACLsEnabled: s.config.ACLsEnabled,
|
||||||
ForwardRPC: func(info structs.RPCInfo, fn func(*grpc.ClientConn) error) (bool, error) {
|
ForwardRPC: func(info structs.RPCInfo, fn func(*grpc.ClientConn) error) (bool, error) {
|
||||||
|
@ -1261,8 +1262,16 @@ func (s *Server) setupExternalGRPC(config *Config, backend storage.Backend, logg
|
||||||
})
|
})
|
||||||
s.peerStreamServer.Register(s.externalGRPCServer)
|
s.peerStreamServer.Register(s.externalGRPCServer)
|
||||||
|
|
||||||
resource.NewServer(resource.Config{
|
registry := resource.NewRegistry()
|
||||||
Backend: backend,
|
|
||||||
|
if s.config.DevMode {
|
||||||
|
demo.Register(registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcegrpc.NewServer(resourcegrpc.Config{
|
||||||
|
Registry: registry,
|
||||||
|
Backend: backend,
|
||||||
|
Logger: logger.Named("grpc-api.resource"),
|
||||||
}).Register(s.externalGRPCServer)
|
}).Register(s.externalGRPCServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
|
@ -25,25 +26,25 @@ func TestList_TypeNotFound(t *testing.T) {
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
_, err := client.List(context.Background(), &pbresource.ListRequest{
|
_, err := client.List(context.Background(), &pbresource.ListRequest{
|
||||||
Type: typev1,
|
Type: demo.TypeV2Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: demo.TenancyDefault,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
require.Contains(t, err.Error(), "resource type mesh/v1/service not registered")
|
require.Contains(t, err.Error(), "resource type demo/v2/artist not registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestList_Empty(t *testing.T) {
|
func TestList_Empty(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
|
demo.Register(server.Registry)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
|
||||||
|
|
||||||
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
||||||
Type: typev1,
|
Type: demo.TypeV1Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: demo.TenancyDefault,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -56,26 +57,25 @@ func TestList_Many(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
|
demo.Register(server.Registry)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
|
||||||
|
|
||||||
resources := make([]*pbresource.Resource, 10)
|
resources := make([]*pbresource.Resource, 10)
|
||||||
for i := 0; i < len(resources); i++ {
|
for i := 0; i < len(resources); i++ {
|
||||||
r := &pbresource.Resource{
|
artist, err := demo.GenerateV2Artist()
|
||||||
Id: &pbresource.ID{
|
require.NoError(t, err)
|
||||||
Uid: fmt.Sprintf("uid%d", i),
|
|
||||||
Name: fmt.Sprintf("name%d", i),
|
// Prevent test flakes if the generated names collide.
|
||||||
Type: typev1,
|
artist.Id.Name = fmt.Sprintf("%s-%d", artist.Id.Name, i)
|
||||||
Tenancy: tenancy,
|
_, err = server.Backend.WriteCAS(tc.ctx, artist)
|
||||||
},
|
require.NoError(t, err)
|
||||||
Version: "",
|
|
||||||
}
|
resources[i] = artist
|
||||||
server.Backend.WriteCAS(tc.ctx, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
||||||
Type: typev1,
|
Type: demo.TypeV2Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: demo.TenancyDefault,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -88,13 +88,18 @@ func TestList_GroupVersionMismatch(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
|
demo.Register(server.Registry)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
|
||||||
server.Backend.WriteCAS(tc.ctx, &pbresource.Resource{Id: id2})
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = server.Backend.WriteCAS(tc.ctx, artist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
||||||
Type: typev1,
|
Type: demo.TypeV1Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: artist.Id.Tenancy,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -109,18 +114,21 @@ func TestList_VerifyReadConsistencyArg(t *testing.T) {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
mockBackend := NewMockBackend(t)
|
mockBackend := NewMockBackend(t)
|
||||||
server := NewServer(Config{
|
server := NewServer(Config{
|
||||||
registry: resource.NewRegistry(),
|
Registry: resource.NewRegistry(),
|
||||||
Backend: mockBackend,
|
Backend: mockBackend,
|
||||||
})
|
})
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
demo.Register(server.Registry)
|
||||||
resource1 := &pbresource.Resource{Id: id1, Version: "1"}
|
|
||||||
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockBackend.On("List", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
mockBackend.On("List", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return([]*pbresource.Resource{resource1}, nil)
|
Return([]*pbresource.Resource{artist}, nil)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{Type: typev1, Tenancy: tenancy, NamePrefix: ""})
|
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{Type: artist.Id.Type, Tenancy: artist.Id.Tenancy, NamePrefix: ""})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prototest.AssertDeepEqual(t, resource1, rsp.Resources[0])
|
prototest.AssertDeepEqual(t, artist, rsp.Resources[0])
|
||||||
mockBackend.AssertCalled(t, "List", mock.Anything, tc.consistency, mock.Anything, mock.Anything, mock.Anything)
|
mockBackend.AssertCalled(t, "List", mock.Anything, tc.consistency, mock.Anything, mock.Anything, mock.Anything)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,29 +14,36 @@ import (
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRead_TypeNotFound(t *testing.T) {
|
func TestRead_TypeNotFound(t *testing.T) {
|
||||||
server := NewServer(Config{registry: resource.NewRegistry()})
|
server := NewServer(Config{Registry: resource.NewRegistry()})
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
_, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: id1})
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.Read(context.Background(), &pbresource.ReadRequest{Id: artist.Id})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
require.Contains(t, err.Error(), "resource type mesh/v1/service not registered")
|
require.Contains(t, err.Error(), "resource type demo/v2/artist not registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRead_ResourceNotFound(t *testing.T) {
|
func TestRead_ResourceNotFound(t *testing.T) {
|
||||||
for desc, tc := range readTestCases() {
|
for desc, tc := range readTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
demo.Register(server.Registry)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
_, err := client.Read(tc.ctx, &pbresource.ReadRequest{Id: id1})
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.Read(tc.ctx, &pbresource.ReadRequest{Id: artist.Id})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||||
require.Contains(t, err.Error(), "resource not found")
|
require.Contains(t, err.Error(), "resource not found")
|
||||||
|
@ -48,15 +55,19 @@ func TestRead_GroupVersionMismatch(t *testing.T) {
|
||||||
for desc, tc := range readTestCases() {
|
for desc, tc := range readTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
demo.Register(server.Registry)
|
||||||
server.registry.Register(resource.Registration{Type: typev2})
|
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
resource1 := &pbresource.Resource{Id: id1, Version: ""}
|
artist, err := demo.GenerateV2Artist()
|
||||||
_, err := server.Backend.WriteCAS(tc.ctx, resource1)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.Read(tc.ctx, &pbresource.ReadRequest{Id: id2})
|
_, err = server.Backend.WriteCAS(tc.ctx, artist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
id := clone(artist.Id)
|
||||||
|
id.Type = demo.TypeV1Artist
|
||||||
|
|
||||||
|
_, err = client.Read(tc.ctx, &pbresource.ReadRequest{Id: id})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
require.Contains(t, err.Error(), "resource was requested with GroupVersion")
|
require.Contains(t, err.Error(), "resource was requested with GroupVersion")
|
||||||
|
@ -68,13 +79,16 @@ func TestRead_Success(t *testing.T) {
|
||||||
for desc, tc := range readTestCases() {
|
for desc, tc := range readTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
demo.Register(server.Registry)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
resource1 := &pbresource.Resource{Id: id1, Version: ""}
|
|
||||||
resource1, err := server.Backend.WriteCAS(tc.ctx, resource1)
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rsp, err := client.Read(tc.ctx, &pbresource.ReadRequest{Id: id1})
|
resource1, err := server.Backend.WriteCAS(tc.ctx, artist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp, err := client.Read(tc.ctx, &pbresource.ReadRequest{Id: artist.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prototest.AssertDeepEqual(t, resource1, rsp.Resource)
|
prototest.AssertDeepEqual(t, resource1, rsp.Resource)
|
||||||
})
|
})
|
||||||
|
@ -87,17 +101,20 @@ func TestRead_VerifyReadConsistencyArg(t *testing.T) {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
mockBackend := NewMockBackend(t)
|
mockBackend := NewMockBackend(t)
|
||||||
server := NewServer(Config{
|
server := NewServer(Config{
|
||||||
registry: resource.NewRegistry(),
|
Registry: resource.NewRegistry(),
|
||||||
Backend: mockBackend,
|
Backend: mockBackend,
|
||||||
})
|
})
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
demo.Register(server.Registry)
|
||||||
resource1 := &pbresource.Resource{Id: id1, Version: "1"}
|
|
||||||
mockBackend.On("Read", mock.Anything, mock.Anything, mock.Anything).Return(resource1, nil)
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mockBackend.On("Read", mock.Anything, mock.Anything, mock.Anything).Return(artist, nil)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
rsp, err := client.Read(tc.ctx, &pbresource.ReadRequest{Id: id1})
|
rsp, err := client.Read(tc.ctx, &pbresource.ReadRequest{Id: artist.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prototest.AssertDeepEqual(t, resource1, rsp.Resource)
|
prototest.AssertDeepEqual(t, artist, rsp.Resource)
|
||||||
mockBackend.AssertCalled(t, "Read", mock.Anything, tc.consistency, mock.Anything)
|
mockBackend.AssertCalled(t, "Read", mock.Anything, tc.consistency, mock.Anything)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ package resource
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
|
@ -21,7 +23,8 @@ type Server struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
registry Registry
|
Logger hclog.Logger
|
||||||
|
Registry Registry
|
||||||
|
|
||||||
// Backend is the storage backend that will be used for resource persistence.
|
// Backend is the storage backend that will be used for resource persistence.
|
||||||
Backend Backend
|
Backend Backend
|
||||||
|
@ -47,11 +50,6 @@ func (s *Server) Register(grpcServer *grpc.Server) {
|
||||||
pbresource.RegisterResourceServiceServer(grpcServer, s)
|
pbresource.RegisterResourceServiceServer(grpcServer, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbresource.WriteResponse, error) {
|
|
||||||
// TODO
|
|
||||||
return &pbresource.WriteResponse{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) WriteStatus(ctx context.Context, req *pbresource.WriteStatusRequest) (*pbresource.WriteStatusResponse, error) {
|
func (s *Server) WriteStatus(ctx context.Context, req *pbresource.WriteStatusRequest) (*pbresource.WriteStatusResponse, error) {
|
||||||
// TODO
|
// TODO
|
||||||
return &pbresource.WriteStatusResponse{}, nil
|
return &pbresource.WriteStatusResponse{}, nil
|
||||||
|
@ -64,7 +62,7 @@ func (s *Server) Delete(ctx context.Context, req *pbresource.DeleteRequest) (*pb
|
||||||
|
|
||||||
//nolint:unparam
|
//nolint:unparam
|
||||||
func (s *Server) resolveType(typ *pbresource.Type) (*resource.Registration, error) {
|
func (s *Server) resolveType(typ *pbresource.Type) (*resource.Registration, error) {
|
||||||
v, ok := s.registry.Resolve(typ)
|
v, ok := s.Registry.Resolve(typ)
|
||||||
if ok {
|
if ok {
|
||||||
return &v, nil
|
return &v, nil
|
||||||
}
|
}
|
||||||
|
@ -90,3 +88,5 @@ func readConsistencyFrom(ctx context.Context) storage.ReadConsistency {
|
||||||
}
|
}
|
||||||
return storage.EventualConsistency
|
return storage.EventualConsistency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clone[T proto.Message](v T) T { return proto.Clone(v).(T) }
|
||||||
|
|
|
@ -5,25 +5,21 @@ package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/storage/inmem"
|
"github.com/hashicorp/consul/internal/storage/inmem"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWrite_TODO(t *testing.T) {
|
|
||||||
server := testServer(t)
|
|
||||||
client := testClient(t, server)
|
|
||||||
resp, err := client.Write(context.Background(), &pbresource.WriteRequest{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriteStatus_TODO(t *testing.T) {
|
func TestWriteStatus_TODO(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
@ -47,8 +43,11 @@ func testServer(t *testing.T) *Server {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
go backend.Run(testContext(t))
|
go backend.Run(testContext(t))
|
||||||
|
|
||||||
registry := resource.NewRegistry()
|
return NewServer(Config{
|
||||||
return NewServer(Config{registry: registry, Backend: backend})
|
Logger: testutil.Logger(t),
|
||||||
|
Registry: resource.NewRegistry(),
|
||||||
|
Backend: backend,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClient(t *testing.T, server *Server) pbresource.ResourceServiceClient {
|
func testClient(t *testing.T, server *Server) pbresource.ResourceServiceClient {
|
||||||
|
@ -67,46 +66,24 @@ func testClient(t *testing.T, server *Server) pbresource.ResourceServiceClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testContext(t *testing.T) context.Context {
|
func testContext(t *testing.T) context.Context {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
t.Cleanup(cancel)
|
t.Cleanup(cancel)
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func modifyArtist(t *testing.T, res *pbresource.Resource) *pbresource.Resource {
|
||||||
tenancy = &pbresource.Tenancy{
|
t.Helper()
|
||||||
Partition: "default",
|
|
||||||
Namespace: "default",
|
var artist pbdemov2.Artist
|
||||||
PeerName: "local",
|
require.NoError(t, res.Data.UnmarshalTo(&artist))
|
||||||
}
|
artist.Name = fmt.Sprintf("The artist formerly known as %s", artist.Name)
|
||||||
typev1 = &pbresource.Type{
|
|
||||||
Group: "mesh",
|
data, err := anypb.New(&artist)
|
||||||
GroupVersion: "v1",
|
require.NoError(t, err)
|
||||||
Kind: "service",
|
|
||||||
}
|
res = clone(res)
|
||||||
typev2 = &pbresource.Type{
|
res.Data = data
|
||||||
Group: "mesh",
|
return res
|
||||||
GroupVersion: "v2",
|
}
|
||||||
Kind: "service",
|
|
||||||
}
|
|
||||||
id1 = &pbresource.ID{
|
|
||||||
Uid: "abcd",
|
|
||||||
Name: "billing",
|
|
||||||
Type: typev1,
|
|
||||||
Tenancy: tenancy,
|
|
||||||
}
|
|
||||||
id2 = &pbresource.ID{
|
|
||||||
Uid: "abcd",
|
|
||||||
Name: "billing",
|
|
||||||
Type: typev2,
|
|
||||||
Tenancy: tenancy,
|
|
||||||
}
|
|
||||||
resourcev1 = &pbresource.Resource{
|
|
||||||
Id: &pbresource.ID{
|
|
||||||
Uid: "someUid",
|
|
||||||
Name: "someName",
|
|
||||||
Type: typev1,
|
|
||||||
Tenancy: tenancy,
|
|
||||||
},
|
|
||||||
Version: "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -10,14 +10,13 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWatchList_TypeNotFound(t *testing.T) {
|
func TestWatchList_TypeNotFound(t *testing.T) {
|
||||||
|
@ -26,8 +25,8 @@ func TestWatchList_TypeNotFound(t *testing.T) {
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
stream, err := client.WatchList(context.Background(), &pbresource.WatchListRequest{
|
stream, err := client.WatchList(context.Background(), &pbresource.WatchListRequest{
|
||||||
Type: typev1,
|
Type: demo.TypeV2Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: demo.TenancyDefault,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -35,34 +34,37 @@ func TestWatchList_TypeNotFound(t *testing.T) {
|
||||||
|
|
||||||
err = mustGetError(t, rspCh)
|
err = mustGetError(t, rspCh)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
require.Contains(t, err.Error(), "resource type mesh/v1/service not registered")
|
require.Contains(t, err.Error(), "resource type demo/v2/artist not registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchList_GroupVersionMatches(t *testing.T) {
|
func TestWatchList_GroupVersionMatches(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
demo.Register(server.Registry)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// create a watch
|
// create a watch
|
||||||
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
||||||
Type: typev1,
|
Type: demo.TypeV2Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: demo.TenancyDefault,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rspCh := handleResourceStream(t, stream)
|
rspCh := handleResourceStream(t, stream)
|
||||||
|
|
||||||
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// insert and verify upsert event received
|
// insert and verify upsert event received
|
||||||
r1, err := server.Backend.WriteCAS(ctx, resourcev1)
|
r1, err := server.Backend.WriteCAS(ctx, artist)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rsp := mustGetResource(t, rspCh)
|
rsp := mustGetResource(t, rspCh)
|
||||||
require.Equal(t, pbresource.WatchEvent_OPERATION_UPSERT, rsp.Operation)
|
require.Equal(t, pbresource.WatchEvent_OPERATION_UPSERT, rsp.Operation)
|
||||||
prototest.AssertDeepEqual(t, r1, rsp.Resource)
|
prototest.AssertDeepEqual(t, r1, rsp.Resource)
|
||||||
|
|
||||||
// update and verify upsert event received
|
// update and verify upsert event received
|
||||||
r2 := clone(r1)
|
r2 := modifyArtist(t, r1)
|
||||||
r2, err = server.Backend.WriteCAS(ctx, r2)
|
r2, err = server.Backend.WriteCAS(ctx, r2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rsp = mustGetResource(t, rspCh)
|
rsp = mustGetResource(t, rspCh)
|
||||||
|
@ -77,27 +79,29 @@ func TestWatchList_GroupVersionMatches(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchList_GroupVersionMismatch(t *testing.T) {
|
func TestWatchList_GroupVersionMismatch(t *testing.T) {
|
||||||
// Given a watch on typev2 that only differs from typev1 by GroupVersion
|
// Given a watch on TypeArtistV1 that only differs from TypeArtistV2 by GroupVersion
|
||||||
// When a resource of typev1 is created/updated/deleted
|
// When a resource of TypeArtistV2 is created/updated/deleted
|
||||||
// Then no watch events should be emitted
|
// Then no watch events should be emitted
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
|
demo.Register(server.Registry)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server.registry.Register(resource.Registration{Type: typev1})
|
|
||||||
server.registry.Register(resource.Registration{Type: typev2})
|
|
||||||
|
|
||||||
// create a watch for typev2
|
// create a watch for TypeArtistV1
|
||||||
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
||||||
Type: typev2,
|
Type: demo.TypeV1Artist,
|
||||||
Tenancy: tenancy,
|
Tenancy: demo.TenancyDefault,
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rspCh := handleResourceStream(t, stream)
|
rspCh := handleResourceStream(t, stream)
|
||||||
|
|
||||||
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// insert
|
// insert
|
||||||
r1, err := server.Backend.WriteCAS(ctx, resourcev1)
|
r1, err := server.Backend.WriteCAS(ctx, artist)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// update
|
// update
|
||||||
|
@ -176,5 +180,3 @@ type resourceOrError struct {
|
||||||
rsp *pbresource.WatchEvent
|
rsp *pbresource.WatchEvent
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func clone[T proto.Message](v T) T { return proto.Clone(v).(T) }
|
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/oklog/ulid/v2"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
|
"github.com/hashicorp/consul/lib/retry"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbresource.WriteResponse, error) {
|
||||||
|
if err := validateWriteRequiredFields(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err := s.resolveType(req.Resource.Id.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the user sent the correct type of data.
|
||||||
|
if !req.Resource.Data.MessageIs(reg.Proto) {
|
||||||
|
got := strings.TrimPrefix(req.Resource.Data.TypeUrl, "type.googleapis.com/")
|
||||||
|
|
||||||
|
return nil, status.Errorf(
|
||||||
|
codes.InvalidArgument,
|
||||||
|
"resource.data is of wrong type (expected=%q, got=%q)",
|
||||||
|
reg.Proto.ProtoReflect().Descriptor().FullName(),
|
||||||
|
got,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// At the storage backend layer, all writes are CAS operations.
|
||||||
|
//
|
||||||
|
// This makes it possible to *safely* do things like keeping the Uid stable
|
||||||
|
// across writes, carrying statuses over, and passing the current version of
|
||||||
|
// the resource to hooks, without restricting ourselves to only using the more
|
||||||
|
// feature-rich storage systems that support "patch" updates etc. natively.
|
||||||
|
//
|
||||||
|
// Although CAS semantics are useful for machine users like controllers, human
|
||||||
|
// users generally don't need them. If the user is performing a non-CAS write,
|
||||||
|
// we read the current version, and automatically retry if the CAS write fails.
|
||||||
|
var result *pbresource.Resource
|
||||||
|
err = s.retryCAS(ctx, req.Resource.Version, func() error {
|
||||||
|
input := clone(req.Resource)
|
||||||
|
|
||||||
|
// We read with EventualConsistency here because:
|
||||||
|
//
|
||||||
|
// - In the common case, individual resources are written infrequently, and
|
||||||
|
// when using the Raft backend followers are generally within a few hundred
|
||||||
|
// milliseconds of the leader, so the first read will probably return the
|
||||||
|
// current version.
|
||||||
|
//
|
||||||
|
// - StrongConsistency is expensive. In the Raft backend, it involves a round
|
||||||
|
// of heartbeats to verify cluster leadership (in addition to the write's
|
||||||
|
// log replication).
|
||||||
|
//
|
||||||
|
// - CAS failures will be retried by retryCAS anyway. So the read-modify-write
|
||||||
|
// cycle should eventually succeed.
|
||||||
|
existing, err := s.Backend.Read(ctx, storage.EventualConsistency, input.Id)
|
||||||
|
switch {
|
||||||
|
// Create path.
|
||||||
|
case errors.Is(err, storage.ErrNotFound):
|
||||||
|
input.Id.Uid = ulid.Make().String()
|
||||||
|
|
||||||
|
// TODO: Prevent setting statuses in this endpoint.
|
||||||
|
|
||||||
|
// Update path.
|
||||||
|
case err == nil:
|
||||||
|
// Use the stored ID because it includes the Uid.
|
||||||
|
//
|
||||||
|
// Generally, users won't provide the Uid but controllers will, because
|
||||||
|
// controllers need to operate on a specific "incarnation" of a resource
|
||||||
|
// as opposed to an older/newer resource with the same name, whereas users
|
||||||
|
// just want to update the current resource.
|
||||||
|
input.Id = existing.Id
|
||||||
|
|
||||||
|
// User is doing a non-CAS write, use the current version.
|
||||||
|
if input.Version == "" {
|
||||||
|
input.Version = existing.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the stored version matches the user-given version.
|
||||||
|
//
|
||||||
|
// Although CAS operations are implemented "for real" at the storage backend
|
||||||
|
// layer, we must check the version here too to prevent a scenario where:
|
||||||
|
//
|
||||||
|
// - Current resource version is `v2`
|
||||||
|
// - User passes version `v2`
|
||||||
|
// - Read returns stale version `v1`
|
||||||
|
// - We carry `v1`'s statuses over (effectively overwriting `v2`'s statuses)
|
||||||
|
// - CAS operation succeeds anyway because user-given version is current
|
||||||
|
//
|
||||||
|
// TODO(boxofrad): add a test for this once the status field has been added.
|
||||||
|
if input.Version != existing.Version {
|
||||||
|
return storage.ErrCASFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Carry over the statuses here.
|
||||||
|
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Generation = ulid.Make().String()
|
||||||
|
result, err = s.Backend.WriteCAS(ctx, input)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, storage.ErrCASFailure):
|
||||||
|
return nil, status.Error(codes.Aborted, err.Error())
|
||||||
|
case errors.Is(err, storage.ErrWrongUid):
|
||||||
|
return nil, status.Error(codes.FailedPrecondition, err.Error())
|
||||||
|
case err != nil:
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to write resource: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pbresource.WriteResponse{Resource: result}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryCAS retries the given operation with exponential backoff if the user
|
||||||
|
// didn't provide a version. This is intended to hide failures when the user
|
||||||
|
// isn't intentionally performing a CAS operation (all writes are, by design,
|
||||||
|
// CAS operations at the storage backend layer).
|
||||||
|
func (s *Server) retryCAS(ctx context.Context, vsn string, cas func() error) error {
|
||||||
|
if vsn != "" {
|
||||||
|
return cas()
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxAttempts = 5
|
||||||
|
|
||||||
|
// These parameters are fairly arbitrary, so if you find better ones then go
|
||||||
|
// ahead and swap them out! In general, we want to wait long enough to smooth
|
||||||
|
// over small amounts of storage replication lag, but not so long that we make
|
||||||
|
// matters worse by holding onto load.
|
||||||
|
backoff := &retry.Waiter{
|
||||||
|
MinWait: 50 * time.Millisecond,
|
||||||
|
MaxWait: 1 * time.Second,
|
||||||
|
Jitter: retry.NewJitter(50),
|
||||||
|
Factor: 75 * time.Millisecond,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for i := 1; i <= maxAttempts; i++ {
|
||||||
|
if err = cas(); !errors.Is(err, storage.ErrCASFailure) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if backoff.Wait(ctx) != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.Logger.Trace("retrying failed CAS operation", "failure_count", i)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateWriteRequiredFields(req *pbresource.WriteRequest) error {
|
||||||
|
var field string
|
||||||
|
switch {
|
||||||
|
case req.Resource == nil:
|
||||||
|
field = "resource"
|
||||||
|
case req.Resource.Id == nil:
|
||||||
|
field = "resource.id"
|
||||||
|
case req.Resource.Id.Type == nil:
|
||||||
|
field = "resource.id.type"
|
||||||
|
case req.Resource.Id.Tenancy == nil:
|
||||||
|
field = "resource.id.tenancy"
|
||||||
|
case req.Resource.Id.Name == "":
|
||||||
|
field = "resource.id.name"
|
||||||
|
case req.Resource.Data == nil:
|
||||||
|
field = "resource.data"
|
||||||
|
}
|
||||||
|
|
||||||
|
if field == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return status.Errorf(codes.InvalidArgument, "%s is required", field)
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWrite_InputValidation(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
testCases := map[string]func(*pbresource.WriteRequest){
|
||||||
|
"no resource": func(req *pbresource.WriteRequest) { req.Resource = nil },
|
||||||
|
"no id": func(req *pbresource.WriteRequest) { req.Resource.Id = nil },
|
||||||
|
"no type": func(req *pbresource.WriteRequest) { req.Resource.Id.Type = nil },
|
||||||
|
"no tenancy": func(req *pbresource.WriteRequest) { req.Resource.Id.Tenancy = nil },
|
||||||
|
"no name": func(req *pbresource.WriteRequest) { req.Resource.Id.Name = "" },
|
||||||
|
"no data": func(req *pbresource.WriteRequest) { req.Resource.Data = nil },
|
||||||
|
"wrong data type": func(req *pbresource.WriteRequest) {
|
||||||
|
var err error
|
||||||
|
req.Resource.Data, err = anypb.New(&pbdemov2.Album{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for desc, modFn := range testCases {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req := &pbresource.WriteRequest{Resource: res}
|
||||||
|
modFn(req)
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), req)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_TypeNotFound(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
|
require.Contains(t, err.Error(), "resource type demo/v2/artist not registered")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_ResourceCreation(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, rsp.Resource.Version, "resource should have version")
|
||||||
|
require.NotEmpty(t, rsp.Resource.Id.Uid, "resource id should have uid")
|
||||||
|
require.NotEmpty(t, rsp.Resource.Generation, "resource should have generation")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_CASUpdate_Success(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp2, err := client.Write(testContext(t), &pbresource.WriteRequest{
|
||||||
|
Resource: modifyArtist(t, rsp1.Resource),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, rsp1.Resource.Id.Uid, rsp2.Resource.Id.Uid)
|
||||||
|
require.NotEqual(t, rsp1.Resource.Version, rsp2.Resource.Version)
|
||||||
|
require.NotEqual(t, rsp1.Resource.Generation, rsp2.Resource.Generation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_CASUpdate_Failure(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res = modifyArtist(t, rsp1.Resource)
|
||||||
|
res.Version = "wrong-version"
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, codes.Aborted.String(), status.Code(err).String())
|
||||||
|
require.Contains(t, err.Error(), "CAS operation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_Update_WrongUid(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res = modifyArtist(t, rsp1.Resource)
|
||||||
|
res.Id.Uid = "wrong-uid"
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, codes.FailedPrecondition.String(), status.Code(err).String())
|
||||||
|
require.Contains(t, err.Error(), "uid doesn't match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_Update_NoUid(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res = modifyArtist(t, rsp1.Resource)
|
||||||
|
res.Id.Uid = ""
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_NonCASUpdate_Success(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res = modifyArtist(t, rsp1.Resource)
|
||||||
|
res.Version = ""
|
||||||
|
|
||||||
|
rsp2, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, rsp2.Resource.Version)
|
||||||
|
require.NotEqual(t, rsp1.Resource.Version, rsp2.Resource.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrite_NonCASUpdate_Retry(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.Register(server.Registry)
|
||||||
|
|
||||||
|
res, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Simulate conflicting writes by blocking the RPC after it has read the
|
||||||
|
// current version of the resource, but before it tries to make a write.
|
||||||
|
backend := &blockOnceBackend{
|
||||||
|
Backend: server.Backend,
|
||||||
|
|
||||||
|
readCh: make(chan struct{}),
|
||||||
|
blockCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
server.Backend = backend
|
||||||
|
|
||||||
|
errCh := make(chan error)
|
||||||
|
go func() {
|
||||||
|
res := modifyArtist(t, rsp1.Resource)
|
||||||
|
res.Version = ""
|
||||||
|
|
||||||
|
_, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res})
|
||||||
|
errCh <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the read, to ensure the Write in the goroutine above has read the
|
||||||
|
// current version of the resource.
|
||||||
|
<-backend.readCh
|
||||||
|
|
||||||
|
// Update the resource.
|
||||||
|
res = modifyArtist(t, rsp1.Resource)
|
||||||
|
_, err = backend.WriteCAS(testContext(t), res)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Unblock the read.
|
||||||
|
close(backend.blockCh)
|
||||||
|
|
||||||
|
// Check that the write succeeded anyway because of a retry.
|
||||||
|
require.NoError(t, <-errCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
type blockOnceBackend struct {
|
||||||
|
storage.Backend
|
||||||
|
|
||||||
|
once sync.Once
|
||||||
|
readCh chan struct{}
|
||||||
|
blockCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blockOnceBackend) Read(ctx context.Context, consistency storage.ReadConsistency, id *pbresource.ID) (*pbresource.Resource, error) {
|
||||||
|
res, err := b.Backend.Read(ctx, consistency, id)
|
||||||
|
|
||||||
|
b.once.Do(func() {
|
||||||
|
close(b.readCh)
|
||||||
|
<-b.blockCh
|
||||||
|
})
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -85,6 +85,7 @@ require (
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/mitchellh/pointerstructure v1.2.1
|
github.com/mitchellh/pointerstructure v1.2.1
|
||||||
github.com/mitchellh/reflectwalk v1.0.2
|
github.com/mitchellh/reflectwalk v1.0.2
|
||||||
|
github.com/oklog/ulid/v2 v2.1.0
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -821,6 +821,8 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||||
|
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
|
@ -850,6 +852,7 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
|
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
// Package demo includes fake resource types for working on Consul's generic
|
||||||
|
// state storage without having to refer to specific features.
|
||||||
|
package demo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
pbdemov1 "github.com/hashicorp/consul/proto/private/pbdemo/v1"
|
||||||
|
pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// TenancyDefault contains the default values for all tenancy units.
|
||||||
|
TenancyDefault = &pbresource.Tenancy{
|
||||||
|
Partition: "default",
|
||||||
|
PeerName: "local",
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeV1Artist represents a musician or group of musicians.
|
||||||
|
TypeV1Artist = &pbresource.Type{
|
||||||
|
Group: "demo",
|
||||||
|
GroupVersion: "v1",
|
||||||
|
Kind: "artist",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeV1Album represents a collection of an artist's songs.
|
||||||
|
TypeV1Album = &pbresource.Type{
|
||||||
|
Group: "demo",
|
||||||
|
GroupVersion: "v1",
|
||||||
|
Kind: "album",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeV2Artist represents a musician or group of musicians.
|
||||||
|
TypeV2Artist = &pbresource.Type{
|
||||||
|
Group: "demo",
|
||||||
|
GroupVersion: "v2",
|
||||||
|
Kind: "artist",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeV2Album represents a collection of an artist's songs.
|
||||||
|
TypeV2Album = &pbresource.Type{
|
||||||
|
Group: "demo",
|
||||||
|
GroupVersion: "v2",
|
||||||
|
Kind: "album",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register demo types. Should only be called in tests and dev mode.
|
||||||
|
func Register(r resource.Registry) {
|
||||||
|
r.Register(resource.Registration{
|
||||||
|
Type: TypeV1Artist,
|
||||||
|
Proto: &pbdemov1.Artist{},
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(resource.Registration{
|
||||||
|
Type: TypeV1Album,
|
||||||
|
Proto: &pbdemov1.Album{},
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(resource.Registration{
|
||||||
|
Type: TypeV2Artist,
|
||||||
|
Proto: &pbdemov2.Artist{},
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Register(resource.Registration{
|
||||||
|
Type: TypeV2Album,
|
||||||
|
Proto: &pbdemov2.Album{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateV2Artist generates a random Artist resource.
|
||||||
|
func GenerateV2Artist() (*pbresource.Resource, error) {
|
||||||
|
adjective := adjectives[rand.Intn(len(adjectives))]
|
||||||
|
noun := nouns[rand.Intn(len(nouns))]
|
||||||
|
|
||||||
|
numMembers := rand.Intn(5) + 1
|
||||||
|
groupMembers := make(map[string]string, numMembers)
|
||||||
|
for i := 0; i < numMembers; i++ {
|
||||||
|
groupMembers[members[rand.Intn(len(members))]] = instruments[rand.Intn(len(instruments))]
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := anypb.New(&pbdemov2.Artist{
|
||||||
|
Name: fmt.Sprintf("%s %s", adjective, noun),
|
||||||
|
Genre: randomGenre(),
|
||||||
|
GroupMembers: groupMembers,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pbresource.Resource{
|
||||||
|
Id: &pbresource.ID{
|
||||||
|
Type: TypeV2Artist,
|
||||||
|
Tenancy: TenancyDefault,
|
||||||
|
Name: fmt.Sprintf("%s-%s", strings.ToLower(adjective), strings.ToLower(noun)),
|
||||||
|
},
|
||||||
|
Data: data,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"generated_at": time.Now().Format(time.RFC3339),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateV2Album generates a random Album resource, owned by the Artist with
|
||||||
|
// the given ID.
|
||||||
|
func GenerateV2Album(artistID *pbresource.ID) (*pbresource.Resource, error) {
|
||||||
|
adjective := adjectives[rand.Intn(len(adjectives))]
|
||||||
|
noun := nouns[rand.Intn(len(nouns))]
|
||||||
|
|
||||||
|
numTracks := 3 + rand.Intn(3)
|
||||||
|
tracks := make([]string, numTracks)
|
||||||
|
for i := 0; i < numTracks; i++ {
|
||||||
|
words := nouns
|
||||||
|
if i%3 == 0 {
|
||||||
|
words = adjectives
|
||||||
|
}
|
||||||
|
tracks[i] = words[rand.Intn(len(words))]
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := anypb.New(&pbdemov2.Album{
|
||||||
|
Title: fmt.Sprintf("%s %s", adjective, noun),
|
||||||
|
YearOfRelease: int32(1990 + rand.Intn(time.Now().Year()-1990)),
|
||||||
|
CriticallyAclaimed: rand.Int()%2 == 0,
|
||||||
|
Tracks: tracks,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pbresource.Resource{
|
||||||
|
Id: &pbresource.ID{
|
||||||
|
Type: TypeV2Album,
|
||||||
|
Tenancy: artistID.Tenancy,
|
||||||
|
Name: fmt.Sprintf("%s/%s-%s", artistID.Name, strings.ToLower(adjective), strings.ToLower(noun)),
|
||||||
|
},
|
||||||
|
Owner: artistID,
|
||||||
|
Data: data,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"generated_at": time.Now().Format(time.RFC3339),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomGenre() pbdemov2.Genre {
|
||||||
|
return pbdemov2.Genre(rand.Intn(len(pbdemov1.Genre_name)-1) + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
adjectives = []string{
|
||||||
|
"Purple",
|
||||||
|
"Angry",
|
||||||
|
"Euphoric",
|
||||||
|
"Unexpected",
|
||||||
|
"Cheesy",
|
||||||
|
"Rancid",
|
||||||
|
"Pleasant",
|
||||||
|
"Mumbling",
|
||||||
|
"Enlightened",
|
||||||
|
}
|
||||||
|
|
||||||
|
nouns = []string{
|
||||||
|
"Speakerphone",
|
||||||
|
"Fox",
|
||||||
|
"Guppy",
|
||||||
|
"Smile",
|
||||||
|
"Emacs",
|
||||||
|
"Grapefruit",
|
||||||
|
"Engineer",
|
||||||
|
"Basketball",
|
||||||
|
}
|
||||||
|
|
||||||
|
members = []string{
|
||||||
|
"Owl",
|
||||||
|
"Tiger",
|
||||||
|
"Beetle",
|
||||||
|
"Lion",
|
||||||
|
"Chicken",
|
||||||
|
"Snake",
|
||||||
|
"Monkey",
|
||||||
|
"Kitten",
|
||||||
|
"Hound",
|
||||||
|
}
|
||||||
|
|
||||||
|
instruments = []string{
|
||||||
|
"Guitar",
|
||||||
|
"Bass",
|
||||||
|
"Lead Vocals",
|
||||||
|
"Backing Vocals",
|
||||||
|
"Drums",
|
||||||
|
"Synthesizer",
|
||||||
|
"Triangle",
|
||||||
|
"Standing by the stage looking cool",
|
||||||
|
}
|
||||||
|
)
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,6 +24,9 @@ type Registration struct {
|
||||||
// Type is the GVK of the resource type.
|
// Type is the GVK of the resource type.
|
||||||
Type *pbresource.Type
|
Type *pbresource.Type
|
||||||
|
|
||||||
|
// Proto is the resource's protobuf message type.
|
||||||
|
Proto proto.Message
|
||||||
|
|
||||||
// In the future, we'll add hooks, the controller etc. here.
|
// In the future, we'll add hooks, the controller etc. here.
|
||||||
// TODO: https://github.com/hashicorp/consul/pull/16622#discussion_r1134515909
|
// TODO: https://github.com/hashicorp/consul/pull/16622#discussion_r1134515909
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,15 +120,15 @@ func (s *Store) WriteCAS(res *pbresource.Resource, vsn string) error {
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
existingRes := existing.(*pbresource.Resource)
|
existingRes := existing.(*pbresource.Resource)
|
||||||
|
|
||||||
// Ensure CAS semantics.
|
|
||||||
if existingRes.Version != vsn {
|
|
||||||
return storage.ErrCASFailure
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uid is immutable.
|
// Uid is immutable.
|
||||||
if existingRes.Id.Uid != res.Id.Uid {
|
if existingRes.Id.Uid != res.Id.Uid {
|
||||||
return storage.ErrWrongUid
|
return storage.ErrWrongUid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure CAS semantics.
|
||||||
|
if existingRes.Version != vsn {
|
||||||
|
return storage.ErrCASFailure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Insert(tableNameResources, res); err != nil {
|
if err := tx.Insert(tableNameResources, res); err != nil {
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by protoc-gen-go-binary. DO NOT EDIT.
|
||||||
|
// source: private/pbdemo/v1/demo.proto
|
||||||
|
|
||||||
|
package demov1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *Artist) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *Artist) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *Album) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *Album) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
|
@ -0,0 +1,387 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.28.1
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: private/pbdemo/v1/demo.proto
|
||||||
|
|
||||||
|
// This package contains fake resource types, which are useful for working on
|
||||||
|
// Consul's generic storage APIs.
|
||||||
|
|
||||||
|
package demov1
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Genre int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Genre_GENRE_UNSPECIFIED Genre = 0
|
||||||
|
Genre_GENRE_JAZZ Genre = 1
|
||||||
|
Genre_GENRE_FOLK Genre = 2
|
||||||
|
Genre_GENRE_POP Genre = 3
|
||||||
|
Genre_GENRE_METAL Genre = 4
|
||||||
|
Genre_GENRE_PUNK Genre = 5
|
||||||
|
Genre_GENRE_BLUES Genre = 6
|
||||||
|
Genre_GENRE_R_AND_B Genre = 7
|
||||||
|
Genre_GENRE_COUNTRY Genre = 8
|
||||||
|
Genre_GENRE_DISCO Genre = 9
|
||||||
|
Genre_GENRE_SKA Genre = 10
|
||||||
|
Genre_GENRE_HIP_HOP Genre = 11
|
||||||
|
Genre_GENRE_INDIE Genre = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for Genre.
|
||||||
|
var (
|
||||||
|
Genre_name = map[int32]string{
|
||||||
|
0: "GENRE_UNSPECIFIED",
|
||||||
|
1: "GENRE_JAZZ",
|
||||||
|
2: "GENRE_FOLK",
|
||||||
|
3: "GENRE_POP",
|
||||||
|
4: "GENRE_METAL",
|
||||||
|
5: "GENRE_PUNK",
|
||||||
|
6: "GENRE_BLUES",
|
||||||
|
7: "GENRE_R_AND_B",
|
||||||
|
8: "GENRE_COUNTRY",
|
||||||
|
9: "GENRE_DISCO",
|
||||||
|
10: "GENRE_SKA",
|
||||||
|
11: "GENRE_HIP_HOP",
|
||||||
|
12: "GENRE_INDIE",
|
||||||
|
}
|
||||||
|
Genre_value = map[string]int32{
|
||||||
|
"GENRE_UNSPECIFIED": 0,
|
||||||
|
"GENRE_JAZZ": 1,
|
||||||
|
"GENRE_FOLK": 2,
|
||||||
|
"GENRE_POP": 3,
|
||||||
|
"GENRE_METAL": 4,
|
||||||
|
"GENRE_PUNK": 5,
|
||||||
|
"GENRE_BLUES": 6,
|
||||||
|
"GENRE_R_AND_B": 7,
|
||||||
|
"GENRE_COUNTRY": 8,
|
||||||
|
"GENRE_DISCO": 9,
|
||||||
|
"GENRE_SKA": 10,
|
||||||
|
"GENRE_HIP_HOP": 11,
|
||||||
|
"GENRE_INDIE": 12,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x Genre) Enum() *Genre {
|
||||||
|
p := new(Genre)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Genre) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Genre) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_private_pbdemo_v1_demo_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Genre) Type() protoreflect.EnumType {
|
||||||
|
return &file_private_pbdemo_v1_demo_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Genre) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Genre.Descriptor instead.
|
||||||
|
func (Genre) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Artist struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
|
||||||
|
Genre Genre `protobuf:"varint,3,opt,name=genre,proto3,enum=hashicorp.consul.internal.demo.v1.Genre" json:"genre,omitempty"`
|
||||||
|
GroupMembers int32 `protobuf:"varint,4,opt,name=group_members,json=groupMembers,proto3" json:"group_members,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) Reset() {
|
||||||
|
*x = Artist{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_private_pbdemo_v1_demo_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Artist) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Artist) 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 Artist.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Artist) Descriptor() ([]byte, []int) {
|
||||||
|
return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetDescription() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Description
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetGenre() Genre {
|
||||||
|
if x != nil {
|
||||||
|
return x.Genre
|
||||||
|
}
|
||||||
|
return Genre_GENRE_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetGroupMembers() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.GroupMembers
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Album struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
YearOfRelease int32 `protobuf:"varint,2,opt,name=year_of_release,json=yearOfRelease,proto3" json:"year_of_release,omitempty"`
|
||||||
|
CriticallyAclaimed bool `protobuf:"varint,3,opt,name=critically_aclaimed,json=criticallyAclaimed,proto3" json:"critically_aclaimed,omitempty"`
|
||||||
|
Tracks []string `protobuf:"bytes,4,rep,name=tracks,proto3" json:"tracks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) Reset() {
|
||||||
|
*x = Album{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_private_pbdemo_v1_demo_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Album) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Album) ProtoReflect() protoreflect.Message {
|
||||||
|
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 {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Album.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Album) Descriptor() ([]byte, []int) {
|
||||||
|
return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetYearOfRelease() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.YearOfRelease
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetCriticallyAclaimed() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.CriticallyAclaimed
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetTracks() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Tracks
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_private_pbdemo_v1_demo_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_private_pbdemo_v1_demo_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1c, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f,
|
||||||
|
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, 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, 0x8c, 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, 0x2f, 0x0a,
|
||||||
|
0x13, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x61, 0x63, 0x6c, 0x61,
|
||||||
|
0x69, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x72, 0x69, 0x74,
|
||||||
|
0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 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, 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 (
|
||||||
|
file_private_pbdemo_v1_demo_proto_rawDescOnce sync.Once
|
||||||
|
file_private_pbdemo_v1_demo_proto_rawDescData = file_private_pbdemo_v1_demo_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_private_pbdemo_v1_demo_proto_rawDescGZIP() []byte {
|
||||||
|
file_private_pbdemo_v1_demo_proto_rawDescOnce.Do(func() {
|
||||||
|
file_private_pbdemo_v1_demo_proto_rawDescData = protoimpl.X.CompressGZIP(file_private_pbdemo_v1_demo_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_private_pbdemo_v1_demo_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_private_pbdemo_v1_demo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_private_pbdemo_v1_demo_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_private_pbdemo_v1_demo_proto_goTypes = []interface{}{
|
||||||
|
(Genre)(0), // 0: hashicorp.consul.internal.demo.v1.Genre
|
||||||
|
(*Artist)(nil), // 1: hashicorp.consul.internal.demo.v1.Artist
|
||||||
|
(*Album)(nil), // 2: hashicorp.consul.internal.demo.v1.Album
|
||||||
|
}
|
||||||
|
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
|
||||||
|
1, // [1:1] is the sub-list for method output_type
|
||||||
|
1, // [1:1] is the sub-list for method input_type
|
||||||
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_private_pbdemo_v1_demo_proto_init() }
|
||||||
|
func file_private_pbdemo_v1_demo_proto_init() {
|
||||||
|
if File_private_pbdemo_v1_demo_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_private_pbdemo_v1_demo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Artist); 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[1].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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_private_pbdemo_v1_demo_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_private_pbdemo_v1_demo_proto_goTypes,
|
||||||
|
DependencyIndexes: file_private_pbdemo_v1_demo_proto_depIdxs,
|
||||||
|
EnumInfos: file_private_pbdemo_v1_demo_proto_enumTypes,
|
||||||
|
MessageInfos: file_private_pbdemo_v1_demo_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_private_pbdemo_v1_demo_proto = out.File
|
||||||
|
file_private_pbdemo_v1_demo_proto_rawDesc = nil
|
||||||
|
file_private_pbdemo_v1_demo_proto_goTypes = nil
|
||||||
|
file_private_pbdemo_v1_demo_proto_depIdxs = nil
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
// This package contains fake resource types, which are useful for working on
|
||||||
|
// Consul's generic storage APIs.
|
||||||
|
package hashicorp.consul.internal.demo.v1;
|
||||||
|
|
||||||
|
message Artist {
|
||||||
|
string name = 1;
|
||||||
|
string description = 2;
|
||||||
|
Genre genre = 3;
|
||||||
|
int32 group_members = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Genre {
|
||||||
|
GENRE_UNSPECIFIED = 0;
|
||||||
|
GENRE_JAZZ = 1;
|
||||||
|
GENRE_FOLK = 2;
|
||||||
|
GENRE_POP = 3;
|
||||||
|
GENRE_METAL = 4;
|
||||||
|
GENRE_PUNK = 5;
|
||||||
|
GENRE_BLUES = 6;
|
||||||
|
GENRE_R_AND_B = 7;
|
||||||
|
GENRE_COUNTRY = 8;
|
||||||
|
GENRE_DISCO = 9;
|
||||||
|
GENRE_SKA = 10;
|
||||||
|
GENRE_HIP_HOP = 11;
|
||||||
|
GENRE_INDIE = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Album {
|
||||||
|
string name = 1;
|
||||||
|
int32 year_of_release = 2;
|
||||||
|
bool critically_aclaimed = 3;
|
||||||
|
repeated string tracks = 4;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by protoc-gen-go-binary. DO NOT EDIT.
|
||||||
|
// source: private/pbdemo/v2/demo.proto
|
||||||
|
|
||||||
|
package demov2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *Artist) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *Artist) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *Album) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *Album) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
|
@ -0,0 +1,387 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.28.1
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: private/pbdemo/v2/demo.proto
|
||||||
|
|
||||||
|
// This package contains fake resource types, which are useful for working on
|
||||||
|
// Consul's generic storage APIs.
|
||||||
|
|
||||||
|
package demov2
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Genre int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Genre_GENRE_UNSPECIFIED Genre = 0
|
||||||
|
Genre_GENRE_JAZZ Genre = 1
|
||||||
|
Genre_GENRE_FOLK Genre = 2
|
||||||
|
Genre_GENRE_POP Genre = 3
|
||||||
|
Genre_GENRE_METAL Genre = 4
|
||||||
|
Genre_GENRE_PUNK Genre = 5
|
||||||
|
Genre_GENRE_BLUES Genre = 6
|
||||||
|
Genre_GENRE_R_AND_B Genre = 7
|
||||||
|
Genre_GENRE_COUNTRY Genre = 8
|
||||||
|
Genre_GENRE_DISCO Genre = 9
|
||||||
|
Genre_GENRE_SKA Genre = 10
|
||||||
|
Genre_GENRE_HIP_HOP Genre = 11
|
||||||
|
Genre_GENRE_INDIE Genre = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for Genre.
|
||||||
|
var (
|
||||||
|
Genre_name = map[int32]string{
|
||||||
|
0: "GENRE_UNSPECIFIED",
|
||||||
|
1: "GENRE_JAZZ",
|
||||||
|
2: "GENRE_FOLK",
|
||||||
|
3: "GENRE_POP",
|
||||||
|
4: "GENRE_METAL",
|
||||||
|
5: "GENRE_PUNK",
|
||||||
|
6: "GENRE_BLUES",
|
||||||
|
7: "GENRE_R_AND_B",
|
||||||
|
8: "GENRE_COUNTRY",
|
||||||
|
9: "GENRE_DISCO",
|
||||||
|
10: "GENRE_SKA",
|
||||||
|
11: "GENRE_HIP_HOP",
|
||||||
|
12: "GENRE_INDIE",
|
||||||
|
}
|
||||||
|
Genre_value = map[string]int32{
|
||||||
|
"GENRE_UNSPECIFIED": 0,
|
||||||
|
"GENRE_JAZZ": 1,
|
||||||
|
"GENRE_FOLK": 2,
|
||||||
|
"GENRE_POP": 3,
|
||||||
|
"GENRE_METAL": 4,
|
||||||
|
"GENRE_PUNK": 5,
|
||||||
|
"GENRE_BLUES": 6,
|
||||||
|
"GENRE_R_AND_B": 7,
|
||||||
|
"GENRE_COUNTRY": 8,
|
||||||
|
"GENRE_DISCO": 9,
|
||||||
|
"GENRE_SKA": 10,
|
||||||
|
"GENRE_HIP_HOP": 11,
|
||||||
|
"GENRE_INDIE": 12,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x Genre) Enum() *Genre {
|
||||||
|
p := new(Genre)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Genre) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Genre) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_private_pbdemo_v2_demo_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Genre) Type() protoreflect.EnumType {
|
||||||
|
return &file_private_pbdemo_v2_demo_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Genre) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Genre.Descriptor instead.
|
||||||
|
func (Genre) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_private_pbdemo_v2_demo_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Artist struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Genre Genre `protobuf:"varint,2,opt,name=genre,proto3,enum=hashicorp.consul.internal.demo.v2.Genre" json:"genre,omitempty"`
|
||||||
|
GroupMembers map[string]string `protobuf:"bytes,3,rep,name=group_members,json=groupMembers,proto3" json:"group_members,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) Reset() {
|
||||||
|
*x = Artist{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_private_pbdemo_v2_demo_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Artist) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Artist) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_private_pbdemo_v2_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 Artist.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Artist) Descriptor() ([]byte, []int) {
|
||||||
|
return file_private_pbdemo_v2_demo_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetGenre() Genre {
|
||||||
|
if x != nil {
|
||||||
|
return x.Genre
|
||||||
|
}
|
||||||
|
return Genre_GENRE_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Artist) GetGroupMembers() map[string]string {
|
||||||
|
if x != nil {
|
||||||
|
return x.GroupMembers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Album struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
|
YearOfRelease int32 `protobuf:"varint,2,opt,name=year_of_release,json=yearOfRelease,proto3" json:"year_of_release,omitempty"`
|
||||||
|
CriticallyAclaimed bool `protobuf:"varint,3,opt,name=critically_aclaimed,json=criticallyAclaimed,proto3" json:"critically_aclaimed,omitempty"`
|
||||||
|
Tracks []string `protobuf:"bytes,4,rep,name=tracks,proto3" json:"tracks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) Reset() {
|
||||||
|
*x = Album{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_private_pbdemo_v2_demo_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Album) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Album) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_private_pbdemo_v2_demo_proto_msgTypes[1]
|
||||||
|
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 Album.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Album) Descriptor() ([]byte, []int) {
|
||||||
|
return file_private_pbdemo_v2_demo_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetTitle() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Title
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetYearOfRelease() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.YearOfRelease
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetCriticallyAclaimed() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.CriticallyAclaimed
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Album) GetTracks() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Tracks
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_private_pbdemo_v2_demo_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_private_pbdemo_v2_demo_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1c, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f,
|
||||||
|
0x2f, 0x76, 0x32, 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,
|
||||||
|
0x32, 0x22, 0xff, 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, 0x3e, 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x18, 0x02, 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, 0x32, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65,
|
||||||
|
0x12, 0x60, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72,
|
||||||
|
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 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, 0x32, 0x2e, 0x41, 0x72, 0x74, 0x69,
|
||||||
|
0x73, 0x74, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45,
|
||||||
|
0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65,
|
||||||
|
0x72, 0x73, 0x1a, 0x3f, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65,
|
||||||
|
0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||||
|
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||||
|
0x02, 0x38, 0x01, 0x22, 0x8e, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x62, 0x75, 0x6d, 0x12, 0x14, 0x0a,
|
||||||
|
0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69,
|
||||||
|
0x74, 0x6c, 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, 0x2f, 0x0a, 0x13, 0x63,
|
||||||
|
0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x61, 0x63, 0x6c, 0x61, 0x69, 0x6d,
|
||||||
|
0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63,
|
||||||
|
0x61, 0x6c, 0x6c, 0x79, 0x41, 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, 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, 0x32, 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, 0x32, 0x3b, 0x64, 0x65, 0x6d,
|
||||||
|
0x6f, 0x76, 0x32, 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, 0x32, 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, 0x32, 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, 0x32, 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, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_private_pbdemo_v2_demo_proto_rawDescOnce sync.Once
|
||||||
|
file_private_pbdemo_v2_demo_proto_rawDescData = file_private_pbdemo_v2_demo_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_private_pbdemo_v2_demo_proto_rawDescGZIP() []byte {
|
||||||
|
file_private_pbdemo_v2_demo_proto_rawDescOnce.Do(func() {
|
||||||
|
file_private_pbdemo_v2_demo_proto_rawDescData = protoimpl.X.CompressGZIP(file_private_pbdemo_v2_demo_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_private_pbdemo_v2_demo_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_private_pbdemo_v2_demo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_private_pbdemo_v2_demo_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
|
var file_private_pbdemo_v2_demo_proto_goTypes = []interface{}{
|
||||||
|
(Genre)(0), // 0: hashicorp.consul.internal.demo.v2.Genre
|
||||||
|
(*Artist)(nil), // 1: hashicorp.consul.internal.demo.v2.Artist
|
||||||
|
(*Album)(nil), // 2: hashicorp.consul.internal.demo.v2.Album
|
||||||
|
nil, // 3: hashicorp.consul.internal.demo.v2.Artist.GroupMembersEntry
|
||||||
|
}
|
||||||
|
var file_private_pbdemo_v2_demo_proto_depIdxs = []int32{
|
||||||
|
0, // 0: hashicorp.consul.internal.demo.v2.Artist.genre:type_name -> hashicorp.consul.internal.demo.v2.Genre
|
||||||
|
3, // 1: hashicorp.consul.internal.demo.v2.Artist.group_members:type_name -> hashicorp.consul.internal.demo.v2.Artist.GroupMembersEntry
|
||||||
|
2, // [2:2] is the sub-list for method output_type
|
||||||
|
2, // [2:2] is the sub-list for method input_type
|
||||||
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_private_pbdemo_v2_demo_proto_init() }
|
||||||
|
func file_private_pbdemo_v2_demo_proto_init() {
|
||||||
|
if File_private_pbdemo_v2_demo_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_private_pbdemo_v2_demo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Artist); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_private_pbdemo_v2_demo_proto_msgTypes[1].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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_private_pbdemo_v2_demo_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 3,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_private_pbdemo_v2_demo_proto_goTypes,
|
||||||
|
DependencyIndexes: file_private_pbdemo_v2_demo_proto_depIdxs,
|
||||||
|
EnumInfos: file_private_pbdemo_v2_demo_proto_enumTypes,
|
||||||
|
MessageInfos: file_private_pbdemo_v2_demo_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_private_pbdemo_v2_demo_proto = out.File
|
||||||
|
file_private_pbdemo_v2_demo_proto_rawDesc = nil
|
||||||
|
file_private_pbdemo_v2_demo_proto_goTypes = nil
|
||||||
|
file_private_pbdemo_v2_demo_proto_depIdxs = nil
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
// This package contains fake resource types, which are useful for working on
|
||||||
|
// Consul's generic storage APIs.
|
||||||
|
package hashicorp.consul.internal.demo.v2;
|
||||||
|
|
||||||
|
message Artist {
|
||||||
|
string name = 1;
|
||||||
|
Genre genre = 2;
|
||||||
|
map<string, string> group_members = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Genre {
|
||||||
|
GENRE_UNSPECIFIED = 0;
|
||||||
|
GENRE_JAZZ = 1;
|
||||||
|
GENRE_FOLK = 2;
|
||||||
|
GENRE_POP = 3;
|
||||||
|
GENRE_METAL = 4;
|
||||||
|
GENRE_PUNK = 5;
|
||||||
|
GENRE_BLUES = 6;
|
||||||
|
GENRE_R_AND_B = 7;
|
||||||
|
GENRE_COUNTRY = 8;
|
||||||
|
GENRE_DISCO = 9;
|
||||||
|
GENRE_SKA = 10;
|
||||||
|
GENRE_HIP_HOP = 11;
|
||||||
|
GENRE_INDIE = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Album {
|
||||||
|
string title = 1;
|
||||||
|
int32 year_of_release = 2;
|
||||||
|
bool critically_aclaimed = 3;
|
||||||
|
repeated string tracks = 4;
|
||||||
|
}
|
Loading…
Reference in New Issue