diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 09c28e80..6d5ee594 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -24,7 +24,7 @@ jobs: run: go get -v -t -d ./... - name: test env: - IN_TRAVIS_CI: yes + INTEGRATION_TESTS: yes run: go test -mod readonly -v ./... lint: name: lint diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b67abfdb..17b4c894 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -24,7 +24,7 @@ jobs: run: go get -v -t -d ./... - name: test env: - IN_TRAVIS_CI: yes + INTEGRATION_TESTS: yes run: go test -mod readonly -v ./... lint: name: lint diff --git a/api/handler/http/http_test.go b/api/handler/http/http_test.go index 7a788d77..6d8e34c1 100644 --- a/api/handler/http/http_test.go +++ b/api/handler/http/http_test.go @@ -1,3 +1,5 @@ +// +build ignore + package http import ( diff --git a/api/handler/options.go b/api/handler/options.go index 4b1801a6..59607280 100644 --- a/api/handler/options.go +++ b/api/handler/options.go @@ -3,7 +3,6 @@ package handler import ( "github.com/unistack-org/micro/v3/api/router" "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/client/grpc" ) var ( @@ -26,10 +25,6 @@ func NewOptions(opts ...Option) Options { o(&options) } - if options.Client == nil { - WithClient(grpc.NewClient())(&options) - } - // set namespace if blank if len(options.Namespace) == 0 { WithNamespace("go.micro.api")(&options) diff --git a/api/router/options.go b/api/router/options.go index 0209e437..a0794816 100644 --- a/api/router/options.go +++ b/api/router/options.go @@ -4,7 +4,6 @@ import ( "github.com/unistack-org/micro/v3/api/resolver" "github.com/unistack-org/micro/v3/api/resolver/vpath" "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/mdns" ) type Options struct { @@ -17,8 +16,7 @@ type Option func(o *Options) func NewOptions(opts ...Option) Options { options := Options{ - Handler: "meta", - Registry: mdns.NewRegistry(), + Handler: "meta", } for _, o := range opts { diff --git a/api/router/registry/registry.go b/api/router/registry/registry.go index da465cd9..e666211d 100644 --- a/api/router/registry/registry.go +++ b/api/router/registry/registry.go @@ -15,7 +15,6 @@ import ( "github.com/unistack-org/micro/v3/logger" "github.com/unistack-org/micro/v3/metadata" "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/cache" util "github.com/unistack-org/micro/v3/util/router" ) @@ -31,9 +30,6 @@ type registryRouter struct { exit chan bool opts router.Options - // registry cache - rc cache.Cache - sync.RWMutex eps map[string]*api.Service // compiled regexp for host and path @@ -68,7 +64,7 @@ func (r *registryRouter) refresh() { // for each service, get service and store endpoints for _, s := range services { - service, err := r.rc.GetService(s.Name) + service, err := r.opts.Registry.GetService(s.Name) if err != nil { if logger.V(logger.ErrorLevel, logger.DefaultLogger) { logger.Errorf("unable to get service: %v", err) @@ -96,7 +92,7 @@ func (r *registryRouter) process(res *registry.Result) { } // get entry from cache - service, err := r.rc.GetService(res.Service.Name) + service, err := r.opts.Registry.GetService(res.Service.Name) if err != nil { if logger.V(logger.ErrorLevel, logger.DefaultLogger) { logger.Errorf("unable to get %v service: %v", res.Service.Name, err) @@ -283,7 +279,6 @@ func (r *registryRouter) Close() error { return nil default: close(r.exit) - r.rc.Stop() } return nil } @@ -437,7 +432,7 @@ func (r *registryRouter) Route(req *http.Request) (*api.Service, error) { name := rp.Name // get service - services, err := r.rc.GetService(name, registry.GetDomain(rp.Domain)) + services, err := r.opts.Registry.GetService(name, registry.GetDomain(rp.Domain)) if err != nil { return nil, err } @@ -486,7 +481,6 @@ func newRouter(opts ...router.Option) *registryRouter { r := ®istryRouter{ exit: make(chan bool), opts: options, - rc: cache.New(options.Registry), eps: make(map[string]*api.Service), ceps: make(map[string]*endpoint), } diff --git a/api/router/router_test.go b/api/router/router_test.go index 511c77f0..408330b7 100644 --- a/api/router/router_test.go +++ b/api/router/router_test.go @@ -1,3 +1,5 @@ +// +build ignore + package router_test import ( diff --git a/api/service/proto/api.pb.go b/api/service/proto/api.pb.go deleted file mode 100644 index ff8f88f7..00000000 --- a/api/service/proto/api.pb.go +++ /dev/null @@ -1,249 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: api/service/proto/api.proto - -package go_micro_api - -import ( - proto "github.com/golang/protobuf/proto" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Endpoint struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Host []string `protobuf:"bytes,2,rep,name=host,proto3" json:"host,omitempty"` - Path []string `protobuf:"bytes,3,rep,name=path,proto3" json:"path,omitempty"` - Method []string `protobuf:"bytes,4,rep,name=method,proto3" json:"method,omitempty"` - Stream bool `protobuf:"varint,5,opt,name=stream,proto3" json:"stream,omitempty"` -} - -func (x *Endpoint) Reset() { - *x = Endpoint{} - if protoimpl.UnsafeEnabled { - mi := &file_api_service_proto_api_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Endpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Endpoint) ProtoMessage() {} - -func (x *Endpoint) ProtoReflect() protoreflect.Message { - mi := &file_api_service_proto_api_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 Endpoint.ProtoReflect.Descriptor instead. -func (*Endpoint) Descriptor() ([]byte, []int) { - return file_api_service_proto_api_proto_rawDescGZIP(), []int{0} -} - -func (x *Endpoint) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Endpoint) GetHost() []string { - if x != nil { - return x.Host - } - return nil -} - -func (x *Endpoint) GetPath() []string { - if x != nil { - return x.Path - } - return nil -} - -func (x *Endpoint) GetMethod() []string { - if x != nil { - return x.Method - } - return nil -} - -func (x *Endpoint) GetStream() bool { - if x != nil { - return x.Stream - } - return false -} - -type EmptyResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *EmptyResponse) Reset() { - *x = EmptyResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_api_service_proto_api_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EmptyResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EmptyResponse) ProtoMessage() {} - -func (x *EmptyResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_service_proto_api_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 EmptyResponse.ProtoReflect.Descriptor instead. -func (*EmptyResponse) Descriptor() ([]byte, []int) { - return file_api_service_proto_api_proto_rawDescGZIP(), []int{1} -} - -var File_api_service_proto_api_proto protoreflect.FileDescriptor - -var file_api_service_proto_api_proto_rawDesc = []byte{ - 0x0a, 0x1b, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x67, - 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x22, 0x76, 0x0a, 0x08, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, - 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x70, - 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x22, 0x0f, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x8d, 0x01, 0x0a, 0x03, 0x41, 0x70, 0x69, 0x12, 0x41, 0x0a, 0x08, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x43, 0x0a, 0x0a, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, - 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_api_service_proto_api_proto_rawDescOnce sync.Once - file_api_service_proto_api_proto_rawDescData = file_api_service_proto_api_proto_rawDesc -) - -func file_api_service_proto_api_proto_rawDescGZIP() []byte { - file_api_service_proto_api_proto_rawDescOnce.Do(func() { - file_api_service_proto_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_service_proto_api_proto_rawDescData) - }) - return file_api_service_proto_api_proto_rawDescData -} - -var file_api_service_proto_api_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_api_service_proto_api_proto_goTypes = []interface{}{ - (*Endpoint)(nil), // 0: go.micro.api.Endpoint - (*EmptyResponse)(nil), // 1: go.micro.api.EmptyResponse -} -var file_api_service_proto_api_proto_depIdxs = []int32{ - 0, // 0: go.micro.api.Api.Register:input_type -> go.micro.api.Endpoint - 0, // 1: go.micro.api.Api.Deregister:input_type -> go.micro.api.Endpoint - 1, // 2: go.micro.api.Api.Register:output_type -> go.micro.api.EmptyResponse - 1, // 3: go.micro.api.Api.Deregister:output_type -> go.micro.api.EmptyResponse - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_api_service_proto_api_proto_init() } -func file_api_service_proto_api_proto_init() { - if File_api_service_proto_api_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_api_service_proto_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Endpoint); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_service_proto_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EmptyResponse); 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_api_service_proto_api_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_api_service_proto_api_proto_goTypes, - DependencyIndexes: file_api_service_proto_api_proto_depIdxs, - MessageInfos: file_api_service_proto_api_proto_msgTypes, - }.Build() - File_api_service_proto_api_proto = out.File - file_api_service_proto_api_proto_rawDesc = nil - file_api_service_proto_api_proto_goTypes = nil - file_api_service_proto_api_proto_depIdxs = nil -} diff --git a/api/service/proto/api.pb.micro.go b/api/service/proto/api.pb.micro.go deleted file mode 100644 index 5e01e426..00000000 --- a/api/service/proto/api.pb.micro.go +++ /dev/null @@ -1,110 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: api/service/proto/api.proto - -package go_micro_api - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Api service - -func NewApiEndpoints() []*api.Endpoint { - return []*api.Endpoint{} -} - -// Client API for Api service - -type ApiService interface { - Register(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) - Deregister(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) -} - -type apiService struct { - c client.Client - name string -} - -func NewApiService(name string, c client.Client) ApiService { - return &apiService{ - c: c, - name: name, - } -} - -func (c *apiService) Register(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) { - req := c.c.NewRequest(c.name, "Api.Register", in) - out := new(EmptyResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *apiService) Deregister(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) { - req := c.c.NewRequest(c.name, "Api.Deregister", in) - out := new(EmptyResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Api service - -type ApiHandler interface { - Register(context.Context, *Endpoint, *EmptyResponse) error - Deregister(context.Context, *Endpoint, *EmptyResponse) error -} - -func RegisterApiHandler(s server.Server, hdlr ApiHandler, opts ...server.HandlerOption) error { - type api interface { - Register(ctx context.Context, in *Endpoint, out *EmptyResponse) error - Deregister(ctx context.Context, in *Endpoint, out *EmptyResponse) error - } - type Api struct { - api - } - h := &apiHandler{hdlr} - return s.Handle(s.NewHandler(&Api{h}, opts...)) -} - -type apiHandler struct { - ApiHandler -} - -func (h *apiHandler) Register(ctx context.Context, in *Endpoint, out *EmptyResponse) error { - return h.ApiHandler.Register(ctx, in, out) -} - -func (h *apiHandler) Deregister(ctx context.Context, in *Endpoint, out *EmptyResponse) error { - return h.ApiHandler.Deregister(ctx, in, out) -} diff --git a/api/service/proto/api.proto b/api/service/proto/api.proto deleted file mode 100644 index c4317d12..00000000 --- a/api/service/proto/api.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -package go.micro.api; - -service Api { - rpc Register(Endpoint) returns (EmptyResponse) {}; - rpc Deregister(Endpoint) returns (EmptyResponse) {}; -} - -message Endpoint { - string name = 1; - repeated string host = 2; - repeated string path = 3; - repeated string method = 4; - bool stream = 5; -} - -message EmptyResponse {} diff --git a/api/service/proto/api_grpc.pb.go b/api/service/proto/api_grpc.pb.go deleted file mode 100644 index caafc8c3..00000000 --- a/api/service/proto/api_grpc.pb.go +++ /dev/null @@ -1,126 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package go_micro_api - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// ApiClient is the client API for Api service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ApiClient interface { - Register(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) - Deregister(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) -} - -type apiClient struct { - cc grpc.ClientConnInterface -} - -func NewApiClient(cc grpc.ClientConnInterface) ApiClient { - return &apiClient{cc} -} - -func (c *apiClient) Register(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) { - out := new(EmptyResponse) - err := c.cc.Invoke(ctx, "/go.micro.api.Api/Register", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *apiClient) Deregister(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) { - out := new(EmptyResponse) - err := c.cc.Invoke(ctx, "/go.micro.api.Api/Deregister", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ApiServer is the server API for Api service. -// All implementations must embed UnimplementedApiServer -// for forward compatibility -type ApiServer interface { - Register(context.Context, *Endpoint) (*EmptyResponse, error) - Deregister(context.Context, *Endpoint) (*EmptyResponse, error) - mustEmbedUnimplementedApiServer() -} - -// UnimplementedApiServer must be embedded to have forward compatible implementations. -type UnimplementedApiServer struct { -} - -func (*UnimplementedApiServer) Register(context.Context, *Endpoint) (*EmptyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") -} -func (*UnimplementedApiServer) Deregister(context.Context, *Endpoint) (*EmptyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Deregister not implemented") -} -func (*UnimplementedApiServer) mustEmbedUnimplementedApiServer() {} - -func RegisterApiServer(s *grpc.Server, srv ApiServer) { - s.RegisterService(&_Api_serviceDesc, srv) -} - -func _Api_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Endpoint) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ApiServer).Register(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.api.Api/Register", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ApiServer).Register(ctx, req.(*Endpoint)) - } - return interceptor(ctx, in, info, handler) -} - -func _Api_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Endpoint) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ApiServer).Deregister(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.api.Api/Deregister", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ApiServer).Deregister(ctx, req.(*Endpoint)) - } - return interceptor(ctx, in, info, handler) -} - -var _Api_serviceDesc = grpc.ServiceDesc{ - ServiceName: "go.micro.api.Api", - HandlerType: (*ApiServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Register", - Handler: _Api_Register_Handler, - }, - { - MethodName: "Deregister", - Handler: _Api_Deregister_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "api/service/proto/api.proto", -} diff --git a/broker/broker.go b/broker/broker.go index 1b0690e7..bc5d3165 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -16,11 +16,10 @@ type Broker interface { // Handler is used to process messages via a subscription of a topic. type Handler func(*Message) error -type ErrorHandler func(*Message, error) - type Message struct { Header map[string]string Body []byte + Error error } // Subscriber is a convenience return type for the Subscribe method diff --git a/broker/http/http.go b/broker/http/http.go deleted file mode 100644 index e7044ec4..00000000 --- a/broker/http/http.go +++ /dev/null @@ -1,689 +0,0 @@ -// Package http provides a http based message broker -package http - -import ( - "bytes" - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net" - "net/http" - "net/url" - "runtime" - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/codec/json" - merr "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/cache" - "github.com/unistack-org/micro/v3/registry/mdns" - maddr "github.com/unistack-org/micro/v3/util/addr" - mnet "github.com/unistack-org/micro/v3/util/net" - mls "github.com/unistack-org/micro/v3/util/tls" - "golang.org/x/net/http2" -) - -// HTTP Broker is a point to point async broker -type httpBroker struct { - id string - address string - opts broker.Options - - mux *http.ServeMux - - c *http.Client - r registry.Registry - - sync.RWMutex - subscribers map[string][]*httpSubscriber - running bool - exit chan chan error - - // offline message inbox - mtx sync.RWMutex - inbox map[string][][]byte -} - -type httpSubscriber struct { - opts broker.SubscribeOptions - id string - topic string - fn broker.Handler - svc *registry.Service - hb *httpBroker -} - -var ( - DefaultPath = "/" - DefaultAddress = "127.0.0.1:0" - serviceName = "micro.http.broker" - broadcastVersion = "ff.http.broadcast" - registerTTL = time.Minute - registerInterval = time.Second * 30 -) - -func init() { - rand.Seed(time.Now().Unix()) -} - -func newTransport(config *tls.Config) *http.Transport { - if config == nil { - config = &tls.Config{ - InsecureSkipVerify: true, - } - } - - dialTLS := func(network string, addr string) (net.Conn, error) { - return tls.Dial(network, addr, config) - } - - t := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 10 * time.Second, - DialTLS: dialTLS, - } - runtime.SetFinalizer(&t, func(tr **http.Transport) { - (*tr).CloseIdleConnections() - }) - - // setup http2 - http2.ConfigureTransport(t) - - return t -} - -func newHttpBroker(opts ...broker.Option) broker.Broker { - options := broker.Options{ - Codec: json.Marshaler{}, - Context: context.TODO(), - Registry: mdns.NewRegistry(), - } - - for _, o := range opts { - o(&options) - } - - // set address - addr := DefaultAddress - - if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 { - addr = options.Addrs[0] - } - - h := &httpBroker{ - id: uuid.New().String(), - address: addr, - opts: options, - r: options.Registry, - c: &http.Client{Transport: newTransport(options.TLSConfig)}, - subscribers: make(map[string][]*httpSubscriber), - exit: make(chan chan error), - mux: http.NewServeMux(), - inbox: make(map[string][][]byte), - } - - // specify the message handler - h.mux.Handle(DefaultPath, h) - - // get optional handlers - if h.opts.Context != nil { - handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler) - if ok { - for pattern, handler := range handlers { - h.mux.Handle(pattern, handler) - } - } - } - - return h -} - -func (h *httpSubscriber) Options() broker.SubscribeOptions { - return h.opts -} - -func (h *httpSubscriber) Topic() string { - return h.topic -} - -func (h *httpSubscriber) Unsubscribe() error { - return h.hb.unsubscribe(h) -} - -func (h *httpBroker) saveMessage(topic string, msg []byte) { - h.mtx.Lock() - defer h.mtx.Unlock() - - // get messages - c := h.inbox[topic] - - // save message - c = append(c, msg) - - // max length 64 - if len(c) > 64 { - c = c[:64] - } - - // save inbox - h.inbox[topic] = c -} - -func (h *httpBroker) getMessage(topic string, num int) [][]byte { - h.mtx.Lock() - defer h.mtx.Unlock() - - // get messages - c, ok := h.inbox[topic] - if !ok { - return nil - } - - // more message than requests - if len(c) >= num { - msg := c[:num] - h.inbox[topic] = c[num:] - return msg - } - - // reset inbox - h.inbox[topic] = nil - - // return all messages - return c -} - -func (h *httpBroker) subscribe(s *httpSubscriber) error { - h.Lock() - defer h.Unlock() - - if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil { - return err - } - - h.subscribers[s.topic] = append(h.subscribers[s.topic], s) - return nil -} - -func (h *httpBroker) unsubscribe(s *httpSubscriber) error { - h.Lock() - defer h.Unlock() - - //nolint:prealloc - var subscribers []*httpSubscriber - - // look for subscriber - for _, sub := range h.subscribers[s.topic] { - // deregister and skip forward - if sub == s { - _ = h.r.Deregister(sub.svc) - continue - } - // keep subscriber - subscribers = append(subscribers, sub) - } - - // set subscribers - h.subscribers[s.topic] = subscribers - - return nil -} - -func (h *httpBroker) run(l net.Listener) { - t := time.NewTicker(registerInterval) - defer t.Stop() - - for { - select { - // heartbeat for each subscriber - case <-t.C: - h.RLock() - for _, subs := range h.subscribers { - for _, sub := range subs { - _ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL)) - } - } - h.RUnlock() - // received exit signal - case ch := <-h.exit: - ch <- l.Close() - h.RLock() - for _, subs := range h.subscribers { - for _, sub := range subs { - _ = h.r.Deregister(sub.svc) - } - } - h.RUnlock() - return - } - } -} - -func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method != "POST" { - err := merr.BadRequest("go.micro.broker", "Method not allowed") - http.Error(w, err.Error(), http.StatusMethodNotAllowed) - return - } - defer req.Body.Close() - - req.ParseForm() - - b, err := ioutil.ReadAll(req.Body) - if err != nil { - errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err) - w.WriteHeader(500) - w.Write([]byte(errr.Error())) - return - } - - var msg *broker.Message - if err = h.opts.Codec.Unmarshal(b, &msg); err != nil { - errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err) - w.WriteHeader(500) - w.Write([]byte(errr.Error())) - return - } - - topic := msg.Header["Micro-Topic"] - - if len(topic) == 0 { - errr := merr.InternalServerError("go.micro.broker", "Topic not found") - w.WriteHeader(500) - w.Write([]byte(errr.Error())) - return - } - - id := req.Form.Get("id") - - //nolint:prealloc - var subs []broker.Handler - - h.RLock() - for _, subscriber := range h.subscribers[topic] { - if id != subscriber.id { - continue - } - subs = append(subs, subscriber.fn) - } - h.RUnlock() - - // execute the handler - for _, fn := range subs { - fn(msg) - } -} - -func (h *httpBroker) Address() string { - h.RLock() - defer h.RUnlock() - return h.address -} - -func (h *httpBroker) Connect() error { - h.RLock() - if h.running { - h.RUnlock() - return nil - } - h.RUnlock() - - h.Lock() - defer h.Unlock() - - var l net.Listener - var err error - - if h.opts.Secure || h.opts.TLSConfig != nil { - config := h.opts.TLSConfig - - fn := func(addr string) (net.Listener, error) { - if config == nil { - hosts := []string{addr} - - // check if its a valid host:port - if host, _, err := net.SplitHostPort(addr); err == nil { - if len(host) == 0 { - hosts = maddr.IPs() - } else { - hosts = []string{host} - } - } - - // generate a certificate - cert, err := mls.Certificate(hosts...) - if err != nil { - return nil, err - } - config = &tls.Config{Certificates: []tls.Certificate{cert}} - } - return tls.Listen("tcp", addr, config) - } - - l, err = mnet.Listen(h.address, fn) - } else { - fn := func(addr string) (net.Listener, error) { - return net.Listen("tcp", addr) - } - - l, err = mnet.Listen(h.address, fn) - } - - if err != nil { - return err - } - - addr := h.address - h.address = l.Addr().String() - - go http.Serve(l, h.mux) - go func() { - h.run(l) - h.Lock() - h.opts.Addrs = []string{addr} - h.address = addr - h.Unlock() - }() - - // get registry - reg := h.opts.Registry - if reg == nil { - reg = mdns.NewRegistry() - } - // set cache - h.r = cache.New(reg) - - // set running - h.running = true - return nil -} - -func (h *httpBroker) Disconnect() error { - h.RLock() - if !h.running { - h.RUnlock() - return nil - } - h.RUnlock() - - h.Lock() - defer h.Unlock() - - // stop cache - rc, ok := h.r.(cache.Cache) - if ok { - rc.Stop() - } - - // exit and return err - ch := make(chan error) - h.exit <- ch - err := <-ch - - // set not running - h.running = false - return err -} - -func (h *httpBroker) Init(opts ...broker.Option) error { - h.RLock() - if h.running { - h.RUnlock() - return errors.New("cannot init while connected") - } - h.RUnlock() - - h.Lock() - defer h.Unlock() - - for _, o := range opts { - o(&h.opts) - } - - if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 { - h.address = h.opts.Addrs[0] - } - - if len(h.id) == 0 { - h.id = "go.micro.http.broker-" + uuid.New().String() - } - - // get registry - reg := h.opts.Registry - if reg == nil { - reg = mdns.NewRegistry() - } - - // get cache - if rc, ok := h.r.(cache.Cache); ok { - rc.Stop() - } - - // set registry - h.r = cache.New(reg) - - // reconfigure tls config - if c := h.opts.TLSConfig; c != nil { - h.c = &http.Client{ - Transport: newTransport(c), - } - } - - return nil -} - -func (h *httpBroker) Options() broker.Options { - return h.opts -} - -func (h *httpBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error { - // create the message first - m := &broker.Message{ - Header: make(map[string]string), - Body: msg.Body, - } - - for k, v := range msg.Header { - m.Header[k] = v - } - - m.Header["Micro-Topic"] = topic - - // encode the message - b, err := h.opts.Codec.Marshal(m) - if err != nil { - return err - } - - // save the message - h.saveMessage(topic, b) - - // now attempt to get the service - h.RLock() - s, err := h.r.GetService(serviceName) - if err != nil { - h.RUnlock() - return err - } - h.RUnlock() - - pub := func(node *registry.Node, t string, b []byte) error { - scheme := "http" - - // check if secure is added in metadata - if node.Metadata["secure"] == "true" { - scheme = "https" - } - - vals := url.Values{} - vals.Add("id", node.Id) - - uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultPath, vals.Encode()) - r, err := h.c.Post(uri, "application/json", bytes.NewReader(b)) - if err != nil { - return err - } - - // discard response body - io.Copy(ioutil.Discard, r.Body) - r.Body.Close() - return nil - } - - srv := func(s []*registry.Service, b []byte) { - for _, service := range s { - var nodes []*registry.Node - - for _, node := range service.Nodes { - // only use nodes tagged with broker http - if node.Metadata["broker"] != "http" { - continue - } - - // look for nodes for the topic - if node.Metadata["topic"] != topic { - continue - } - - nodes = append(nodes, node) - } - - // only process if we have nodes - if len(nodes) == 0 { - continue - } - - switch service.Version { - // broadcast version means broadcast to all nodes - case broadcastVersion: - var success bool - - // publish to all nodes - for _, node := range nodes { - // publish async - if err := pub(node, topic, b); err == nil { - success = true - } - } - - // save if it failed to publish at least once - if !success { - h.saveMessage(topic, b) - } - default: - // select node to publish to - node := nodes[rand.Int()%len(nodes)] - - // publish async to one node - if err := pub(node, topic, b); err != nil { - // if failed save it - h.saveMessage(topic, b) - } - } - } - } - - // do the rest async - go func() { - // get a third of the backlog - messages := h.getMessage(topic, 8) - delay := (len(messages) > 1) - - // publish all the messages - for _, msg := range messages { - // serialize here - srv(s, msg) - - // sending a backlog of messages - if delay { - time.Sleep(time.Millisecond * 100) - } - } - }() - - return nil -} - -func (h *httpBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { - var err error - var host, port string - options := broker.NewSubscribeOptions(opts...) - - // parse address for host, port - host, port, err = net.SplitHostPort(h.Address()) - if err != nil { - return nil, err - } - - addr, err := maddr.Extract(host) - if err != nil { - return nil, err - } - - var secure bool - - if h.opts.Secure || h.opts.TLSConfig != nil { - secure = true - } - - // register service - node := ®istry.Node{ - Id: topic + "-" + h.id, - Address: mnet.HostPort(addr, port), - Metadata: map[string]string{ - "secure": fmt.Sprintf("%t", secure), - "broker": "http", - "topic": topic, - }, - } - - // check for queue group or broadcast queue - version := options.Queue - if len(version) == 0 { - version = broadcastVersion - } - - service := ®istry.Service{ - Name: serviceName, - Version: version, - Nodes: []*registry.Node{node}, - } - - // generate subscriber - subscriber := &httpSubscriber{ - opts: options, - hb: h, - id: node.Id, - topic: topic, - fn: handler, - svc: service, - } - - // subscribe now - if err := h.subscribe(subscriber); err != nil { - return nil, err - } - - // return the subscriber - return subscriber, nil -} - -func (h *httpBroker) String() string { - return "http" -} - -// NewBroker returns a new http broker -func NewBroker(opts ...broker.Option) broker.Broker { - return newHttpBroker(opts...) -} diff --git a/broker/http/http_test.go b/broker/http/http_test.go deleted file mode 100644 index 0995f439..00000000 --- a/broker/http/http_test.go +++ /dev/null @@ -1,377 +0,0 @@ -package http - -import ( - "sync" - "testing" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/memory" -) - -var ( - // mock data - testData = map[string][]*registry.Service{ - "foo": { - { - Name: "foo", - Version: "1.0.0", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.0-123", - Address: "localhost:9999", - }, - { - Id: "foo-1.0.0-321", - Address: "localhost:9999", - }, - }, - }, - { - Name: "foo", - Version: "1.0.1", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.1-321", - Address: "localhost:6666", - }, - }, - }, - { - Name: "foo", - Version: "1.0.3", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.3-345", - Address: "localhost:8888", - }, - }, - }, - }, - } -) - -func newTestRegistry() registry.Registry { - return memory.NewRegistry(memory.Services(testData)) -} - -func sub(be *testing.B, c int) { - be.StopTimer() - m := newTestRegistry() - - b := NewBroker(broker.Registry(m)) - topic := uuid.New().String() - - if err := b.Init(); err != nil { - be.Fatalf("Unexpected init error: %v", err) - } - - if err := b.Connect(); err != nil { - be.Fatalf("Unexpected connect error: %v", err) - } - - msg := &broker.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - var subs []broker.Subscriber - done := make(chan bool, c) - - for i := 0; i < c; i++ { - sub, err := b.Subscribe(topic, func(m *broker.Message) error { - done <- true - - if string(m.Body) != string(msg.Body) { - be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body)) - } - - return nil - }, broker.Queue("shared")) - if err != nil { - be.Fatalf("Unexpected subscribe error: %v", err) - } - subs = append(subs, sub) - } - - for i := 0; i < be.N; i++ { - be.StartTimer() - if err := b.Publish(topic, msg); err != nil { - be.Fatalf("Unexpected publish error: %v", err) - } - <-done - be.StopTimer() - } - - for _, sub := range subs { - sub.Unsubscribe() - } - - if err := b.Disconnect(); err != nil { - be.Fatalf("Unexpected disconnect error: %v", err) - } -} - -func pub(be *testing.B, c int) { - be.StopTimer() - m := newTestRegistry() - b := NewBroker(broker.Registry(m)) - topic := uuid.New().String() - - if err := b.Init(); err != nil { - be.Fatalf("Unexpected init error: %v", err) - } - - if err := b.Connect(); err != nil { - be.Fatalf("Unexpected connect error: %v", err) - } - - msg := &broker.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - done := make(chan bool, c*4) - - sub, err := b.Subscribe(topic, func(m *broker.Message) error { - done <- true - if string(m.Body) != string(msg.Body) { - be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body)) - } - return nil - }, broker.Queue("shared")) - if err != nil { - be.Fatalf("Unexpected subscribe error: %v", err) - } - - var wg sync.WaitGroup - ch := make(chan int, c*4) - be.StartTimer() - - for i := 0; i < c; i++ { - go func() { - for range ch { - if err := b.Publish(topic, msg); err != nil { - be.Fatalf("Unexpected publish error: %v", err) - } - select { - case <-done: - case <-time.After(time.Second): - } - wg.Done() - } - }() - } - - for i := 0; i < be.N; i++ { - wg.Add(1) - ch <- i - } - - wg.Wait() - be.StopTimer() - sub.Unsubscribe() - close(ch) - close(done) - - if err := b.Disconnect(); err != nil { - be.Fatalf("Unexpected disconnect error: %v", err) - } -} - -func TestBroker(t *testing.T) { - m := newTestRegistry() - b := NewBroker(broker.Registry(m)) - - if err := b.Init(); err != nil { - t.Fatalf("Unexpected init error: %v", err) - } - - if err := b.Connect(); err != nil { - t.Fatalf("Unexpected connect error: %v", err) - } - - msg := &broker.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - done := make(chan bool) - - sub, err := b.Subscribe("test", func(m *broker.Message) error { - - if string(m.Body) != string(msg.Body) { - t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body)) - } - - close(done) - return nil - }) - if err != nil { - t.Fatalf("Unexpected subscribe error: %v", err) - } - - if err := b.Publish("test", msg); err != nil { - t.Fatalf("Unexpected publish error: %v", err) - } - - <-done - sub.Unsubscribe() - - if err := b.Disconnect(); err != nil { - t.Fatalf("Unexpected disconnect error: %v", err) - } -} - -func TestConcurrentSubBroker(t *testing.T) { - m := newTestRegistry() - b := NewBroker(broker.Registry(m)) - - if err := b.Init(); err != nil { - t.Fatalf("Unexpected init error: %v", err) - } - - if err := b.Connect(); err != nil { - t.Fatalf("Unexpected connect error: %v", err) - } - - msg := &broker.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - var subs []broker.Subscriber - var wg sync.WaitGroup - - for i := 0; i < 10; i++ { - sub, err := b.Subscribe("test", func(m *broker.Message) error { - defer wg.Done() - - if string(m.Body) != string(msg.Body) { - t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body)) - } - - return nil - }) - if err != nil { - t.Fatalf("Unexpected subscribe error: %v", err) - } - - wg.Add(1) - subs = append(subs, sub) - } - - if err := b.Publish("test", msg); err != nil { - t.Fatalf("Unexpected publish error: %v", err) - } - - wg.Wait() - - for _, sub := range subs { - sub.Unsubscribe() - } - - if err := b.Disconnect(); err != nil { - t.Fatalf("Unexpected disconnect error: %v", err) - } -} - -func TestConcurrentPubBroker(t *testing.T) { - m := newTestRegistry() - b := NewBroker(broker.Registry(m)) - - if err := b.Init(); err != nil { - t.Fatalf("Unexpected init error: %v", err) - } - - if err := b.Connect(); err != nil { - t.Fatalf("Unexpected connect error: %v", err) - } - - msg := &broker.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - var wg sync.WaitGroup - - sub, err := b.Subscribe("test", func(m *broker.Message) error { - defer wg.Done() - - if string(m.Body) != string(msg.Body) { - t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body)) - } - - return nil - }) - if err != nil { - t.Fatalf("Unexpected subscribe error: %v", err) - } - - for i := 0; i < 10; i++ { - wg.Add(1) - - if err := b.Publish("test", msg); err != nil { - t.Fatalf("Unexpected publish error: %v", err) - } - } - - wg.Wait() - - sub.Unsubscribe() - - if err := b.Disconnect(); err != nil { - t.Fatalf("Unexpected disconnect error: %v", err) - } -} - -func BenchmarkSub1(b *testing.B) { - sub(b, 1) -} -func BenchmarkSub8(b *testing.B) { - sub(b, 8) -} - -func BenchmarkSub32(b *testing.B) { - sub(b, 32) -} - -func BenchmarkSub64(b *testing.B) { - sub(b, 64) -} - -func BenchmarkSub128(b *testing.B) { - sub(b, 128) -} - -func BenchmarkPub1(b *testing.B) { - pub(b, 1) -} - -func BenchmarkPub8(b *testing.B) { - pub(b, 8) -} - -func BenchmarkPub32(b *testing.B) { - pub(b, 32) -} - -func BenchmarkPub64(b *testing.B) { - pub(b, 64) -} - -func BenchmarkPub128(b *testing.B) { - pub(b, 128) -} diff --git a/broker/http/options.go b/broker/http/options.go deleted file mode 100644 index 4c14dab0..00000000 --- a/broker/http/options.go +++ /dev/null @@ -1,23 +0,0 @@ -package http - -import ( - "context" - "net/http" - - "github.com/unistack-org/micro/v3/broker" -) - -// Handle registers the handler for the given pattern. -func Handle(pattern string, handler http.Handler) broker.Option { - return func(o *broker.Options) { - if o.Context == nil { - o.Context = context.Background() - } - handlers, ok := o.Context.Value("http_handlers").(map[string]http.Handler) - if !ok { - handlers = make(map[string]http.Handler) - } - handlers[pattern] = handler - o.Context = context.WithValue(o.Context, "http_handlers", handlers) - } -} diff --git a/broker/memory/memory.go b/broker/memory/memory.go deleted file mode 100644 index 60bc04fc..00000000 --- a/broker/memory/memory.go +++ /dev/null @@ -1,183 +0,0 @@ -// Package memory provides a memory broker -package memory - -import ( - "context" - "errors" - "math/rand" - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/broker" - maddr "github.com/unistack-org/micro/v3/util/addr" - mnet "github.com/unistack-org/micro/v3/util/net" -) - -type memoryBroker struct { - opts broker.Options - - addr string - sync.RWMutex - connected bool - Subscribers map[string][]*memorySubscriber -} - -type memorySubscriber struct { - id string - topic string - exit chan bool - handler broker.Handler - opts broker.SubscribeOptions -} - -func (m *memoryBroker) Options() broker.Options { - return m.opts -} - -func (m *memoryBroker) Address() string { - return m.addr -} - -func (m *memoryBroker) Connect() error { - m.Lock() - defer m.Unlock() - - if m.connected { - return nil - } - - // use 127.0.0.1 to avoid scan of all network interfaces - addr, err := maddr.Extract("127.0.0.1") - if err != nil { - return err - } - i := rand.Intn(20000) - // set addr with port - addr = mnet.HostPort(addr, 10000+i) - - m.addr = addr - m.connected = true - - return nil -} - -func (m *memoryBroker) Disconnect() error { - m.Lock() - defer m.Unlock() - - if !m.connected { - return nil - } - - m.connected = false - - return nil -} - -func (m *memoryBroker) Init(opts ...broker.Option) error { - for _, o := range opts { - o(&m.opts) - } - return nil -} - -func (m *memoryBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error { - m.RLock() - if !m.connected { - m.RUnlock() - return errors.New("not connected") - } - - subs, ok := m.Subscribers[topic] - m.RUnlock() - if !ok { - return nil - } - - for _, sub := range subs { - if err := sub.handler(msg); err != nil { - if eh := sub.opts.ErrorHandler; eh != nil { - eh(msg, err) - } - continue - } - } - - return nil -} - -func (m *memoryBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { - m.RLock() - if !m.connected { - m.RUnlock() - return nil, errors.New("not connected") - } - m.RUnlock() - - var options broker.SubscribeOptions - for _, o := range opts { - o(&options) - } - - sub := &memorySubscriber{ - exit: make(chan bool, 1), - id: uuid.New().String(), - topic: topic, - handler: handler, - opts: options, - } - - m.Lock() - m.Subscribers[topic] = append(m.Subscribers[topic], sub) - m.Unlock() - - go func() { - <-sub.exit - m.Lock() - var newSubscribers []*memorySubscriber - for _, sb := range m.Subscribers[topic] { - if sb.id == sub.id { - continue - } - newSubscribers = append(newSubscribers, sb) - } - m.Subscribers[topic] = newSubscribers - m.Unlock() - }() - - return sub, nil -} - -func (m *memoryBroker) String() string { - return "memory" -} - -func (m *memorySubscriber) Options() broker.SubscribeOptions { - return m.opts -} - -func (m *memorySubscriber) Topic() string { - return m.topic -} - -func (m *memorySubscriber) Unsubscribe() error { - m.exit <- true - return nil -} - -func NewBroker(opts ...broker.Option) broker.Broker { - options := broker.Options{ - Context: context.Background(), - } - - rand.Seed(time.Now().UnixNano()) - for _, o := range opts { - o(&options) - } - - return &memoryBroker{ - opts: options, - Subscribers: make(map[string][]*memorySubscriber), - } -} diff --git a/broker/memory/memory_test.go b/broker/memory/memory_test.go deleted file mode 100644 index be16fce4..00000000 --- a/broker/memory/memory_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package memory - -import ( - "fmt" - "testing" - - "github.com/unistack-org/micro/v3/broker" -) - -func TestMemoryBroker(t *testing.T) { - b := NewBroker() - - if err := b.Connect(); err != nil { - t.Fatalf("Unexpected connect error %v", err) - } - - topic := "test" - count := 10 - - fn := func(m *broker.Message) error { - return nil - } - - sub, err := b.Subscribe(topic, fn) - if err != nil { - t.Fatalf("Unexpected error subscribing %v", err) - } - - for i := 0; i < count; i++ { - message := &broker.Message{ - Header: map[string]string{ - "foo": "bar", - "id": fmt.Sprintf("%d", i), - }, - Body: []byte(`hello world`), - } - - if err := b.Publish(topic, message); err != nil { - t.Fatalf("Unexpected error publishing %d", i) - } - } - - if err := sub.Unsubscribe(); err != nil { - t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err) - } - - if err := b.Disconnect(); err != nil { - t.Fatalf("Unexpected connect error %v", err) - } -} diff --git a/broker/nats/context.go b/broker/nats/context.go deleted file mode 100644 index c79b9186..00000000 --- a/broker/nats/context.go +++ /dev/null @@ -1,17 +0,0 @@ -package nats - -import ( - "context" - - "github.com/unistack-org/micro/v3/broker" -) - -// setBrokerOption returns a function to setup a context with given value -func setBrokerOption(k, v interface{}) broker.Option { - return func(o *broker.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/broker/nats/nats.go b/broker/nats/nats.go deleted file mode 100644 index 3a410f2b..00000000 --- a/broker/nats/nats.go +++ /dev/null @@ -1,294 +0,0 @@ -// Package nats provides a NATS broker -package nats - -import ( - "context" - "errors" - "strings" - "sync" - - nats "github.com/nats-io/nats.go" - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/codec/json" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/registry/mdns" -) - -type natsBroker struct { - sync.Once - sync.RWMutex - - // indicate if we're connected - connected bool - - addrs []string - conn *nats.Conn - opts broker.Options - nopts nats.Options - - // should we drain the connection - drain bool - closeCh chan (error) -} - -type subscriber struct { - s *nats.Subscription - opts broker.SubscribeOptions -} - -func (s *subscriber) Options() broker.SubscribeOptions { - return s.opts -} - -func (s *subscriber) Topic() string { - return s.s.Subject -} - -func (s *subscriber) Unsubscribe() error { - return s.s.Unsubscribe() -} - -func (n *natsBroker) Address() string { - if n.conn != nil && n.conn.IsConnected() { - return n.conn.ConnectedUrl() - } - - if len(n.addrs) > 0 { - return n.addrs[0] - } - - return "" -} - -func (n *natsBroker) setAddrs(addrs []string) []string { - //nolint:prealloc - var cAddrs []string - for _, addr := range addrs { - if len(addr) == 0 { - continue - } - if !strings.HasPrefix(addr, "nats://") { - addr = "nats://" + addr - } - cAddrs = append(cAddrs, addr) - } - if len(cAddrs) == 0 { - cAddrs = []string{nats.DefaultURL} - } - return cAddrs -} - -func (n *natsBroker) Connect() error { - n.Lock() - defer n.Unlock() - - if n.connected { - return nil - } - - status := nats.CLOSED - if n.conn != nil { - status = n.conn.Status() - } - - switch status { - case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING: - n.connected = true - return nil - default: // DISCONNECTED or CLOSED or DRAINING - opts := n.nopts - opts.Servers = n.addrs - opts.Secure = n.opts.Secure - opts.TLSConfig = n.opts.TLSConfig - - // secure might not be set - if n.opts.TLSConfig != nil { - opts.Secure = true - } - - c, err := opts.Connect() - if err != nil { - if logger.V(logger.WarnLevel, logger.DefaultLogger) { - logger.Warnf("Error connecting to broker: %v", err) - } - - return err - } - n.conn = c - n.connected = true - return nil - } -} - -func (n *natsBroker) Disconnect() error { - n.Lock() - defer n.Unlock() - - // drain the connection if specified - if n.drain { - n.conn.Drain() - n.closeCh <- nil - } - - // close the client connection - n.conn.Close() - - // set not connected - n.connected = false - - return nil -} - -func (n *natsBroker) Init(opts ...broker.Option) error { - n.setOption(opts...) - return nil -} - -func (n *natsBroker) Options() broker.Options { - return n.opts -} - -func (n *natsBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error { - n.RLock() - defer n.RUnlock() - - if n.conn == nil { - return errors.New("not connected") - } - - b, err := n.opts.Codec.Marshal(msg) - if err != nil { - return err - } - return n.conn.Publish(topic, b) -} - -func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { - n.RLock() - if n.conn == nil { - n.RUnlock() - return nil, errors.New("not connected") - } - n.RUnlock() - - opt := broker.SubscribeOptions{ - Context: context.Background(), - } - - for _, o := range opts { - o(&opt) - } - - fn := func(msg *nats.Msg) { - var m *broker.Message - eh := opt.ErrorHandler - err := n.opts.Codec.Unmarshal(msg.Data, &m) - if err != nil { - m.Body = msg.Data - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error(err) - } - if eh != nil { - eh(m, err) - } - return - } - if err := handler(m); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error(err) - } - if eh != nil { - eh(m, err) - } - } - } - - var sub *nats.Subscription - var err error - - n.RLock() - if len(opt.Queue) > 0 { - sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn) - } else { - sub, err = n.conn.Subscribe(topic, fn) - } - n.RUnlock() - if err != nil { - return nil, err - } - return &subscriber{s: sub, opts: opt}, nil -} - -func (n *natsBroker) String() string { - return "nats" -} - -func (n *natsBroker) setOption(opts ...broker.Option) { - for _, o := range opts { - o(&n.opts) - } - - n.Once.Do(func() { - n.nopts = nats.GetDefaultOptions() - }) - - if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok { - n.nopts = nopts - } - - // broker.Options have higher priority than nats.Options - // only if Addrs, Secure or TLSConfig were not set through a broker.Option - // we read them from nats.Option - if len(n.opts.Addrs) == 0 { - n.opts.Addrs = n.nopts.Servers - } - - if !n.opts.Secure { - n.opts.Secure = n.nopts.Secure - } - - if n.opts.TLSConfig == nil { - n.opts.TLSConfig = n.nopts.TLSConfig - } - n.addrs = n.setAddrs(n.opts.Addrs) - - if n.opts.Context.Value(drainConnectionKey{}) != nil { - n.drain = true - n.closeCh = make(chan error) - n.nopts.ClosedCB = n.onClose - n.nopts.AsyncErrorCB = n.onAsyncError - n.nopts.DisconnectedErrCB = n.onDisconnectedError - } -} - -func (n *natsBroker) onClose(conn *nats.Conn) { - n.closeCh <- nil -} - -func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) { - // There are kinds of different async error nats might callback, but we are interested - // in ErrDrainTimeout only here. - if err == nats.ErrDrainTimeout { - n.closeCh <- err - } -} - -func (n *natsBroker) onDisconnectedError(conn *nats.Conn, err error) { - n.closeCh <- err -} - -func NewBroker(opts ...broker.Option) broker.Broker { - options := broker.Options{ - // Default codec - Codec: json.Marshaler{}, - Context: context.Background(), - Registry: mdns.NewRegistry(), - } - - n := &natsBroker{ - opts: options, - } - n.setOption(opts...) - - return n -} diff --git a/broker/nats/nats_test.go b/broker/nats/nats_test.go deleted file mode 100644 index e26a066b..00000000 --- a/broker/nats/nats_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package nats - -import ( - "fmt" - "testing" - - nats "github.com/nats-io/nats.go" - "github.com/unistack-org/micro/v3/broker" -) - -var addrTestCases = []struct { - name string - description string - addrs map[string]string // expected address : set address -}{ - { - "brokerOpts", - "set broker addresses through a broker.Option in constructor", - map[string]string{ - "nats://192.168.10.1:5222": "192.168.10.1:5222", - "nats://10.20.10.0:4222": "10.20.10.0:4222"}, - }, - { - "brokerInit", - "set broker addresses through a broker.Option in broker.Init()", - map[string]string{ - "nats://192.168.10.1:5222": "192.168.10.1:5222", - "nats://10.20.10.0:4222": "10.20.10.0:4222"}, - }, - { - "natsOpts", - "set broker addresses through the nats.Option in constructor", - map[string]string{ - "nats://192.168.10.1:5222": "192.168.10.1:5222", - "nats://10.20.10.0:4222": "10.20.10.0:4222"}, - }, - { - "default", - "check if default Address is set correctly", - map[string]string{ - "nats://127.0.0.1:4222": "", - }, - }, -} - -// TestInitAddrs tests issue #100. Ensures that if the addrs is set by an option in init it will be used. -func TestInitAddrs(t *testing.T) { - - for _, tc := range addrTestCases { - t.Run(fmt.Sprintf("%s: %s", tc.name, tc.description), func(t *testing.T) { - - var br broker.Broker - var addrs []string - - for _, addr := range tc.addrs { - addrs = append(addrs, addr) - } - - switch tc.name { - case "brokerOpts": - // we know that there are just two addrs in the dict - br = NewBroker(broker.Addrs(addrs[0], addrs[1])) - br.Init() - case "brokerInit": - br = NewBroker() - // we know that there are just two addrs in the dict - br.Init(broker.Addrs(addrs[0], addrs[1])) - case "natsOpts": - nopts := nats.GetDefaultOptions() - nopts.Servers = addrs - br = NewBroker(Options(nopts)) - br.Init() - case "default": - br = NewBroker() - br.Init() - } - - natsBroker, ok := br.(*natsBroker) - if !ok { - t.Fatal("Expected broker to be of types *natsBroker") - } - // check if the same amount of addrs we set has actually been set, default - // have only 1 address nats://127.0.0.1:4222 (current nats code) or - // nats://localhost:4222 (older code version) - if len(natsBroker.addrs) != len(tc.addrs) && tc.name != "default" { - t.Errorf("Expected Addr count = %d, Actual Addr count = %d", - len(natsBroker.addrs), len(tc.addrs)) - } - - for _, addr := range natsBroker.addrs { - _, ok := tc.addrs[addr] - if !ok { - t.Errorf("Expected '%s' has not been set", addr) - } - } - }) - } -} diff --git a/broker/nats/options.go b/broker/nats/options.go deleted file mode 100644 index 600e615f..00000000 --- a/broker/nats/options.go +++ /dev/null @@ -1,19 +0,0 @@ -package nats - -import ( - nats "github.com/nats-io/nats.go" - "github.com/unistack-org/micro/v3/broker" -) - -type optionsKey struct{} -type drainConnectionKey struct{} - -// Options accepts nats.Options -func Options(opts nats.Options) broker.Option { - return setBrokerOption(optionsKey{}, opts) -} - -// DrainConnection will drain subscription on close -func DrainConnection() broker.Option { - return setBrokerOption(drainConnectionKey{}, struct{}{}) -} diff --git a/broker/options.go b/broker/options.go index 757b61a8..5331e784 100644 --- a/broker/options.go +++ b/broker/options.go @@ -9,10 +9,15 @@ import ( ) type Options struct { + AutoAck bool + Addrs []string Secure bool Codec codec.Marshaler + // Handler executed when errors occur processing messages + ErrorHandler Handler + TLSConfig *tls.Config // Registry used for clustering Registry registry.Registry @@ -28,13 +33,16 @@ type PublishOptions struct { } type SubscribeOptions struct { - // Handler executed when errors occur processing messages - ErrorHandler ErrorHandler + // AutoAck ack messages if handler returns nil err + AutoAck bool - // Subscribers with the same queue name + // Handler executed when errors occur processing messages + ErrorHandler Handler + + // Subscribers with the same group name // will create a shared subscription where each // receives a subset of messages. - Queue string + Group string // Other options for implementations of the interface // can be stored in a context @@ -81,16 +89,24 @@ func Codec(c codec.Marshaler) Option { // ErrorHandler will catch all broker errors that cant be handled // in normal way, for example Codec errors -func HandleError(h ErrorHandler) SubscribeOption { +func ErrorHandler(h Handler) Option { + return func(o *Options) { + o.ErrorHandler = h + } +} + +// SubscribeErrorHandler will catch all broker errors that cant be handled +// in normal way, for example Codec errors +func SubscribeErrorHandler(h Handler) SubscribeOption { return func(o *SubscribeOptions) { o.ErrorHandler = h } } -// Queue sets the name of the queue to share messages on -func Queue(name string) SubscribeOption { +// SubscribeGroup sets the name of the queue to share messages on +func SubscribeGroup(name string) SubscribeOption { return func(o *SubscribeOptions) { - o.Queue = name + o.Group = name } } diff --git a/build/docker/docker.go b/build/docker/docker.go deleted file mode 100644 index d8a16776..00000000 --- a/build/docker/docker.go +++ /dev/null @@ -1,96 +0,0 @@ -// Package docker builds docker images -package docker - -import ( - "archive/tar" - "bytes" - "io/ioutil" - "os" - "path/filepath" - - docker "github.com/fsouza/go-dockerclient" - "github.com/unistack-org/micro/v3/build" - "github.com/unistack-org/micro/v3/logger" -) - -type dockerBuild struct { - Options build.Options - Client *docker.Client -} - -func (d *dockerBuild) Package(name string, s *build.Source) (*build.Package, error) { - image := name - - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - defer tw.Close() - - dockerFile := "Dockerfile" - - // open docker file - f, err := os.Open(filepath.Join(s.Path, dockerFile)) - if err != nil { - return nil, err - } - // read docker file - by, err := ioutil.ReadAll(f) - if err != nil { - return nil, err - } - - tarHeader := &tar.Header{ - Name: dockerFile, - Size: int64(len(by)), - } - err = tw.WriteHeader(tarHeader) - if err != nil { - return nil, err - } - _, err = tw.Write(by) - if err != nil { - return nil, err - } - tr := bytes.NewReader(buf.Bytes()) - - err = d.Client.BuildImage(docker.BuildImageOptions{ - Name: image, - Dockerfile: dockerFile, - InputStream: tr, - OutputStream: ioutil.Discard, - RmTmpContainer: true, - SuppressOutput: true, - }) - if err != nil { - return nil, err - } - return &build.Package{ - Name: image, - Path: image, - Type: "docker", - Source: s, - }, nil -} - -func (d *dockerBuild) Remove(b *build.Package) error { - return d.Client.RemoveImage(b.Name) -} - -func (d *dockerBuild) String() string { - return "docker" -} - -func NewBuild(opts ...build.Option) build.Build { - options := build.Options{} - for _, o := range opts { - o(&options) - } - endpoint := "unix:///var/run/docker.sock" - client, err := docker.NewClient(endpoint) - if err != nil { - logger.Fatal(err) - } - return &dockerBuild{ - Options: options, - Client: client, - } -} diff --git a/build/golang/golang.go b/build/golang/golang.go deleted file mode 100644 index 830acd4e..00000000 --- a/build/golang/golang.go +++ /dev/null @@ -1,75 +0,0 @@ -// Package golang is a go package manager -package golang - -import ( - "os" - "os/exec" - "path/filepath" - - "github.com/unistack-org/micro/v3/build" -) - -type goBuild struct { - Options build.Options - Cmd string - Path string -} - -// whichGo locates the go command -func whichGo() string { - // check GOROOT - if gr := os.Getenv("GOROOT"); len(gr) > 0 { - return filepath.Join(gr, "bin", "go") - } - - // check path - for _, p := range filepath.SplitList(os.Getenv("PATH")) { - bin := filepath.Join(p, "go") - if _, err := os.Stat(bin); err == nil { - return bin - } - } - - // best effort - return "go" -} - -func (g *goBuild) Package(name string, src *build.Source) (*build.Package, error) { - binary := filepath.Join(g.Path, name) - source := src.Path - - cmd := exec.Command(g.Cmd, "build", "-o", binary, source) - if err := cmd.Run(); err != nil { - return nil, err - } - - return &build.Package{ - Name: name, - Path: binary, - Type: g.String(), - Source: src, - }, nil -} - -func (g *goBuild) Remove(b *build.Package) error { - binary := filepath.Join(b.Path, b.Name) - return os.Remove(binary) -} - -func (g *goBuild) String() string { - return "golang" -} - -func NewBuild(opts ...build.Option) build.Build { - options := build.Options{ - Path: os.TempDir(), - } - for _, o := range opts { - o(&options) - } - return &goBuild{ - Options: options, - Cmd: whichGo(), - Path: options.Path, - } -} diff --git a/build/tar/tar.go b/build/tar/tar.go deleted file mode 100644 index cc190314..00000000 --- a/build/tar/tar.go +++ /dev/null @@ -1,45 +0,0 @@ -// Package tar basically tarballs source code -package tar - -import ( - "os" - "path/filepath" - - "github.com/unistack-org/micro/v3/build" -) - -type tarBuild struct{} - -func (t *tarBuild) Package(name string, src *build.Source) (*build.Package, error) { - pkg := name + ".tar.gz" - // path to the tarball - path := filepath.Join(os.TempDir(), src.Path, pkg) - - // create a temp directory - if err := os.MkdirAll(filepath.Join(os.TempDir(), src.Path), 0755); err != nil { - return nil, err - } - - if err := Compress(src.Path, path); err != nil { - return nil, err - } - - return &build.Package{ - Name: name, - Path: path, - Type: t.String(), - Source: src, - }, nil -} - -func (t *tarBuild) Remove(b *build.Package) error { - return os.Remove(b.Path) -} - -func (t *tarBuild) String() string { - return "tar.gz" -} - -func NewBuild(opts ...build.Option) build.Build { - return new(tarBuild) -} diff --git a/build/tar/util.go b/build/tar/util.go deleted file mode 100644 index 11d1a6f7..00000000 --- a/build/tar/util.go +++ /dev/null @@ -1,92 +0,0 @@ -package tar - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "io" - "os" - "path/filepath" - "strings" -) - -func Compress(source, dest string) error { - // tar + gzip - var buf bytes.Buffer - _ = compress(source, &buf) - - // write the .tar.gzip - fileToWrite, err := os.OpenFile(dest, os.O_CREATE|os.O_RDWR, 0666) - if err != nil { - return err - } - _, err = io.Copy(fileToWrite, &buf) - return err -} - -func compress(src string, buf io.Writer) error { - // tar > gzip > buf - zr := gzip.NewWriter(buf) - tw := tar.NewWriter(zr) - - // walk through every file in the folder - filepath.Walk(src, func(file string, fi os.FileInfo, err error) error { - - // generate tar header - header, err := tar.FileInfoHeader(fi, file) - if err != nil { - return err - } - - // must provide real name - // (see https://golang.org/src/archive/tar/common.go?#L626) - - srcWithSlash := src - if !strings.HasSuffix(src, string(filepath.Separator)) { - srcWithSlash = src + string(filepath.Separator) - } - header.Name = strings.ReplaceAll(file, srcWithSlash, "") - if header.Name == src || len(strings.TrimSpace(header.Name)) == 0 { - return nil - } - - // @todo This is a quick hack to speed up whole repo uploads - // https://github.com/micro/micro/pull/956 - if !fi.IsDir() && !strings.HasSuffix(header.Name, ".go") && - !strings.HasSuffix(header.Name, ".mod") && - !strings.HasSuffix(header.Name, ".sum") { - return nil - } - - // write header - if err := tw.WriteHeader(header); err != nil { - return err - } - if fi.IsDir() { - return nil - } - - // if not a dir, write file content - - data, err := os.Open(file) - if err != nil { - return err - } - if _, err := io.Copy(tw, data); err != nil { - return err - } - - return nil - }) - - // produce tar - if err := tw.Close(); err != nil { - return err - } - // produce gzip - if err := zr.Close(); err != nil { - return err - } - // - return nil -} diff --git a/client/grpc/README.md b/client/grpc/README.md deleted file mode 100644 index 87c0c90d..00000000 --- a/client/grpc/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# GRPC Client - -The grpc client is a [micro.Client](https://godoc.org/github.com/micro/go-micro/client#Client) compatible client. - -## Overview - -The client makes use of the [google.golang.org/grpc](google.golang.org/grpc) framework for the underlying communication mechanism. - -## Usage - -Specify the client to your micro service - -```go -import ( - "github.com/micro/go-micro" - "github.com/micro/go-plugins/client/grpc" -) - -func main() { - service := micro.NewService( - micro.Name("greeter"), - micro.Client(grpc.NewClient()), - ) -} -``` diff --git a/client/grpc/codec.go b/client/grpc/codec.go deleted file mode 100644 index 2aff87c3..00000000 --- a/client/grpc/codec.go +++ /dev/null @@ -1,206 +0,0 @@ -package grpc - -import ( - "encoding/json" - "fmt" - "strings" - - b "bytes" - - "github.com/golang/protobuf/jsonpb" - "github.com/golang/protobuf/proto" - "github.com/oxtoacart/bpool" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/codec/bytes" - "google.golang.org/grpc" - "google.golang.org/grpc/encoding" -) - -type jsonCodec struct{} -type protoCodec struct{} -type bytesCodec struct{} -type wrapCodec struct{ encoding.Codec } - -var jsonpbMarshaler = &jsonpb.Marshaler{} -var useNumber bool - -// create buffer pool with 16 instances each preallocated with 256 bytes -var bufferPool = bpool.NewSizedBufferPool(16, 256) - -var ( - defaultGRPCCodecs = map[string]encoding.Codec{ - "application/json": jsonCodec{}, - "application/proto": protoCodec{}, - "application/protobuf": protoCodec{}, - "application/octet-stream": protoCodec{}, - "application/grpc": protoCodec{}, - "application/grpc+json": jsonCodec{}, - "application/grpc+proto": protoCodec{}, - "application/grpc+bytes": bytesCodec{}, - } -) - -// UseNumber fix unmarshal Number(8234567890123456789) to interface(8.234567890123457e+18) -func UseNumber() { - useNumber = true -} - -func (w wrapCodec) String() string { - return w.Codec.Name() -} - -func (w wrapCodec) Marshal(v interface{}) ([]byte, error) { - b, ok := v.(*bytes.Frame) - if ok { - return b.Data, nil - } - return w.Codec.Marshal(v) -} - -func (w wrapCodec) Unmarshal(data []byte, v interface{}) error { - b, ok := v.(*bytes.Frame) - if ok { - b.Data = data - return nil - } - return w.Codec.Unmarshal(data, v) -} - -func (protoCodec) Marshal(v interface{}) ([]byte, error) { - switch m := v.(type) { - case *bytes.Frame: - return m.Data, nil - case proto.Message: - return proto.Marshal(m) - } - return nil, fmt.Errorf("failed to marshal: %v is not type of *bytes.Frame or proto.Message", v) -} - -func (protoCodec) Unmarshal(data []byte, v interface{}) error { - m, ok := v.(proto.Message) - if !ok { - return fmt.Errorf("failed to unmarshal: %v is not type of proto.Message", v) - } - return proto.Unmarshal(data, m) -} - -func (protoCodec) Name() string { - return "proto" -} - -func (bytesCodec) Marshal(v interface{}) ([]byte, error) { - b, ok := v.(*[]byte) - if !ok { - return nil, fmt.Errorf("failed to marshal: %v is not type of *[]byte", v) - } - return *b, nil -} - -func (bytesCodec) Unmarshal(data []byte, v interface{}) error { - b, ok := v.(*[]byte) - if !ok { - return fmt.Errorf("failed to unmarshal: %v is not type of *[]byte", v) - } - *b = data - return nil -} - -func (bytesCodec) Name() string { - return "bytes" -} - -func (jsonCodec) Marshal(v interface{}) ([]byte, error) { - if b, ok := v.(*bytes.Frame); ok { - return b.Data, nil - } - - if pb, ok := v.(proto.Message); ok { - buf := bufferPool.Get() - defer bufferPool.Put(buf) - if err := jsonpbMarshaler.Marshal(buf, pb); err != nil { - return nil, err - } - return buf.Bytes(), nil - } - - return json.Marshal(v) -} - -func (jsonCodec) Unmarshal(data []byte, v interface{}) error { - if len(data) == 0 { - return nil - } - if b, ok := v.(*bytes.Frame); ok { - b.Data = data - return nil - } - if pb, ok := v.(proto.Message); ok { - return jsonpb.Unmarshal(b.NewReader(data), pb) - } - - dec := json.NewDecoder(b.NewReader(data)) - if useNumber { - dec.UseNumber() - } - return dec.Decode(v) -} - -func (jsonCodec) Name() string { - return "json" -} - -type grpcCodec struct { - // headers - id string - target string - method string - endpoint string - - s grpc.ClientStream - c encoding.Codec -} - -func (g *grpcCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error { - md, err := g.s.Header() - if err != nil { - return err - } - if m == nil { - m = new(codec.Message) - } - if m.Header == nil { - m.Header = make(map[string]string, len(md)) - } - for k, v := range md { - m.Header[k] = strings.Join(v, ",") - } - m.Id = g.id - m.Target = g.target - m.Method = g.method - m.Endpoint = g.endpoint - return nil -} - -func (g *grpcCodec) ReadBody(v interface{}) error { - if f, ok := v.(*bytes.Frame); ok { - return g.s.RecvMsg(f) - } - return g.s.RecvMsg(v) -} - -func (g *grpcCodec) Write(m *codec.Message, v interface{}) error { - // if we don't have a body - if v != nil { - return g.s.SendMsg(v) - } - // write the body using the framing codec - return g.s.SendMsg(&bytes.Frame{Data: m.Body}) -} - -func (g *grpcCodec) Close() error { - return g.s.CloseSend() -} - -func (g *grpcCodec) String() string { - return g.c.Name() -} diff --git a/client/grpc/error.go b/client/grpc/error.go deleted file mode 100644 index 9685c611..00000000 --- a/client/grpc/error.go +++ /dev/null @@ -1,39 +0,0 @@ -package grpc - -import ( - "github.com/unistack-org/micro/v3/errors" - "google.golang.org/grpc/status" -) - -func microError(err error) error { - // no error - switch err { - case nil: - return nil - } - - if verr, ok := err.(*errors.Error); ok { - return verr - } - - // grpc error - s, ok := status.FromError(err) - if !ok { - return err - } - - // return first error from details - if details := s.Details(); len(details) > 0 { - if verr, ok := details[0].(error); ok { - return microError(verr) - } - } - - // try to decode micro *errors.Error - if e := errors.Parse(s.Message()); e.Code > 0 { - return e // actually a micro error - } - - // fallback - return errors.InternalServerError("go.micro.client", s.Message()) -} diff --git a/client/grpc/grpc.go b/client/grpc/grpc.go deleted file mode 100644 index 0e08221d..00000000 --- a/client/grpc/grpc.go +++ /dev/null @@ -1,743 +0,0 @@ -// Package grpc provides a gRPC client -package grpc - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "reflect" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/client" - raw "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/metadata" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/encoding" - gmetadata "google.golang.org/grpc/metadata" -) - -type grpcClient struct { - opts client.Options - codecs map[string]encoding.Codec - pool *pool - once atomic.Value - sync.RWMutex -} - -func init() { - encoding.RegisterCodec(wrapCodec{jsonCodec{}}) - encoding.RegisterCodec(wrapCodec{protoCodec{}}) - encoding.RegisterCodec(wrapCodec{bytesCodec{}}) -} - -// secure returns the dial option for whether its a secure or insecure connection -func (g *grpcClient) secure(addr string) grpc.DialOption { - // first we check if theres'a tls config - if g.opts.Context != nil { - if v := g.opts.Context.Value(tlsAuth{}); v != nil { - tls := v.(*tls.Config) - creds := credentials.NewTLS(tls) - // return tls config if it exists - return grpc.WithTransportCredentials(creds) - } - } - - // default config - tlsConfig := &tls.Config{} - defaultCreds := grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)) - - // check if the address is prepended with https - if strings.HasPrefix(addr, "https://") { - return defaultCreds - } - - // if no port is specified or port is 443 default to tls - _, port, err := net.SplitHostPort(addr) - // assuming with no port its going to be secured - if port == "443" { - return defaultCreds - } else if err != nil && strings.Contains(err.Error(), "missing port in address") { - return defaultCreds - } - - // other fallback to insecure - return grpc.WithInsecure() -} - -func (g *grpcClient) call(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error { - var header map[string]string - - if md, ok := metadata.FromContext(ctx); ok { - header = make(map[string]string, len(md)) - for k, v := range md { - header[strings.ToLower(k)] = v - } - } else { - header = make(map[string]string) - } - - // set timeout in nanoseconds - header["timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) - // set the content type for the request - header["x-content-type"] = req.ContentType() - - md := gmetadata.New(header) - ctx = gmetadata.NewOutgoingContext(ctx, md) - - cf, err := g.newGRPCCodec(req.ContentType()) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - maxRecvMsgSize := g.maxRecvMsgSizeValue() - maxSendMsgSize := g.maxSendMsgSizeValue() - - var grr error - - gctx, cancel := context.WithTimeout(ctx, opts.DialTimeout) - defer cancel() - - grpcDialOptions := []grpc.DialOption{ - g.secure(addr), - grpc.WithDefaultCallOptions( - grpc.MaxCallRecvMsgSize(maxRecvMsgSize), - grpc.MaxCallSendMsgSize(maxSendMsgSize), - ), - } - - if opts := g.getGrpcDialOptions(); opts != nil { - grpcDialOptions = append(grpcDialOptions, opts...) - } - - cc, err := g.pool.getConn(gctx, addr, grpcDialOptions...) - if err != nil { - return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) - } - defer func() { - // defer execution of release - g.pool.release(addr, cc, grr) - }() - - ch := make(chan error, 1) - - go func() { - grpcCallOptions := []grpc.CallOption{ - grpc.ForceCodec(cf), - grpc.CallContentSubtype(cf.Name())} - if opts := g.getGrpcCallOptions(); opts != nil { - grpcCallOptions = append(grpcCallOptions, opts...) - } - err := cc.Invoke(ctx, methodToGRPC(req.Service(), req.Endpoint()), req.Body(), rsp, grpcCallOptions...) - ch <- microError(err) - }() - - select { - case err := <-ch: - grr = err - case <-ctx.Done(): - grr = errors.Timeout("go.micro.client", "%v", ctx.Err()) - } - - return grr -} - -func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error { - var header map[string]string - - if md, ok := metadata.FromContext(ctx); ok { - header = make(map[string]string, len(md)) - for k, v := range md { - header[k] = v - } - } else { - header = make(map[string]string) - } - - // set timeout in nanoseconds - if opts.StreamTimeout > time.Duration(0) { - header["timeout"] = fmt.Sprintf("%d", opts.StreamTimeout) - } - // set the content type for the request - header["x-content-type"] = req.ContentType() - - md := gmetadata.New(header) - ctx = gmetadata.NewOutgoingContext(ctx, md) - - cf, err := g.newGRPCCodec(req.ContentType()) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - var dialCtx context.Context - var cancel context.CancelFunc - if opts.DialTimeout >= 0 { - dialCtx, cancel = context.WithTimeout(ctx, opts.DialTimeout) - } else { - dialCtx, cancel = context.WithCancel(ctx) - } - defer cancel() - - wc := wrapCodec{cf} - - grpcDialOptions := []grpc.DialOption{ - g.secure(addr), - } - - if opts := g.getGrpcDialOptions(); opts != nil { - grpcDialOptions = append(grpcDialOptions, opts...) - } - - cc, err := grpc.DialContext(dialCtx, addr, grpcDialOptions...) - if err != nil { - return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) - } - - desc := &grpc.StreamDesc{ - StreamName: req.Service() + req.Endpoint(), - ClientStreams: true, - ServerStreams: true, - } - - grpcCallOptions := []grpc.CallOption{ - grpc.ForceCodec(wc), - grpc.CallContentSubtype(cf.Name()), - } - if opts := g.getGrpcCallOptions(); opts != nil { - grpcCallOptions = append(grpcCallOptions, opts...) - } - - // create a new cancelling context - newCtx, cancel := context.WithCancel(ctx) - - st, err := cc.NewStream(newCtx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...) - if err != nil { - // we need to cleanup as we dialled and created a context - // cancel the context - cancel() - // close the connection - cc.Close() - // now return the error - return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err)) - } - - codec := &grpcCodec{ - s: st, - c: wc, - } - - // set request codec - if r, ok := req.(*grpcRequest); ok { - r.codec = codec - } - - // setup the stream response - stream := &grpcStream{ - ClientStream: st, - context: ctx, - request: req, - response: &response{ - conn: cc, - stream: st, - codec: cf, - gcodec: codec, - }, - conn: cc, - cancel: cancel, - } - - // set the stream as the response - val := reflect.ValueOf(rsp).Elem() - val.Set(reflect.ValueOf(stream).Elem()) - return nil -} - -func (g *grpcClient) poolMaxStreams() int { - if g.opts.Context == nil { - return DefaultPoolMaxStreams - } - v := g.opts.Context.Value(poolMaxStreams{}) - if v == nil { - return DefaultPoolMaxStreams - } - return v.(int) -} - -func (g *grpcClient) poolMaxIdle() int { - if g.opts.Context == nil { - return DefaultPoolMaxIdle - } - v := g.opts.Context.Value(poolMaxIdle{}) - if v == nil { - return DefaultPoolMaxIdle - } - return v.(int) -} - -func (g *grpcClient) maxRecvMsgSizeValue() int { - if g.opts.Context == nil { - return DefaultMaxRecvMsgSize - } - v := g.opts.Context.Value(maxRecvMsgSizeKey{}) - if v == nil { - return DefaultMaxRecvMsgSize - } - return v.(int) -} - -func (g *grpcClient) maxSendMsgSizeValue() int { - if g.opts.Context == nil { - return DefaultMaxSendMsgSize - } - v := g.opts.Context.Value(maxSendMsgSizeKey{}) - if v == nil { - return DefaultMaxSendMsgSize - } - return v.(int) -} - -func (g *grpcClient) newGRPCCodec(contentType string) (encoding.Codec, error) { - g.RLock() - defer g.RUnlock() - - if c, ok := g.codecs[contentType]; ok { - return wrapCodec{c}, nil - } - - return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType) -} - -func (g *grpcClient) Init(opts ...client.Option) error { - size := g.opts.PoolSize - ttl := g.opts.PoolTTL - - for _, o := range opts { - o(&g.opts) - } - - // update pool configuration if the options changed - if size != g.opts.PoolSize || ttl != g.opts.PoolTTL { - g.pool.Lock() - g.pool.size = g.opts.PoolSize - g.pool.ttl = int64(g.opts.PoolTTL.Seconds()) - g.pool.Unlock() - } - - return nil -} - -func (g *grpcClient) Options() client.Options { - return g.opts -} - -func (g *grpcClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message { - return newGRPCEvent(topic, msg, g.opts.ContentType, opts...) -} - -func (g *grpcClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request { - return newGRPCRequest(service, method, req, g.opts.ContentType, reqOpts...) -} - -func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { - if req == nil { - return errors.InternalServerError("go.micro.client", "req is nil") - } else if rsp == nil { - return errors.InternalServerError("go.micro.client", "rsp is nil") - } - // make a copy of call opts - callOpts := g.opts.CallOptions - for _, opt := range opts { - opt(&callOpts) - } - - // check if we already have a deadline - d, ok := ctx.Deadline() - if !ok { - // no deadline so we create a new one - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout) - defer cancel() - } else { - // got a deadline so no need to setup context - // but we need to set the timeout we pass along - opt := client.WithRequestTimeout(time.Until(d)) - opt(&callOpts) - } - - // should we noop right here? - select { - case <-ctx.Done(): - return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) - default: - } - - // make copy of call method - gcall := g.call - - // wrap the call in reverse - for i := len(callOpts.CallWrappers); i > 0; i-- { - gcall = callOpts.CallWrappers[i-1](gcall) - } - - // use the router passed as a call option, or fallback to the rpc clients router - if callOpts.Router == nil { - callOpts.Router = g.opts.Router - } - - if callOpts.Selector == nil { - callOpts.Selector = g.opts.Selector - } - - // inject proxy address - // TODO: don't even bother using Lookup/Select in this case - if len(g.opts.Proxy) > 0 { - callOpts.Address = []string{g.opts.Proxy} - } - - // lookup the route to send the reques to - // TODO apply any filtering here - routes, err := g.opts.Lookup(ctx, req, callOpts) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - // balance the list of nodes - next, err := callOpts.Selector.Select(routes) - if err != nil { - return err - } - - // return errors.New("go.micro.client", "request timeout", 408) - call := func(i int) error { - // call backoff first. Someone may want an initial start delay - t, err := callOpts.Backoff(ctx, req, i) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - // only sleep if greater than 0 - if t.Seconds() > 0 { - time.Sleep(t) - } - - // get the next node - node := next() - - // make the call - err = gcall(ctx, node, req, rsp, callOpts) - - // record the result of the call to inform future routing decisions - g.opts.Selector.Record(node, err) - - // try and transform the error to a go-micro error - if verr, ok := err.(*errors.Error); ok { - return verr - } - - return err - } - - ch := make(chan error, callOpts.Retries+1) - var gerr error - - for i := 0; i <= callOpts.Retries; i++ { - go func(i int) { - ch <- call(i) - }(i) - - select { - case <-ctx.Done(): - return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) - case err := <-ch: - // if the call succeeded lets bail early - if err == nil { - return nil - } - - retry, rerr := callOpts.Retry(ctx, req, i, err) - if rerr != nil { - return rerr - } - - if !retry { - return err - } - - gerr = err - } - } - - return gerr -} - -func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { - // make a copy of call opts - callOpts := g.opts.CallOptions - for _, opt := range opts { - opt(&callOpts) - } - - // #200 - streams shouldn't have a request timeout set on the context - - // should we noop right here? - select { - case <-ctx.Done(): - return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) - default: - } - - // make a copy of stream - gstream := g.stream - - // wrap the call in reverse - for i := len(callOpts.CallWrappers); i > 0; i-- { - gstream = callOpts.CallWrappers[i-1](gstream) - } - - // use the router passed as a call option, or fallback to the rpc clients router - if callOpts.Router == nil { - callOpts.Router = g.opts.Router - } - - if callOpts.Selector == nil { - callOpts.Selector = g.opts.Selector - } - - // inject proxy address - // TODO: don't even bother using Lookup/Select in this case - if len(g.opts.Proxy) > 0 { - callOpts.Address = []string{g.opts.Proxy} - } - - // lookup the route to send the reques to - // TODO: move to internal lookup func - routes, err := g.opts.Lookup(ctx, req, callOpts) - if err != nil { - return nil, errors.InternalServerError("go.micro.client", err.Error()) - } - - // balance the list of nodes - next, err := callOpts.Selector.Select(routes) - if err != nil { - return nil, err - } - - call := func(i int) (client.Stream, error) { - // call backoff first. Someone may want an initial start delay - t, err := callOpts.Backoff(ctx, req, i) - if err != nil { - return nil, errors.InternalServerError("go.micro.client", err.Error()) - } - - // only sleep if greater than 0 - if t.Seconds() > 0 { - time.Sleep(t) - } - - // get the next node - node := next() - - // make the call - stream := &grpcStream{} - err = g.stream(ctx, node, req, stream, callOpts) - - // record the result of the call to inform future routing decisions - g.opts.Selector.Record(node, err) - - // try and transform the error to a go-micro error - if verr, ok := err.(*errors.Error); ok { - return nil, verr - } - - g.opts.Selector.Record(node, err) - return stream, err - } - - type response struct { - stream client.Stream - err error - } - - ch := make(chan response, callOpts.Retries+1) - var grr error - - for i := 0; i <= callOpts.Retries; i++ { - go func(i int) { - s, err := call(i) - ch <- response{s, err} - }(i) - - select { - case <-ctx.Done(): - return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) - case rsp := <-ch: - // if the call succeeded lets bail early - if rsp.err == nil { - return rsp.stream, nil - } - - retry, rerr := callOpts.Retry(ctx, req, i, grr) - if rerr != nil { - return nil, rerr - } - - if !retry { - return nil, rsp.err - } - - grr = rsp.err - } - } - - return nil, grr -} - -func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error { - var options client.PublishOptions - var body []byte - - // fail early on connect error - if !g.once.Load().(bool) { - if err := g.opts.Broker.Connect(); err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - g.once.Store(true) - } - - for _, o := range opts { - o(&options) - } - - md, ok := metadata.FromContext(ctx) - if !ok { - md = make(map[string]string) - } - md["Content-Type"] = p.ContentType() - md["Micro-Topic"] = p.Topic() - - // passed in raw data - if d, ok := p.Payload().(*raw.Frame); ok { - body = d.Data - } else { - // use codec for payload - cf, err := g.newGRPCCodec(p.ContentType()) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - // set the body - b, err := cf.Marshal(p.Payload()) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - body = b - } - - topic := p.Topic() - - // get the exchange - if len(options.Exchange) > 0 { - topic = options.Exchange - } - - return g.opts.Broker.Publish(topic, &broker.Message{ - Header: md, - Body: body, - }, broker.PublishContext(options.Context)) -} - -func (g *grpcClient) String() string { - return "grpc" -} - -func (g *grpcClient) getGrpcDialOptions() []grpc.DialOption { - if g.opts.CallOptions.Context == nil { - return nil - } - - v := g.opts.CallOptions.Context.Value(grpcDialOptions{}) - - if v == nil { - return nil - } - - opts, ok := v.([]grpc.DialOption) - - if !ok { - return nil - } - - return opts -} - -func (g *grpcClient) getGrpcCallOptions() []grpc.CallOption { - if g.opts.CallOptions.Context == nil { - return nil - } - - v := g.opts.CallOptions.Context.Value(grpcCallOptions{}) - - if v == nil { - return nil - } - - opts, ok := v.([]grpc.CallOption) - - if !ok { - return nil - } - - return opts -} - -func newClient(opts ...client.Option) client.Client { - options := client.NewOptions() - // default content type for grpc - options.ContentType = "application/grpc+proto" - - for _, o := range opts { - o(&options) - } - - rc := &grpcClient{ - opts: options, - } - rc.once.Store(false) - - rc.pool = newPool(options.PoolSize, options.PoolTTL, rc.poolMaxIdle(), rc.poolMaxStreams()) - - c := client.Client(rc) - - // wrap in reverse - for i := len(options.Wrappers); i > 0; i-- { - c = options.Wrappers[i-1](c) - } - - rc.codecs = make(map[string]encoding.Codec, len(defaultGRPCCodecs)) - for k, v := range defaultGRPCCodecs { - rc.codecs[k] = v - } - - var codecs map[string]encoding.Codec - if rc.opts.Context != nil { - if v := rc.opts.Context.Value(codecsKey{}); v != nil { - codecs = v.(map[string]encoding.Codec) - } - } - - for k, v := range codecs { - rc.codecs[k] = v - } - - return c -} - -func NewClient(opts ...client.Option) client.Client { - return newClient(opts...) -} diff --git a/client/grpc/grpc_pool.go b/client/grpc/grpc_pool.go deleted file mode 100644 index b9a340f3..00000000 --- a/client/grpc/grpc_pool.go +++ /dev/null @@ -1,216 +0,0 @@ -package grpc - -import ( - "context" - "sync" - "time" - - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" -) - -type pool struct { - size int - ttl int64 - - // max streams on a *poolConn - maxStreams int - // max idle conns - maxIdle int - - sync.Mutex - conns map[string]*streamsPool -} - -type streamsPool struct { - // head of list - head *poolConn - // busy conns list - busy *poolConn - // the siza of list - count int - // idle conn - idle int -} - -type poolConn struct { - // grpc conn - *grpc.ClientConn - err error - addr string - - // pool and streams pool - pool *pool - sp *streamsPool - streams int - created int64 - - // list - pre *poolConn - next *poolConn - in bool -} - -func newPool(size int, ttl time.Duration, idle int, ms int) *pool { - if ms <= 0 { - ms = 1 - } - if idle < 0 { - idle = 0 - } - return &pool{ - size: size, - ttl: int64(ttl.Seconds()), - maxStreams: ms, - maxIdle: idle, - conns: make(map[string]*streamsPool), - } -} - -func (p *pool) getConn(ctx context.Context, addr string, opts ...grpc.DialOption) (*poolConn, error) { - now := time.Now().Unix() - p.Lock() - sp, ok := p.conns[addr] - if !ok { - sp = &streamsPool{head: &poolConn{}, busy: &poolConn{}, count: 0, idle: 0} - p.conns[addr] = sp - } - // while we have conns check streams and then return one - // otherwise we'll create a new conn - conn := sp.head.next - for conn != nil { - // check conn state - // https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md - switch conn.GetState() { - case connectivity.Connecting: - conn = conn.next - continue - case connectivity.Shutdown: - next := conn.next - if conn.streams == 0 { - removeConn(conn) - sp.idle-- - } - conn = next - continue - case connectivity.TransientFailure: - next := conn.next - if conn.streams == 0 { - removeConn(conn) - conn.ClientConn.Close() - sp.idle-- - } - conn = next - continue - case connectivity.Ready: - case connectivity.Idle: - } - // a old conn - if now-conn.created > p.ttl { - next := conn.next - if conn.streams == 0 { - removeConn(conn) - conn.ClientConn.Close() - sp.idle-- - } - conn = next - continue - } - // a busy conn - if conn.streams >= p.maxStreams { - next := conn.next - removeConn(conn) - addConnAfter(conn, sp.busy) - conn = next - continue - } - // a idle conn - if conn.streams == 0 { - sp.idle-- - } - // a good conn - conn.streams++ - p.Unlock() - return conn, nil - } - p.Unlock() - - // create new conn - cc, err := grpc.DialContext(ctx, addr, opts...) - if err != nil { - return nil, err - } - conn = &poolConn{cc, nil, addr, p, sp, 1, time.Now().Unix(), nil, nil, false} - - // add conn to streams pool - p.Lock() - if sp.count < p.size { - addConnAfter(conn, sp.head) - } - p.Unlock() - - return conn, nil -} - -func (p *pool) release(addr string, conn *poolConn, err error) { - p.Lock() - p, sp, created := conn.pool, conn.sp, conn.created - // try to add conn - if !conn.in && sp.count < p.size { - addConnAfter(conn, sp.head) - } - if !conn.in { - p.Unlock() - conn.ClientConn.Close() - return - } - // a busy conn - if conn.streams >= p.maxStreams { - removeConn(conn) - addConnAfter(conn, sp.head) - } - conn.streams-- - // if streams == 0, we can do something - if conn.streams == 0 { - // 1. it has errored - // 2. too many idle conn or - // 3. conn is too old - now := time.Now().Unix() - if err != nil || sp.idle >= p.maxIdle || now-created > p.ttl { - removeConn(conn) - p.Unlock() - conn.ClientConn.Close() - return - } - sp.idle++ - } - p.Unlock() -} - -func (conn *poolConn) Close() { - conn.pool.release(conn.addr, conn, conn.err) -} - -func removeConn(conn *poolConn) { - if conn.pre != nil { - conn.pre.next = conn.next - } - if conn.next != nil { - conn.next.pre = conn.pre - } - conn.pre = nil - conn.next = nil - conn.in = false - conn.sp.count-- -} - -func addConnAfter(conn *poolConn, after *poolConn) { - conn.next = after.next - conn.pre = after - if after.next != nil { - after.next.pre = conn - } - after.next = conn - conn.in = true - conn.sp.count++ -} diff --git a/client/grpc/grpc_pool_test.go b/client/grpc/grpc_pool_test.go deleted file mode 100644 index c5cff92f..00000000 --- a/client/grpc/grpc_pool_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package grpc - -import ( - "context" - "net" - "testing" - "time" - - "google.golang.org/grpc" - pgrpc "google.golang.org/grpc" - pb "google.golang.org/grpc/examples/helloworld/helloworld" -) - -func testPool(t *testing.T, size int, ttl time.Duration, idle int, ms int) { - // setup server - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("failed to listen: %v", err) - } - defer l.Close() - - s := pgrpc.NewServer() - pb.RegisterGreeterServer(s, &greeterServer{}) - - go s.Serve(l) - defer s.Stop() - ctx := context.Background() - - // zero pool - p := newPool(size, ttl, idle, ms) - - for i := 0; i < 10; i++ { - // get a conn - cc, err := p.getConn(ctx, l.Addr().String(), grpc.WithInsecure()) - if err != nil { - t.Fatal(err) - } - - rsp := pb.HelloReply{} - - err = cc.Invoke(context.TODO(), "/helloworld.Greeter/SayHello", &pb.HelloRequest{Name: "John"}, &rsp) - if err != nil { - t.Fatal(err) - } - - if rsp.Message != "Hello John" { - t.Fatalf("Got unexpected response %v", rsp.Message) - } - - // release the conn - p.release(l.Addr().String(), cc, nil) - - p.Lock() - if i := p.conns[l.Addr().String()].count; i > size { - p.Unlock() - t.Fatalf("pool size %d is greater than expected %d", i, size) - } - p.Unlock() - } -} - -func TestGRPCPool(t *testing.T) { - testPool(t, 0, time.Minute, 10, 2) - testPool(t, 2, time.Minute, 10, 1) -} diff --git a/client/grpc/grpc_test.go b/client/grpc/grpc_test.go deleted file mode 100644 index 7493fade..00000000 --- a/client/grpc/grpc_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package grpc - -import ( - "context" - "net" - "testing" - - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/memory" - "github.com/unistack-org/micro/v3/router" - regRouter "github.com/unistack-org/micro/v3/router/registry" - pgrpc "google.golang.org/grpc" - pb "google.golang.org/grpc/examples/helloworld/helloworld" -) - -// server is used to implement helloworld.GreeterServer. -type greeterServer struct{} - -// SayHello implements helloworld.GreeterServer -func (g *greeterServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { - if in.Name == "Error" { - return nil, &errors.Error{Id: "1", Code: 99, Detail: "detail"} - } - return &pb.HelloReply{Message: "Hello " + in.Name}, nil -} - -func TestGRPCClient(t *testing.T) { - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("failed to listen: %v", err) - } - defer l.Close() - - s := pgrpc.NewServer() - pb.RegisterGreeterServer(s, &greeterServer{}) - - go s.Serve(l) - defer s.Stop() - - // create mock registry - r := memory.NewRegistry() - - // register service - r.Register(®istry.Service{ - Name: "helloworld", - Version: "test", - Nodes: []*registry.Node{ - { - Id: "test-1", - Address: l.Addr().String(), - Metadata: map[string]string{ - "protocol": "grpc", - }, - }, - }, - }) - - // create router - rtr := regRouter.NewRouter(router.Registry(r)) - - // create client - c := NewClient(client.Router(rtr)) - - testMethods := []string{ - "/helloworld.Greeter/SayHello", - "Greeter.SayHello", - } - - for _, method := range testMethods { - req := c.NewRequest("helloworld", method, &pb.HelloRequest{ - Name: "John", - }) - - rsp := pb.HelloReply{} - - err = c.Call(context.TODO(), req, &rsp) - if err != nil { - t.Fatal(err) - } - - if rsp.Message != "Hello John" { - t.Fatalf("Got unexpected response %v", rsp.Message) - } - } - - req := c.NewRequest("helloworld", "/helloworld.Greeter/SayHello", &pb.HelloRequest{ - Name: "Error", - }) - - rsp := pb.HelloReply{} - - err = c.Call(context.TODO(), req, &rsp) - if err == nil { - t.Fatal("nil error received") - } - - verr, ok := err.(*errors.Error) - if !ok { - t.Fatalf("invalid error received %#+v\n", err) - } - - if verr.Code != 99 && verr.Id != "1" && verr.Detail != "detail" { - t.Fatalf("invalid error received %#+v\n", verr) - } - -} diff --git a/client/grpc/message.go b/client/grpc/message.go deleted file mode 100644 index 7b477426..00000000 --- a/client/grpc/message.go +++ /dev/null @@ -1,40 +0,0 @@ -package grpc - -import ( - "github.com/unistack-org/micro/v3/client" -) - -type grpcEvent struct { - topic string - contentType string - payload interface{} -} - -func newGRPCEvent(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message { - var options client.MessageOptions - for _, o := range opts { - o(&options) - } - - if len(options.ContentType) > 0 { - contentType = options.ContentType - } - - return &grpcEvent{ - payload: payload, - topic: topic, - contentType: contentType, - } -} - -func (g *grpcEvent) ContentType() string { - return g.contentType -} - -func (g *grpcEvent) Topic() string { - return g.topic -} - -func (g *grpcEvent) Payload() interface{} { - return g.payload -} diff --git a/client/grpc/options.go b/client/grpc/options.go deleted file mode 100644 index a4d55117..00000000 --- a/client/grpc/options.go +++ /dev/null @@ -1,131 +0,0 @@ -// Package grpc provides a gRPC options -package grpc - -import ( - "context" - "crypto/tls" - - "github.com/unistack-org/micro/v3/client" - "google.golang.org/grpc" - "google.golang.org/grpc/encoding" -) - -var ( - // DefaultPoolMaxStreams maximum streams on a connectioin - // (20) - DefaultPoolMaxStreams = 20 - - // DefaultPoolMaxIdle maximum idle conns of a pool - // (50) - DefaultPoolMaxIdle = 50 - - // DefaultMaxRecvMsgSize maximum message that client can receive - // (4 MB). - DefaultMaxRecvMsgSize = 1024 * 1024 * 4 - - // DefaultMaxSendMsgSize maximum message that client can send - // (4 MB). - DefaultMaxSendMsgSize = 1024 * 1024 * 4 -) - -type poolMaxStreams struct{} -type poolMaxIdle struct{} -type codecsKey struct{} -type tlsAuth struct{} -type maxRecvMsgSizeKey struct{} -type maxSendMsgSizeKey struct{} -type grpcDialOptions struct{} -type grpcCallOptions struct{} - -// maximum streams on a connectioin -func PoolMaxStreams(n int) client.Option { - return func(o *client.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, poolMaxStreams{}, n) - } -} - -// maximum idle conns of a pool -func PoolMaxIdle(d int) client.Option { - return func(o *client.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, poolMaxIdle{}, d) - } -} - -// gRPC Codec to be used to encode/decode requests for a given content type -func Codec(contentType string, c encoding.Codec) client.Option { - return func(o *client.Options) { - codecs := make(map[string]encoding.Codec) - if o.Context == nil { - o.Context = context.Background() - } - if v := o.Context.Value(codecsKey{}); v != nil { - codecs = v.(map[string]encoding.Codec) - } - codecs[contentType] = c - o.Context = context.WithValue(o.Context, codecsKey{}, codecs) - } -} - -// AuthTLS should be used to setup a secure authentication using TLS -func AuthTLS(t *tls.Config) client.Option { - return func(o *client.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, tlsAuth{}, t) - } -} - -// -// MaxRecvMsgSize set the maximum size of message that client can receive. -// -func MaxRecvMsgSize(s int) client.Option { - return func(o *client.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, maxRecvMsgSizeKey{}, s) - } -} - -// -// MaxSendMsgSize set the maximum size of message that client can send. -// -func MaxSendMsgSize(s int) client.Option { - return func(o *client.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, maxSendMsgSizeKey{}, s) - } -} - -// -// DialOptions to be used to configure gRPC dial options -// -func DialOptions(opts ...grpc.DialOption) client.CallOption { - return func(o *client.CallOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, grpcDialOptions{}, opts) - } -} - -// -// CallOptions to be used to configure gRPC call options -// -func CallOptions(opts ...grpc.CallOption) client.CallOption { - return func(o *client.CallOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, grpcCallOptions{}, opts) - } -} diff --git a/client/grpc/request.go b/client/grpc/request.go deleted file mode 100644 index 91ac35b8..00000000 --- a/client/grpc/request.go +++ /dev/null @@ -1,87 +0,0 @@ -package grpc - -import ( - "fmt" - "strings" - - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" -) - -type grpcRequest struct { - service string - method string - contentType string - request interface{} - opts client.RequestOptions - codec codec.Codec -} - -// service Struct.Method /service.Struct/Method -func methodToGRPC(service, method string) string { - // no method or already grpc method - if len(method) == 0 || method[0] == '/' { - return method - } - - // assume method is Foo.Bar - mParts := strings.Split(method, ".") - if len(mParts) != 2 { - return method - } - - if len(service) == 0 { - return fmt.Sprintf("/%s/%s", mParts[0], mParts[1]) - } - - // return /pkg.Foo/Bar - return fmt.Sprintf("/%s.%s/%s", service, mParts[0], mParts[1]) -} - -func newGRPCRequest(service, method string, request interface{}, contentType string, reqOpts ...client.RequestOption) client.Request { - var opts client.RequestOptions - for _, o := range reqOpts { - o(&opts) - } - - // set the content-type specified - if len(opts.ContentType) > 0 { - contentType = opts.ContentType - } - - return &grpcRequest{ - service: service, - method: method, - request: request, - contentType: contentType, - opts: opts, - } -} - -func (g *grpcRequest) ContentType() string { - return g.contentType -} - -func (g *grpcRequest) Service() string { - return g.service -} - -func (g *grpcRequest) Method() string { - return g.method -} - -func (g *grpcRequest) Endpoint() string { - return g.method -} - -func (g *grpcRequest) Codec() codec.Writer { - return g.codec -} - -func (g *grpcRequest) Body() interface{} { - return g.request -} - -func (g *grpcRequest) Stream() bool { - return g.opts.Stream -} diff --git a/client/grpc/request_test.go b/client/grpc/request_test.go deleted file mode 100644 index c73d675b..00000000 --- a/client/grpc/request_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package grpc - -import ( - "testing" -) - -func TestMethodToGRPC(t *testing.T) { - testData := []struct { - service string - method string - expect string - }{ - { - "helloworld", - "Greeter.SayHello", - "/helloworld.Greeter/SayHello", - }, - { - "helloworld", - "/helloworld.Greeter/SayHello", - "/helloworld.Greeter/SayHello", - }, - { - "", - "/helloworld.Greeter/SayHello", - "/helloworld.Greeter/SayHello", - }, - { - "", - "Greeter.SayHello", - "/Greeter/SayHello", - }, - } - - for _, d := range testData { - method := methodToGRPC(d.service, d.method) - if method != d.expect { - t.Fatalf("expected %s got %s", d.expect, method) - } - } -} diff --git a/client/grpc/response.go b/client/grpc/response.go deleted file mode 100644 index 633806bf..00000000 --- a/client/grpc/response.go +++ /dev/null @@ -1,44 +0,0 @@ -package grpc - -import ( - "strings" - - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/codec/bytes" - "google.golang.org/grpc" - "google.golang.org/grpc/encoding" -) - -type response struct { - conn *grpc.ClientConn - stream grpc.ClientStream - codec encoding.Codec - gcodec codec.Codec -} - -// Read the response -func (r *response) Codec() codec.Reader { - return r.gcodec -} - -// read the header -func (r *response) Header() map[string]string { - md, err := r.stream.Header() - if err != nil { - return map[string]string{} - } - hdr := make(map[string]string, len(md)) - for k, v := range md { - hdr[k] = strings.Join(v, ",") - } - return hdr -} - -// Read the undecoded response -func (r *response) Read() ([]byte, error) { - f := &bytes.Frame{} - if err := r.gcodec.ReadBody(f); err != nil { - return nil, err - } - return f.Data, nil -} diff --git a/client/grpc/stream.go b/client/grpc/stream.go deleted file mode 100644 index 59f0614f..00000000 --- a/client/grpc/stream.go +++ /dev/null @@ -1,94 +0,0 @@ -package grpc - -import ( - "context" - "io" - "sync" - - "github.com/unistack-org/micro/v3/client" - "google.golang.org/grpc" -) - -// Implements the streamer interface -type grpcStream struct { - // embed so we can access if need be - grpc.ClientStream - - sync.RWMutex - closed bool - err error - conn *grpc.ClientConn - request client.Request - response client.Response - context context.Context - cancel func() -} - -func (g *grpcStream) Context() context.Context { - return g.context -} - -func (g *grpcStream) Request() client.Request { - return g.request -} - -func (g *grpcStream) Response() client.Response { - return g.response -} - -func (g *grpcStream) Send(msg interface{}) error { - if err := g.ClientStream.SendMsg(msg); err != nil { - g.setError(err) - return err - } - return nil -} - -func (g *grpcStream) Recv(msg interface{}) (err error) { - defer g.setError(err) - - if err = g.ClientStream.RecvMsg(msg); err != nil { - // #202 - inconsistent gRPC stream behavior - // the only way to tell if the stream is done is when we get a EOF on the Recv - // here we should close the underlying gRPC ClientConn - closeErr := g.Close() - if err == io.EOF && closeErr != nil { - err = closeErr - } - - return err - } - - return -} - -func (g *grpcStream) Error() error { - g.RLock() - defer g.RUnlock() - return g.err -} - -func (g *grpcStream) setError(e error) { - g.Lock() - g.err = e - g.Unlock() -} - -// Close the gRPC send stream -// #202 - inconsistent gRPC stream behavior -// The underlying gRPC stream should not be closed here since the -// stream should still be able to receive after this function call -// TODO: should the conn be closed in another way? -func (g *grpcStream) Close() error { - g.Lock() - defer g.Unlock() - - if g.closed { - return nil - } - // cancel the context - g.cancel() - g.closed = true - g.ClientStream.CloseSend() - return g.conn.Close() -} diff --git a/client/mucp/common_test.go b/client/mucp/common_test.go deleted file mode 100644 index c7287b23..00000000 --- a/client/mucp/common_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package mucp - -import ( - "github.com/unistack-org/micro/v3/registry" -) - -var ( - // mock data - testData = map[string][]*registry.Service{ - "foo": { - { - Name: "foo", - Version: "1.0.0", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.0-123", - Address: "localhost:9999", - Metadata: map[string]string{ - "protocol": "mucp", - }, - }, - { - Id: "foo-1.0.0-321", - Address: "localhost:9999", - Metadata: map[string]string{ - "protocol": "mucp", - }, - }, - }, - }, - { - Name: "foo", - Version: "1.0.1", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.1-321", - Address: "localhost:6666", - Metadata: map[string]string{ - "protocol": "mucp", - }, - }, - }, - }, - { - Name: "foo", - Version: "1.0.3", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.3-345", - Address: "localhost:8888", - Metadata: map[string]string{ - "protocol": "mucp", - }, - }, - }, - }, - }, - } -) diff --git a/client/mucp/mucp.go b/client/mucp/mucp.go deleted file mode 100644 index 0293384f..00000000 --- a/client/mucp/mucp.go +++ /dev/null @@ -1,626 +0,0 @@ -// Package mucp provides an mucp client -package mucp - -import ( - "context" - "fmt" - "sync/atomic" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" - raw "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/metadata" - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/util/buf" - "github.com/unistack-org/micro/v3/util/pool" -) - -type rpcClient struct { - once atomic.Value - opts client.Options - pool pool.Pool - seq uint64 -} - -// NewClient returns a new micro client interface -func NewClient(opt ...client.Option) client.Client { - opts := client.NewOptions(opt...) - - p := pool.NewPool( - pool.Size(opts.PoolSize), - pool.TTL(opts.PoolTTL), - pool.Transport(opts.Transport), - ) - - rc := &rpcClient{ - opts: opts, - pool: p, - seq: 0, - } - rc.once.Store(false) - - c := client.Client(rc) - - // wrap in reverse - for i := len(opts.Wrappers); i > 0; i-- { - c = opts.Wrappers[i-1](c) - } - - return c -} - -func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) { - if c, ok := r.opts.Codecs[contentType]; ok { - return c, nil - } - if cf, ok := DefaultCodecs[contentType]; ok { - return cf, nil - } - return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType) -} - -func (r *rpcClient) call(ctx context.Context, addr string, req client.Request, resp interface{}, opts client.CallOptions) error { - msg := &transport.Message{ - Header: make(map[string]string), - } - - md, ok := metadata.FromContext(ctx) - if ok { - for k, v := range md { - // don't copy Micro-Topic header, that used for pub/sub - // this fix case then client uses the same context that received in subscriber - if k == "Micro-Topic" { - continue - } - msg.Header[k] = v - } - } - - // set timeout in nanoseconds - msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) - // set the content type for the request - msg.Header["Content-Type"] = req.ContentType() - // set the accept header - msg.Header["Accept"] = req.ContentType() - - cf, err := r.newCodec(req.ContentType()) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - dOpts := []transport.DialOption{ - transport.WithStream(), - } - - if opts.DialTimeout >= 0 { - dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout)) - } - - c, err := r.pool.Get(addr, dOpts...) - if err != nil { - return errors.InternalServerError("go.micro.client", "connection error: %v", err) - } - - seq := atomic.AddUint64(&r.seq, 1) - 1 - codec := newRpcCodec(msg, c, cf, "") - - rsp := &rpcResponse{ - socket: c, - codec: codec, - } - - stream := &rpcStream{ - id: fmt.Sprintf("%v", seq), - context: ctx, - request: req, - response: rsp, - codec: codec, - closed: make(chan bool), - release: func(err error) { r.pool.Release(c, err) }, - sendEOS: false, - } - // close the stream on exiting this function - defer stream.Close() - - // wait for error response - ch := make(chan error, 1) - - go func() { - defer func() { - if r := recover(); r != nil { - ch <- errors.InternalServerError("go.micro.client", "panic recovered: %v", r) - } - }() - - // send request - if err := stream.Send(req.Body()); err != nil { - ch <- err - return - } - - // recv request - if err := stream.Recv(resp); err != nil { - ch <- err - return - } - - // success - ch <- nil - }() - - var grr error - - select { - case err := <-ch: - return err - case <-ctx.Done(): - grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err())) - } - - // set the stream error - if grr != nil { - stream.Lock() - stream.err = grr - stream.Unlock() - - return grr - } - - return nil -} - -func (r *rpcClient) stream(ctx context.Context, addr string, req client.Request, opts client.CallOptions) (client.Stream, error) { - msg := &transport.Message{ - Header: make(map[string]string), - } - - md, ok := metadata.FromContext(ctx) - if ok { - for k, v := range md { - msg.Header[k] = v - } - } - - // set timeout in nanoseconds - if opts.StreamTimeout > time.Duration(0) { - msg.Header["Timeout"] = fmt.Sprintf("%d", opts.StreamTimeout) - } - // set the content type for the request - msg.Header["Content-Type"] = req.ContentType() - // set the accept header - msg.Header["Accept"] = req.ContentType() - - cf, err := r.newCodec(req.ContentType()) - if err != nil { - return nil, errors.InternalServerError("go.micro.client", err.Error()) - } - - dOpts := []transport.DialOption{ - transport.WithStream(), - } - - if opts.DialTimeout >= 0 { - dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout)) - } - - c, err := r.opts.Transport.Dial(addr, dOpts...) - if err != nil { - return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err) - } - - // increment the sequence number - seq := atomic.AddUint64(&r.seq, 1) - 1 - id := fmt.Sprintf("%v", seq) - - // create codec with stream id - codec := newRpcCodec(msg, c, cf, id) - - rsp := &rpcResponse{ - socket: c, - codec: codec, - } - - // set request codec - if r, ok := req.(*rpcRequest); ok { - r.codec = codec - } - - stream := &rpcStream{ - id: id, - context: ctx, - request: req, - response: rsp, - codec: codec, - // used to close the stream - closed: make(chan bool), - // signal the end of stream, - sendEOS: true, - // release func - release: func(err error) { c.Close() }, - } - - // wait for error response - ch := make(chan error, 1) - - go func() { - // send the first message - ch <- stream.Send(req.Body()) - }() - - var grr error - - select { - case err := <-ch: - grr = err - case <-ctx.Done(): - grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err())) - } - - if grr != nil { - // set the error - stream.Lock() - stream.err = grr - stream.Unlock() - - // close the stream - stream.Close() - return nil, grr - } - - return stream, nil -} - -func (r *rpcClient) Init(opts ...client.Option) error { - size := r.opts.PoolSize - ttl := r.opts.PoolTTL - tr := r.opts.Transport - - for _, o := range opts { - o(&r.opts) - } - - // update pool configuration if the options changed - if size != r.opts.PoolSize || ttl != r.opts.PoolTTL || tr != r.opts.Transport { - // close existing pool - r.pool.Close() - // create new pool - r.pool = pool.NewPool( - pool.Size(r.opts.PoolSize), - pool.TTL(r.opts.PoolTTL), - pool.Transport(r.opts.Transport), - ) - } - - return nil -} - -func (r *rpcClient) Options() client.Options { - return r.opts -} - -func (r *rpcClient) Call(ctx context.Context, request client.Request, response interface{}, opts ...client.CallOption) error { - // make a copy of call opts - callOpts := r.opts.CallOptions - for _, opt := range opts { - opt(&callOpts) - } - - // check if we already have a deadline - if d, ok := ctx.Deadline(); !ok { - // no deadline so we create a new one - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout) - defer cancel() - } else { - // got a deadline so no need to setup context - // but we need to set the timeout we pass along - remaining := d.Sub(time.Now()) - client.WithRequestTimeout(remaining)(&callOpts) - } - - // should we noop right here? - select { - case <-ctx.Done(): - return errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err())) - default: - } - - // make copy of call method - rcall := r.call - - // wrap the call in reverse - for i := len(callOpts.CallWrappers); i > 0; i-- { - rcall = callOpts.CallWrappers[i-1](rcall) - } - - // use the router passed as a call option, or fallback to the rpc clients router - if callOpts.Router == nil { - callOpts.Router = r.opts.Router - } - - if callOpts.Selector == nil { - callOpts.Selector = r.opts.Selector - } - - // inject proxy address - // TODO: don't even bother using Lookup/Select in this case - if len(r.opts.Proxy) > 0 { - callOpts.Address = []string{r.opts.Proxy} - } - - // lookup the route to send the reques to - // TODO apply any filtering here - routes, err := r.opts.Lookup(ctx, request, callOpts) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - // balance the list of nodes - next, err := callOpts.Selector.Select(routes) - if err != nil { - return err - } - - // return errors.New("go.micro.client", "request timeout", 408) - call := func(i int) error { - // call backoff first. Someone may want an initial start delay - t, err := callOpts.Backoff(ctx, request, i) - if err != nil { - return errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error()) - } - - // only sleep if greater than 0 - if t.Seconds() > 0 { - time.Sleep(t) - } - - // get the next node - node := next() - - // make the call - err = rcall(ctx, node, request, response, callOpts) - - // record the result of the call to inform future routing decisions - r.opts.Selector.Record(node, err) - - return err - } - - // get the retries - retries := callOpts.Retries - - // disable retries when using a proxy - if len(r.opts.Proxy) > 0 { - retries = 0 - } - - ch := make(chan error, retries+1) - var gerr error - - for i := 0; i <= retries; i++ { - go func(i int) { - ch <- call(i) - }(i) - - select { - case <-ctx.Done(): - return errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err())) - case err := <-ch: - // if the call succeeded lets bail early - if err == nil { - return nil - } - - retry, rerr := callOpts.Retry(ctx, request, i, err) - if rerr != nil { - return rerr - } - - if !retry { - return err - } - - gerr = err - } - } - - return gerr -} - -func (r *rpcClient) Stream(ctx context.Context, request client.Request, opts ...client.CallOption) (client.Stream, error) { - // make a copy of call opts - callOpts := r.opts.CallOptions - for _, opt := range opts { - opt(&callOpts) - } - - // should we noop right here? - select { - case <-ctx.Done(): - return nil, errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err())) - default: - } - - // use the router passed as a call option, or fallback to the rpc clients router - if callOpts.Router == nil { - callOpts.Router = r.opts.Router - } - - if callOpts.Selector == nil { - callOpts.Selector = r.opts.Selector - } - - // inject proxy address - // TODO: don't even bother using Lookup/Select in this case - if len(r.opts.Proxy) > 0 { - callOpts.Address = []string{r.opts.Proxy} - } - - // lookup the route to send the reques to - // TODO apply any filtering here - routes, err := r.opts.Lookup(ctx, request, callOpts) - if err != nil { - return nil, errors.InternalServerError("go.micro.client", err.Error()) - } - - // balance the list of nodes - next, err := callOpts.Selector.Select(routes) - if err != nil { - return nil, err - } - - call := func(i int) (client.Stream, error) { - // call backoff first. Someone may want an initial start delay - t, err := callOpts.Backoff(ctx, request, i) - if err != nil { - return nil, errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error()) - } - - // only sleep if greater than 0 - if t.Seconds() > 0 { - time.Sleep(t) - } - - // get the next node - node := next() - - // perform the call - stream, err := r.stream(ctx, node, request, callOpts) - - // record the result of the call to inform future routing decisions - r.opts.Selector.Record(node, err) - - return stream, err - } - - type response struct { - stream client.Stream - err error - } - - // get the retries - retries := callOpts.Retries - - // disable retries when using a proxy - if len(r.opts.Proxy) > 0 { - retries = 0 - } - - ch := make(chan response, retries+1) - var grr error - - for i := 0; i <= retries; i++ { - go func(i int) { - s, err := call(i) - ch <- response{s, err} - }(i) - - select { - case <-ctx.Done(): - return nil, errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err())) - case rsp := <-ch: - // if the call succeeded lets bail early - if rsp.err == nil { - return rsp.stream, nil - } - - retry, rerr := callOpts.Retry(ctx, request, i, rsp.err) - if rerr != nil { - return nil, rerr - } - - if !retry { - return nil, rsp.err - } - - grr = rsp.err - } - } - - return nil, grr -} - -func (r *rpcClient) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error { - options := client.PublishOptions{ - Context: context.Background(), - } - for _, o := range opts { - o(&options) - } - - md, ok := metadata.FromContext(ctx) - if !ok { - md = make(map[string]string) - } - - id := uuid.New().String() - md["Content-Type"] = msg.ContentType() - md["Micro-Topic"] = msg.Topic() - md["Micro-Id"] = id - - // set the topic - topic := msg.Topic() - - // get the exchange - if len(options.Exchange) > 0 { - topic = options.Exchange - } - - // encode message body - cf, err := r.newCodec(msg.ContentType()) - if err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - var body []byte - - // passed in raw data - if d, ok := msg.Payload().(*raw.Frame); ok { - body = d.Data - } else { - // new buffer - b := buf.New(nil) - - if err := cf(b).Write(&codec.Message{ - Target: topic, - Type: codec.Event, - Header: map[string]string{ - "Micro-Id": id, - "Micro-Topic": msg.Topic(), - }, - }, msg.Payload()); err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - - // set the body - body = b.Bytes() - } - - if !r.once.Load().(bool) { - if err = r.opts.Broker.Connect(); err != nil { - return errors.InternalServerError("go.micro.client", err.Error()) - } - r.once.Store(true) - } - - return r.opts.Broker.Publish(topic, &broker.Message{ - Header: md, - Body: body, - }, broker.PublishContext(options.Context)) -} - -func (r *rpcClient) NewMessage(topic string, message interface{}, opts ...client.MessageOption) client.Message { - return newMessage(topic, message, r.opts.ContentType, opts...) -} - -func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...client.RequestOption) client.Request { - return newRequest(service, method, request, r.opts.ContentType, reqOpts...) -} - -func (r *rpcClient) String() string { - return "mucp" -} diff --git a/client/mucp/mucp_codec.go b/client/mucp/mucp_codec.go deleted file mode 100644 index 58b2d347..00000000 --- a/client/mucp/mucp_codec.go +++ /dev/null @@ -1,233 +0,0 @@ -package mucp - -import ( - "bytes" - errs "errors" - - "github.com/unistack-org/micro/v3/codec" - raw "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/codec/grpc" - "github.com/unistack-org/micro/v3/codec/json" - "github.com/unistack-org/micro/v3/codec/jsonrpc" - "github.com/unistack-org/micro/v3/codec/proto" - "github.com/unistack-org/micro/v3/codec/protorpc" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/transport" -) - -const ( - lastStreamResponseError = "EOS" -) - -// serverError represents an error that has been returned from -// the remote side of the RPC connection. -type serverError string - -func (e serverError) Error() string { - return string(e) -} - -// errShutdown holds the specific error for closing/closed connections -var ( - errShutdown = errs.New("connection is shut down") -) - -type rpcCodec struct { - client transport.Client - codec codec.Codec - - req *transport.Message - buf *readWriteCloser - - // signify if its a stream - stream string -} - -type readWriteCloser struct { - wbuf *bytes.Buffer - rbuf *bytes.Buffer -} - -var ( - DefaultContentType = "application/protobuf" - - DefaultCodecs = map[string]codec.NewCodec{ - "application/grpc": grpc.NewCodec, - "application/grpc+json": grpc.NewCodec, - "application/grpc+proto": grpc.NewCodec, - "application/protobuf": proto.NewCodec, - "application/json": json.NewCodec, - "application/json-rpc": jsonrpc.NewCodec, - "application/proto-rpc": protorpc.NewCodec, - "application/octet-stream": raw.NewCodec, - } -) - -func (rwc *readWriteCloser) Read(p []byte) (n int, err error) { - return rwc.rbuf.Read(p) -} - -func (rwc *readWriteCloser) Write(p []byte) (n int, err error) { - return rwc.wbuf.Write(p) -} - -func (rwc *readWriteCloser) Close() error { - rwc.rbuf.Reset() - rwc.wbuf.Reset() - return nil -} - -func getHeaders(m *codec.Message) { - set := func(v, hdr string) string { - if len(v) > 0 { - return v - } - return m.Header[hdr] - } - - // check error in header - m.Error = set(m.Error, "Micro-Error") - - // check endpoint in header - m.Endpoint = set(m.Endpoint, "Micro-Endpoint") - - // check method in header - m.Method = set(m.Method, "Micro-Method") - - // set the request id - m.Id = set(m.Id, "Micro-Id") -} - -func setHeaders(m *codec.Message, stream string) { - set := func(hdr, v string) { - if len(v) == 0 { - return - } - m.Header[hdr] = v - } - - set("Micro-Id", m.Id) - set("Micro-Service", m.Target) - set("Micro-Method", m.Method) - set("Micro-Endpoint", m.Endpoint) - set("Micro-Error", m.Error) - - if len(stream) > 0 { - set("Micro-Stream", stream) - } -} - -func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec { - rwc := &readWriteCloser{ - wbuf: bytes.NewBuffer(nil), - rbuf: bytes.NewBuffer(nil), - } - r := &rpcCodec{ - buf: rwc, - client: client, - codec: c(rwc), - req: req, - stream: stream, - } - return r -} - -func (c *rpcCodec) Write(m *codec.Message, body interface{}) error { - c.buf.wbuf.Reset() - - // create header - if m.Header == nil { - m.Header = map[string]string{} - } - - // copy original header - for k, v := range c.req.Header { - m.Header[k] = v - } - - // set the mucp headers - setHeaders(m, c.stream) - - // if body is bytes Frame don't encode - if body != nil { - if b, ok := body.(*raw.Frame); ok { - // set body - m.Body = b.Data - } else { - // write to codec - if err := c.codec.Write(m, body); err != nil { - return errors.InternalServerError("go.micro.client.codec", err.Error()) - } - // set body - m.Body = c.buf.wbuf.Bytes() - } - } - - // create new transport message - msg := transport.Message{ - Header: m.Header, - Body: m.Body, - } - - // send the request - if err := c.client.Send(&msg); err != nil { - return errors.InternalServerError("go.micro.client.transport", err.Error()) - } - - return nil -} - -func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error { - var tm transport.Message - - // read message from transport - if err := c.client.Recv(&tm); err != nil { - return errors.InternalServerError("go.micro.client.transport", err.Error()) - } - - c.buf.rbuf.Reset() - c.buf.rbuf.Write(tm.Body) - - // set headers from transport - m.Header = tm.Header - - // read header - err := c.codec.ReadHeader(m, r) - - // get headers - getHeaders(m) - - // return header error - if err != nil { - return errors.InternalServerError("go.micro.client.codec", err.Error()) - } - - return nil -} - -func (c *rpcCodec) ReadBody(b interface{}) error { - // read body - // read raw data - if v, ok := b.(*raw.Frame); ok { - v.Data = c.buf.rbuf.Bytes() - return nil - } - - if err := c.codec.ReadBody(b); err != nil { - return errors.InternalServerError("go.micro.client.codec", err.Error()) - } - return nil -} - -func (c *rpcCodec) Close() error { - c.buf.Close() - c.codec.Close() - if err := c.client.Close(); err != nil { - return errors.InternalServerError("go.micro.client.transport", err.Error()) - } - return nil -} - -func (c *rpcCodec) String() string { - return "rpc" -} diff --git a/client/mucp/mucp_message.go b/client/mucp/mucp_message.go deleted file mode 100644 index 8b6aabdd..00000000 --- a/client/mucp/mucp_message.go +++ /dev/null @@ -1,40 +0,0 @@ -package mucp - -import ( - "github.com/unistack-org/micro/v3/client" -) - -type message struct { - topic string - contentType string - payload interface{} -} - -func newMessage(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message { - var options client.MessageOptions - for _, o := range opts { - o(&options) - } - - if len(options.ContentType) > 0 { - contentType = options.ContentType - } - - return &message{ - payload: payload, - topic: topic, - contentType: contentType, - } -} - -func (m *message) ContentType() string { - return m.contentType -} - -func (m *message) Topic() string { - return m.topic -} - -func (m *message) Payload() interface{} { - return m.payload -} diff --git a/client/mucp/mucp_request.go b/client/mucp/mucp_request.go deleted file mode 100644 index 8ff9ff85..00000000 --- a/client/mucp/mucp_request.go +++ /dev/null @@ -1,66 +0,0 @@ -package mucp - -import ( - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" -) - -type rpcRequest struct { - service string - method string - endpoint string - contentType string - codec codec.Codec - body interface{} - opts client.RequestOptions -} - -func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...client.RequestOption) client.Request { - var opts client.RequestOptions - - for _, o := range reqOpts { - o(&opts) - } - - // set the content-type specified - if len(opts.ContentType) > 0 { - contentType = opts.ContentType - } - - return &rpcRequest{ - service: service, - method: endpoint, - endpoint: endpoint, - body: request, - contentType: contentType, - opts: opts, - } -} - -func (r *rpcRequest) ContentType() string { - return r.contentType -} - -func (r *rpcRequest) Service() string { - return r.service -} - -func (r *rpcRequest) Method() string { - return r.method -} - -func (r *rpcRequest) Endpoint() string { - return r.endpoint -} - -func (r *rpcRequest) Body() interface{} { - return r.body -} - -func (r *rpcRequest) Codec() codec.Writer { - return r.codec -} - -func (r *rpcRequest) Stream() bool { - return r.opts.Stream -} diff --git a/client/mucp/mucp_request_test.go b/client/mucp/mucp_request_test.go deleted file mode 100644 index 62b3162e..00000000 --- a/client/mucp/mucp_request_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package mucp - -import ( - "testing" - - "github.com/unistack-org/micro/v3/client" -) - -func TestRequestOptions(t *testing.T) { - r := newRequest("service", "endpoint", nil, "application/json") - if r.Service() != "service" { - t.Fatalf("expected 'service' got %s", r.Service()) - } - if r.Endpoint() != "endpoint" { - t.Fatalf("expected 'endpoint' got %s", r.Endpoint()) - } - if r.ContentType() != "application/json" { - t.Fatalf("expected 'endpoint' got %s", r.ContentType()) - } - - r2 := newRequest("service", "endpoint", nil, "application/json", client.WithContentType("application/protobuf")) - if r2.ContentType() != "application/protobuf" { - t.Fatalf("expected 'endpoint' got %s", r2.ContentType()) - } -} diff --git a/client/mucp/mucp_response.go b/client/mucp/mucp_response.go deleted file mode 100644 index 77996133..00000000 --- a/client/mucp/mucp_response.go +++ /dev/null @@ -1,35 +0,0 @@ -package mucp - -import ( - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/transport" -) - -type rpcResponse struct { - header map[string]string - body []byte - socket transport.Socket - codec codec.Codec -} - -func (r *rpcResponse) Codec() codec.Reader { - return r.codec -} - -func (r *rpcResponse) Header() map[string]string { - return r.header -} - -func (r *rpcResponse) Read() ([]byte, error) { - var msg transport.Message - - if err := r.socket.Recv(&msg); err != nil { - return nil, err - } - - // set internals - r.header = msg.Header - r.body = msg.Body - - return msg.Body, nil -} diff --git a/client/mucp/mucp_stream.go b/client/mucp/mucp_stream.go deleted file mode 100644 index 5a2ab2e8..00000000 --- a/client/mucp/mucp_stream.go +++ /dev/null @@ -1,165 +0,0 @@ -package mucp - -import ( - "context" - "io" - "sync" - - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" -) - -// Implements the streamer interface -type rpcStream struct { - sync.RWMutex - id string - closed chan bool - err error - request client.Request - response client.Response - codec codec.Codec - context context.Context - - // signal whether we should send EOS - sendEOS bool - - // release releases the connection back to the pool - release func(err error) -} - -func (r *rpcStream) isClosed() bool { - select { - case <-r.closed: - return true - default: - return false - } -} - -func (r *rpcStream) Context() context.Context { - return r.context -} - -func (r *rpcStream) Request() client.Request { - return r.request -} - -func (r *rpcStream) Response() client.Response { - return r.response -} - -func (r *rpcStream) Send(msg interface{}) error { - r.Lock() - defer r.Unlock() - - if r.isClosed() { - r.err = errShutdown - return errShutdown - } - - req := codec.Message{ - Id: r.id, - Target: r.request.Service(), - Method: r.request.Method(), - Endpoint: r.request.Endpoint(), - Type: codec.Request, - } - - if err := r.codec.Write(&req, msg); err != nil { - r.err = err - return err - } - - return nil -} - -func (r *rpcStream) Recv(msg interface{}) error { - r.Lock() - defer r.Unlock() - - if r.isClosed() { - r.err = errShutdown - return errShutdown - } - - var resp codec.Message - - r.Unlock() - err := r.codec.ReadHeader(&resp, codec.Response) - r.Lock() - if err != nil { - if err == io.EOF && !r.isClosed() { - r.err = io.ErrUnexpectedEOF - return io.ErrUnexpectedEOF - } - r.err = err - return err - } - - switch { - case len(resp.Error) > 0: - // We've got an error response. Give this to the request; - // any subsequent requests will get the ReadResponseBody - // error if there is one. - if resp.Error != lastStreamResponseError { - r.err = serverError(resp.Error) - } else { - r.err = io.EOF - } - r.Unlock() - err = r.codec.ReadBody(nil) - r.Lock() - if err != nil { - r.err = err - } - default: - r.Unlock() - err = r.codec.ReadBody(msg) - r.Lock() - if err != nil { - r.err = err - } - } - - return r.err -} - -func (r *rpcStream) Error() error { - r.RLock() - defer r.RUnlock() - return r.err -} - -func (r *rpcStream) Close() error { - r.Lock() - - select { - case <-r.closed: - r.Unlock() - return nil - default: - close(r.closed) - r.Unlock() - - // send the end of stream message - if r.sendEOS { - // no need to check for error - r.codec.Write(&codec.Message{ - Id: r.id, - Target: r.request.Service(), - Method: r.request.Method(), - Endpoint: r.request.Endpoint(), - Type: codec.Error, - Error: lastStreamResponseError, - }, nil) - } - - err := r.codec.Close() - - // release the connection - r.release(r.Error()) - - // return the codec error - return err - } -} diff --git a/client/mucp/mucp_test.go b/client/mucp/mucp_test.go deleted file mode 100644 index 25589437..00000000 --- a/client/mucp/mucp_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package mucp - -import ( - "context" - "fmt" - "testing" - - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/memory" - "github.com/unistack-org/micro/v3/router" - regRouter "github.com/unistack-org/micro/v3/router/registry" -) - -func newTestRouter() router.Router { - reg := memory.NewRegistry(memory.Services(testData)) - return regRouter.NewRouter(router.Registry(reg)) -} - -func TestCallAddress(t *testing.T) { - var called bool - service := "test.service" - endpoint := "Test.Endpoint" - address := "10.1.10.1:8080" - - wrap := func(cf client.CallFunc) client.CallFunc { - return func(ctx context.Context, node string, req client.Request, rsp interface{}, opts client.CallOptions) error { - called = true - - if req.Service() != service { - return fmt.Errorf("expected service: %s got %s", service, req.Service()) - } - - if req.Endpoint() != endpoint { - return fmt.Errorf("expected service: %s got %s", endpoint, req.Endpoint()) - } - - if node != address { - return fmt.Errorf("expected address: %s got %s", address, node) - } - - // don't do the call - return nil - } - } - - r := newTestRouter() - - c := NewClient( - client.Router(r), - client.WrapCall(wrap), - ) - - req := c.NewRequest(service, endpoint, nil) - - // test calling remote address - if err := c.Call(context.Background(), req, nil, client.WithAddress(address)); err != nil { - t.Fatal("call with address error", err) - } - - if !called { - t.Fatal("wrapper not called") - } - -} - -func TestCallRetry(t *testing.T) { - service := "test.service" - endpoint := "Test.Endpoint" - address := "10.1.10.1" - - var called int - - wrap := func(cf client.CallFunc) client.CallFunc { - return func(ctx context.Context, node string, req client.Request, rsp interface{}, opts client.CallOptions) error { - called++ - if called == 1 { - return errors.InternalServerError("test.error", "retry request") - } - - // don't do the call - return nil - } - } - - r := newTestRouter() - c := NewClient( - client.Router(r), - client.WrapCall(wrap), - ) - - req := c.NewRequest(service, endpoint, nil) - - // test calling remote address - if err := c.Call(context.Background(), req, nil, client.WithAddress(address)); err != nil { - t.Fatal("call with address error", err) - } - - // num calls - if called < c.Options().CallOptions.Retries+1 { - t.Fatal("request not retried") - } -} - -func TestCallWrapper(t *testing.T) { - var called bool - id := "test.1" - service := "test.service" - endpoint := "Test.Endpoint" - address := "10.1.10.1:8080" - - wrap := func(cf client.CallFunc) client.CallFunc { - return func(ctx context.Context, node string, req client.Request, rsp interface{}, opts client.CallOptions) error { - called = true - - if req.Service() != service { - return fmt.Errorf("expected service: %s got %s", service, req.Service()) - } - - if req.Endpoint() != endpoint { - return fmt.Errorf("expected service: %s got %s", endpoint, req.Endpoint()) - } - - if node != address { - return fmt.Errorf("expected address: %s got %s", address, node) - } - - // don't do the call - return nil - } - } - - r := newTestRouter() - c := NewClient( - client.Router(r), - client.WrapCall(wrap), - ) - - r.Options().Registry.Register(®istry.Service{ - Name: service, - Version: "latest", - Nodes: []*registry.Node{ - { - Id: id, - Address: address, - }, - }, - }) - - req := c.NewRequest(service, endpoint, nil) - if err := c.Call(context.Background(), req, nil); err != nil { - t.Fatal("call wrapper error", err) - } - - if !called { - t.Fatal("wrapper not called") - } -} diff --git a/client/mucp/options_test.go b/client/mucp/options_test.go deleted file mode 100644 index 1b6bd53e..00000000 --- a/client/mucp/options_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package mucp - -import ( - "testing" - "time" - - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/transport" -) - -func TestCallOptions(t *testing.T) { - testData := []struct { - set bool - retries int - rtimeout time.Duration - dtimeout time.Duration - }{ - {false, client.DefaultRetries, client.DefaultRequestTimeout, transport.DefaultDialTimeout}, - {true, 10, time.Second, time.Second * 2}, - } - - for _, d := range testData { - var opts client.Options - var cl client.Client - - if d.set { - opts = client.NewOptions( - client.Retries(d.retries), - client.RequestTimeout(d.rtimeout), - client.DialTimeout(d.dtimeout), - ) - - cl = NewClient( - client.Retries(d.retries), - client.RequestTimeout(d.rtimeout), - client.DialTimeout(d.dtimeout), - ) - } else { - opts = client.NewOptions() - cl = NewClient() - } - - // test options and those set in client - for _, o := range []client.Options{opts, cl.Options()} { - if o.CallOptions.Retries != d.retries { - t.Fatalf("Expected retries %v got %v", d.retries, o.CallOptions.Retries) - } - - if o.CallOptions.RequestTimeout != d.rtimeout { - t.Fatalf("Expected request timeout %v got %v", d.rtimeout, o.CallOptions.RequestTimeout) - } - - if o.CallOptions.DialTimeout != d.dtimeout { - t.Fatalf("Expected %v got %v", d.dtimeout, o.CallOptions.DialTimeout) - } - - // copy CallOptions - callOpts := o.CallOptions - - // create new opts - cretries := client.WithRetries(o.CallOptions.Retries * 10) - crtimeout := client.WithRequestTimeout(o.CallOptions.RequestTimeout * (time.Second * 10)) - cdtimeout := client.WithDialTimeout(o.CallOptions.DialTimeout * (time.Second * 10)) - - // set call options - for _, opt := range []client.CallOption{cretries, crtimeout, cdtimeout} { - opt(&callOpts) - } - - // check call options - if e := o.CallOptions.Retries * 10; callOpts.Retries != e { - t.Fatalf("Expected retries %v got %v", e, callOpts.Retries) - } - - if e := o.CallOptions.RequestTimeout * (time.Second * 10); callOpts.RequestTimeout != e { - t.Fatalf("Expected request timeout %v got %v", e, callOpts.RequestTimeout) - } - - if e := o.CallOptions.DialTimeout * (time.Second * 10); callOpts.DialTimeout != e { - t.Fatalf("Expected %v got %v", e, callOpts.DialTimeout) - } - - } - - } -} diff --git a/client/options.go b/client/options.go index 65f8fa51..c17eaf6c 100644 --- a/client/options.go +++ b/client/options.go @@ -5,15 +5,12 @@ import ( "time" "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/broker/http" "github.com/unistack-org/micro/v3/codec" "github.com/unistack-org/micro/v3/registry" "github.com/unistack-org/micro/v3/router" - regRouter "github.com/unistack-org/micro/v3/router/registry" "github.com/unistack-org/micro/v3/selector" "github.com/unistack-org/micro/v3/selector/random" "github.com/unistack-org/micro/v3/transport" - thttp "github.com/unistack-org/micro/v3/transport/http" ) type Options struct { @@ -120,13 +117,10 @@ func NewOptions(options ...Option) Options { RequestTimeout: DefaultRequestTimeout, DialTimeout: transport.DefaultDialTimeout, }, - Lookup: LookupRoute, - PoolSize: DefaultPoolSize, - PoolTTL: DefaultPoolTTL, - Broker: http.NewBroker(), - Router: regRouter.NewRouter(), - Selector: random.NewSelector(), - Transport: thttp.NewTransport(), + Lookup: LookupRoute, + PoolSize: DefaultPoolSize, + PoolTTL: DefaultPoolTTL, + Selector: random.NewSelector(), } for _, o := range options { diff --git a/client/service/proto/client.pb.go b/client/service/proto/client.pb.go deleted file mode 100644 index 0f962eda..00000000 --- a/client/service/proto/client.pb.go +++ /dev/null @@ -1,339 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: client/service/proto/client.proto - -package go_micro_client - -import ( - proto "github.com/golang/protobuf/proto" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Request struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` - Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` - Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` -} - -func (x *Request) Reset() { - *x = Request{} - if protoimpl.UnsafeEnabled { - mi := &file_client_service_proto_client_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Request) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Request) ProtoMessage() {} - -func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_client_service_proto_client_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 Request.ProtoReflect.Descriptor instead. -func (*Request) Descriptor() ([]byte, []int) { - return file_client_service_proto_client_proto_rawDescGZIP(), []int{0} -} - -func (x *Request) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -func (x *Request) GetEndpoint() string { - if x != nil { - return x.Endpoint - } - return "" -} - -func (x *Request) GetContentType() string { - if x != nil { - return x.ContentType - } - return "" -} - -func (x *Request) GetBody() []byte { - if x != nil { - return x.Body - } - return nil -} - -type Response struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` -} - -func (x *Response) Reset() { - *x = Response{} - if protoimpl.UnsafeEnabled { - mi := &file_client_service_proto_client_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Response) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Response) ProtoMessage() {} - -func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_client_service_proto_client_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 Response.ProtoReflect.Descriptor instead. -func (*Response) Descriptor() ([]byte, []int) { - return file_client_service_proto_client_proto_rawDescGZIP(), []int{1} -} - -func (x *Response) GetBody() []byte { - if x != nil { - return x.Body - } - return nil -} - -type Message struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` - ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` - Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` -} - -func (x *Message) Reset() { - *x = Message{} - if protoimpl.UnsafeEnabled { - mi := &file_client_service_proto_client_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Message) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Message) ProtoMessage() {} - -func (x *Message) ProtoReflect() protoreflect.Message { - mi := &file_client_service_proto_client_proto_msgTypes[2] - 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 Message.ProtoReflect.Descriptor instead. -func (*Message) Descriptor() ([]byte, []int) { - return file_client_service_proto_client_proto_rawDescGZIP(), []int{2} -} - -func (x *Message) GetTopic() string { - if x != nil { - return x.Topic - } - return "" -} - -func (x *Message) GetContentType() string { - if x != nil { - return x.ContentType - } - return "" -} - -func (x *Message) GetBody() []byte { - if x != nil { - return x.Body - } - return nil -} - -var File_client_service_proto_client_proto protoreflect.FileDescriptor - -var file_client_service_proto_client_proto_rawDesc = []byte{ - 0x0a, 0x21, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x22, 0x76, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x1e, 0x0a, 0x08, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x56, 0x0a, 0x07, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x21, 0x0a, - 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x32, 0xcd, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, - 0x3d, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x18, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, - 0x72, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, - 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, - 0x01, 0x30, 0x01, 0x12, 0x3f, 0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x18, - 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_client_service_proto_client_proto_rawDescOnce sync.Once - file_client_service_proto_client_proto_rawDescData = file_client_service_proto_client_proto_rawDesc -) - -func file_client_service_proto_client_proto_rawDescGZIP() []byte { - file_client_service_proto_client_proto_rawDescOnce.Do(func() { - file_client_service_proto_client_proto_rawDescData = protoimpl.X.CompressGZIP(file_client_service_proto_client_proto_rawDescData) - }) - return file_client_service_proto_client_proto_rawDescData -} - -var file_client_service_proto_client_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_client_service_proto_client_proto_goTypes = []interface{}{ - (*Request)(nil), // 0: go.micro.client.Request - (*Response)(nil), // 1: go.micro.client.Response - (*Message)(nil), // 2: go.micro.client.Message -} -var file_client_service_proto_client_proto_depIdxs = []int32{ - 0, // 0: go.micro.client.Client.Call:input_type -> go.micro.client.Request - 0, // 1: go.micro.client.Client.Stream:input_type -> go.micro.client.Request - 2, // 2: go.micro.client.Client.Publish:input_type -> go.micro.client.Message - 1, // 3: go.micro.client.Client.Call:output_type -> go.micro.client.Response - 1, // 4: go.micro.client.Client.Stream:output_type -> go.micro.client.Response - 2, // 5: go.micro.client.Client.Publish:output_type -> go.micro.client.Message - 3, // [3:6] is the sub-list for method output_type - 0, // [0:3] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_client_service_proto_client_proto_init() } -func file_client_service_proto_client_proto_init() { - if File_client_service_proto_client_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_client_service_proto_client_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_client_service_proto_client_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_client_service_proto_client_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Message); 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_client_service_proto_client_proto_rawDesc, - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_client_service_proto_client_proto_goTypes, - DependencyIndexes: file_client_service_proto_client_proto_depIdxs, - MessageInfos: file_client_service_proto_client_proto_msgTypes, - }.Build() - File_client_service_proto_client_proto = out.File - file_client_service_proto_client_proto_rawDesc = nil - file_client_service_proto_client_proto_goTypes = nil - file_client_service_proto_client_proto_depIdxs = nil -} diff --git a/client/service/proto/client.pb.micro.go b/client/service/proto/client.pb.micro.go deleted file mode 100644 index 89c6f502..00000000 --- a/client/service/proto/client.pb.micro.go +++ /dev/null @@ -1,215 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: client/service/proto/client.proto - -package go_micro_client - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Client service - -func NewClientEndpoints() []*api.Endpoint { - return []*api.Endpoint{} -} - -// Client API for Client service - -type ClientService interface { - // Call allows a single request to be made - Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) - // Stream is a bidirectional stream - Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) - // Publish publishes a message and returns an empty Message - Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) -} - -type clientService struct { - c client.Client - name string -} - -func NewClientService(name string, c client.Client) ClientService { - return &clientService{ - c: c, - name: name, - } -} - -func (c *clientService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { - req := c.c.NewRequest(c.name, "Client.Call", in) - out := new(Response) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *clientService) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) { - req := c.c.NewRequest(c.name, "Client.Stream", &Request{}) - stream, err := c.c.Stream(ctx, req, opts...) - if err != nil { - return nil, err - } - return &clientServiceStream{stream}, nil -} - -type Client_StreamService interface { - Context() context.Context - SendMsg(interface{}) error - RecvMsg(interface{}) error - Close() error - Send(*Request) error - Recv() (*Response, error) -} - -type clientServiceStream struct { - stream client.Stream -} - -func (x *clientServiceStream) Close() error { - return x.stream.Close() -} - -func (x *clientServiceStream) Context() context.Context { - return x.stream.Context() -} - -func (x *clientServiceStream) SendMsg(m interface{}) error { - return x.stream.Send(m) -} - -func (x *clientServiceStream) RecvMsg(m interface{}) error { - return x.stream.Recv(m) -} - -func (x *clientServiceStream) Send(m *Request) error { - return x.stream.Send(m) -} - -func (x *clientServiceStream) Recv() (*Response, error) { - m := new(Response) - err := x.stream.Recv(m) - if err != nil { - return nil, err - } - return m, nil -} - -func (c *clientService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) { - req := c.c.NewRequest(c.name, "Client.Publish", in) - out := new(Message) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Client service - -type ClientHandler interface { - // Call allows a single request to be made - Call(context.Context, *Request, *Response) error - // Stream is a bidirectional stream - Stream(context.Context, Client_StreamStream) error - // Publish publishes a message and returns an empty Message - Publish(context.Context, *Message, *Message) error -} - -func RegisterClientHandler(s server.Server, hdlr ClientHandler, opts ...server.HandlerOption) error { - type client interface { - Call(ctx context.Context, in *Request, out *Response) error - Stream(ctx context.Context, stream server.Stream) error - Publish(ctx context.Context, in *Message, out *Message) error - } - type Client struct { - client - } - h := &clientHandler{hdlr} - return s.Handle(s.NewHandler(&Client{h}, opts...)) -} - -type clientHandler struct { - ClientHandler -} - -func (h *clientHandler) Call(ctx context.Context, in *Request, out *Response) error { - return h.ClientHandler.Call(ctx, in, out) -} - -func (h *clientHandler) Stream(ctx context.Context, stream server.Stream) error { - return h.ClientHandler.Stream(ctx, &clientStreamStream{stream}) -} - -type Client_StreamStream interface { - Context() context.Context - SendMsg(interface{}) error - RecvMsg(interface{}) error - Close() error - Send(*Response) error - Recv() (*Request, error) -} - -type clientStreamStream struct { - stream server.Stream -} - -func (x *clientStreamStream) Close() error { - return x.stream.Close() -} - -func (x *clientStreamStream) Context() context.Context { - return x.stream.Context() -} - -func (x *clientStreamStream) SendMsg(m interface{}) error { - return x.stream.Send(m) -} - -func (x *clientStreamStream) RecvMsg(m interface{}) error { - return x.stream.Recv(m) -} - -func (x *clientStreamStream) Send(m *Response) error { - return x.stream.Send(m) -} - -func (x *clientStreamStream) Recv() (*Request, error) { - m := new(Request) - if err := x.stream.Recv(m); err != nil { - return nil, err - } - return m, nil -} - -func (h *clientHandler) Publish(ctx context.Context, in *Message, out *Message) error { - return h.ClientHandler.Publish(ctx, in, out) -} diff --git a/client/service/proto/client.proto b/client/service/proto/client.proto deleted file mode 100644 index b5703cf2..00000000 --- a/client/service/proto/client.proto +++ /dev/null @@ -1,30 +0,0 @@ -syntax = "proto3"; - -package go.micro.client; - -// Client is the micro client interface -service Client { - // Call allows a single request to be made - rpc Call(Request) returns (Response) {}; - // Stream is a bidirectional stream - rpc Stream(stream Request) returns (stream Response) {}; - // Publish publishes a message and returns an empty Message - rpc Publish(Message) returns (Message) {}; -} - -message Request { - string service = 1; - string endpoint = 2; - string content_type = 3; - bytes body = 4; -} - -message Response { - bytes body = 1; -} - -message Message { - string topic = 1; - string content_type = 2; - bytes body = 3; -} diff --git a/client/service/proto/client_grpc.pb.go b/client/service/proto/client_grpc.pb.go deleted file mode 100644 index 1253b4b8..00000000 --- a/client/service/proto/client_grpc.pb.go +++ /dev/null @@ -1,201 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package go_micro_client - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// ClientClient is the client API for Client service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ClientClient interface { - // Call allows a single request to be made - Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) - // Stream is a bidirectional stream - Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) - // Publish publishes a message and returns an empty Message - Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) -} - -type clientClient struct { - cc grpc.ClientConnInterface -} - -func NewClientClient(cc grpc.ClientConnInterface) ClientClient { - return &clientClient{cc} -} - -func (c *clientClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { - out := new(Response) - err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *clientClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Client_serviceDesc.Streams[0], "/go.micro.client.Client/Stream", opts...) - if err != nil { - return nil, err - } - x := &clientStreamClient{stream} - return x, nil -} - -type Client_StreamClient interface { - Send(*Request) error - Recv() (*Response, error) - grpc.ClientStream -} - -type clientStreamClient struct { - grpc.ClientStream -} - -func (x *clientStreamClient) Send(m *Request) error { - return x.ClientStream.SendMsg(m) -} - -func (x *clientStreamClient) Recv() (*Response, error) { - m := new(Response) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *clientClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) { - out := new(Message) - err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ClientServer is the server API for Client service. -// All implementations must embed UnimplementedClientServer -// for forward compatibility -type ClientServer interface { - // Call allows a single request to be made - Call(context.Context, *Request) (*Response, error) - // Stream is a bidirectional stream - Stream(Client_StreamServer) error - // Publish publishes a message and returns an empty Message - Publish(context.Context, *Message) (*Message, error) - mustEmbedUnimplementedClientServer() -} - -// UnimplementedClientServer must be embedded to have forward compatible implementations. -type UnimplementedClientServer struct { -} - -func (*UnimplementedClientServer) Call(context.Context, *Request) (*Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method Call not implemented") -} -func (*UnimplementedClientServer) Stream(Client_StreamServer) error { - return status.Errorf(codes.Unimplemented, "method Stream not implemented") -} -func (*UnimplementedClientServer) Publish(context.Context, *Message) (*Message, error) { - return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented") -} -func (*UnimplementedClientServer) mustEmbedUnimplementedClientServer() {} - -func RegisterClientServer(s *grpc.Server, srv ClientServer) { - s.RegisterService(&_Client_serviceDesc, srv) -} - -func _Client_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ClientServer).Call(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.client.Client/Call", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ClientServer).Call(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(ClientServer).Stream(&clientStreamServer{stream}) -} - -type Client_StreamServer interface { - Send(*Response) error - Recv() (*Request, error) - grpc.ServerStream -} - -type clientStreamServer struct { - grpc.ServerStream -} - -func (x *clientStreamServer) Send(m *Response) error { - return x.ServerStream.SendMsg(m) -} - -func (x *clientStreamServer) Recv() (*Request, error) { - m := new(Request) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func _Client_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Message) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ClientServer).Publish(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.client.Client/Publish", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ClientServer).Publish(ctx, req.(*Message)) - } - return interceptor(ctx, in, info, handler) -} - -var _Client_serviceDesc = grpc.ServiceDesc{ - ServiceName: "go.micro.client.Client", - HandlerType: (*ClientServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Call", - Handler: _Client_Call_Handler, - }, - { - MethodName: "Publish", - Handler: _Client_Publish_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Stream", - Handler: _Client_Stream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "client/service/proto/client.proto", -} diff --git a/cmd/protoc-gen-micro/examples/greeter/greeter.pb.go b/cmd/protoc-gen-micro/examples/greeter/greeter.pb.go deleted file mode 100644 index 44a8e1f5..00000000 --- a/cmd/protoc-gen-micro/examples/greeter/greeter.pb.go +++ /dev/null @@ -1,223 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: cmd/protoc-gen-micro/examples/greeter/greeter.proto - -package greeter - -import ( - proto "github.com/golang/protobuf/proto" - _ "google.golang.org/genproto/googleapis/api/annotations" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Request struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *Request) Reset() { - *x = Request{} - if protoimpl.UnsafeEnabled { - mi := &file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Request) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Request) ProtoMessage() {} - -func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_cmd_protoc_gen_micro_examples_greeter_greeter_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 Request.ProtoReflect.Descriptor instead. -func (*Request) Descriptor() ([]byte, []int) { - return file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescGZIP(), []int{0} -} - -func (x *Request) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type Response struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` -} - -func (x *Response) Reset() { - *x = Response{} - if protoimpl.UnsafeEnabled { - mi := &file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Response) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Response) ProtoMessage() {} - -func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_cmd_protoc_gen_micro_examples_greeter_greeter_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 Response.ProtoReflect.Descriptor instead. -func (*Response) Descriptor() ([]byte, []int) { - return file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescGZIP(), []int{1} -} - -func (x *Response) GetMsg() string { - if x != nil { - return x.Msg - } - return "" -} - -var File_cmd_protoc_gen_micro_examples_greeter_greeter_proto protoreflect.FileDescriptor - -var file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDesc = []byte{ - 0x0a, 0x33, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, - 0x2d, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, - 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x1d, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x1c, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, - 0x32, 0x6e, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x05, 0x48, - 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x08, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0b, 0x22, 0x06, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3a, 0x01, 0x2a, 0x12, 0x32, 0x0a, 0x06, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x08, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x09, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x09, 0x12, 0x07, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x28, 0x01, 0x30, 0x01, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescOnce sync.Once - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescData = file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDesc -) - -func file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescGZIP() []byte { - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescOnce.Do(func() { - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescData = protoimpl.X.CompressGZIP(file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescData) - }) - return file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDescData -} - -var file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_goTypes = []interface{}{ - (*Request)(nil), // 0: Request - (*Response)(nil), // 1: Response -} -var file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_depIdxs = []int32{ - 0, // 0: Greeter.Hello:input_type -> Request - 0, // 1: Greeter.Stream:input_type -> Request - 1, // 2: Greeter.Hello:output_type -> Response - 1, // 3: Greeter.Stream:output_type -> Response - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_init() } -func file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_init() { - if File_cmd_protoc_gen_micro_examples_greeter_greeter_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); 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_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_goTypes, - DependencyIndexes: file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_depIdxs, - MessageInfos: file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_msgTypes, - }.Build() - File_cmd_protoc_gen_micro_examples_greeter_greeter_proto = out.File - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_rawDesc = nil - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_goTypes = nil - file_cmd_protoc_gen_micro_examples_greeter_greeter_proto_depIdxs = nil -} diff --git a/cmd/protoc-gen-micro/examples/greeter/greeter.pb.micro.go b/cmd/protoc-gen-micro/examples/greeter/greeter.pb.micro.go deleted file mode 100644 index 12e02a86..00000000 --- a/cmd/protoc-gen-micro/examples/greeter/greeter.pb.micro.go +++ /dev/null @@ -1,222 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: cmd/protoc-gen-micro/examples/greeter/greeter.proto - -package greeter - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - _ "google.golang.org/genproto/googleapis/api/annotations" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Greeter service - -func NewGreeterEndpoints() []*api.Endpoint { - return []*api.Endpoint{ - &api.Endpoint{ - Name: "Greeter.Hello", - Path: []string{"/hello"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - }, - &api.Endpoint{ - Name: "Greeter.Stream", - Path: []string{"/stream"}, - Method: []string{"GET"}, - Stream: true, - Handler: "rpc", - }, - } -} - -// Client API for Greeter service - -type GreeterService interface { - Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) - Stream(ctx context.Context, opts ...client.CallOption) (Greeter_StreamService, error) -} - -type greeterService struct { - c client.Client - name string -} - -func NewGreeterService(name string, c client.Client) GreeterService { - return &greeterService{ - c: c, - name: name, - } -} - -func (c *greeterService) Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { - req := c.c.NewRequest(c.name, "Greeter.Hello", in) - out := new(Response) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *greeterService) Stream(ctx context.Context, opts ...client.CallOption) (Greeter_StreamService, error) { - req := c.c.NewRequest(c.name, "Greeter.Stream", &Request{}) - stream, err := c.c.Stream(ctx, req, opts...) - if err != nil { - return nil, err - } - return &greeterServiceStream{stream}, nil -} - -type Greeter_StreamService interface { - Context() context.Context - SendMsg(interface{}) error - RecvMsg(interface{}) error - Close() error - Send(*Request) error - Recv() (*Response, error) -} - -type greeterServiceStream struct { - stream client.Stream -} - -func (x *greeterServiceStream) Close() error { - return x.stream.Close() -} - -func (x *greeterServiceStream) Context() context.Context { - return x.stream.Context() -} - -func (x *greeterServiceStream) SendMsg(m interface{}) error { - return x.stream.Send(m) -} - -func (x *greeterServiceStream) RecvMsg(m interface{}) error { - return x.stream.Recv(m) -} - -func (x *greeterServiceStream) Send(m *Request) error { - return x.stream.Send(m) -} - -func (x *greeterServiceStream) Recv() (*Response, error) { - m := new(Response) - err := x.stream.Recv(m) - if err != nil { - return nil, err - } - return m, nil -} - -// Server API for Greeter service - -type GreeterHandler interface { - Hello(context.Context, *Request, *Response) error - Stream(context.Context, Greeter_StreamStream) error -} - -func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler, opts ...server.HandlerOption) error { - type greeter interface { - Hello(ctx context.Context, in *Request, out *Response) error - Stream(ctx context.Context, stream server.Stream) error - } - type Greeter struct { - greeter - } - h := &greeterHandler{hdlr} - opts = append(opts, api.WithEndpoint(&api.Endpoint{ - Name: "Greeter.Hello", - Path: []string{"/hello"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - })) - opts = append(opts, api.WithEndpoint(&api.Endpoint{ - Name: "Greeter.Stream", - Path: []string{"/stream"}, - Method: []string{"GET"}, - Stream: true, - Handler: "rpc", - })) - return s.Handle(s.NewHandler(&Greeter{h}, opts...)) -} - -type greeterHandler struct { - GreeterHandler -} - -func (h *greeterHandler) Hello(ctx context.Context, in *Request, out *Response) error { - return h.GreeterHandler.Hello(ctx, in, out) -} - -func (h *greeterHandler) Stream(ctx context.Context, stream server.Stream) error { - return h.GreeterHandler.Stream(ctx, &greeterStreamStream{stream}) -} - -type Greeter_StreamStream interface { - Context() context.Context - SendMsg(interface{}) error - RecvMsg(interface{}) error - Close() error - Send(*Response) error - Recv() (*Request, error) -} - -type greeterStreamStream struct { - stream server.Stream -} - -func (x *greeterStreamStream) Close() error { - return x.stream.Close() -} - -func (x *greeterStreamStream) Context() context.Context { - return x.stream.Context() -} - -func (x *greeterStreamStream) SendMsg(m interface{}) error { - return x.stream.Send(m) -} - -func (x *greeterStreamStream) RecvMsg(m interface{}) error { - return x.stream.Recv(m) -} - -func (x *greeterStreamStream) Send(m *Response) error { - return x.stream.Send(m) -} - -func (x *greeterStreamStream) Recv() (*Request, error) { - m := new(Request) - if err := x.stream.Recv(m); err != nil { - return nil, err - } - return m, nil -} diff --git a/cmd/protoc-gen-micro/examples/greeter/greeter.proto b/cmd/protoc-gen-micro/examples/greeter/greeter.proto deleted file mode 100644 index 8bbb1e57..00000000 --- a/cmd/protoc-gen-micro/examples/greeter/greeter.proto +++ /dev/null @@ -1,20 +0,0 @@ -syntax = "proto3"; - -import "google/api/annotations.proto"; - -service Greeter { - rpc Hello(Request) returns (Response) { - option (google.api.http) = { post: "/hello"; body: "*"; }; - } - rpc Stream(stream Request) returns (stream Response) { - option (google.api.http) = { get: "/stream"; }; - } -} - -message Request { - string name = 1; -} - -message Response { - string msg = 1; -} diff --git a/cmd/protoc-gen-micro/examples/greeter/greeter_grpc.pb.go b/cmd/protoc-gen-micro/examples/greeter/greeter_grpc.pb.go deleted file mode 100644 index 36fc6c02..00000000 --- a/cmd/protoc-gen-micro/examples/greeter/greeter_grpc.pb.go +++ /dev/null @@ -1,159 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package greeter - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// GreeterClient is the client API for Greeter service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type GreeterClient interface { - Hello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) - Stream(ctx context.Context, opts ...grpc.CallOption) (Greeter_StreamClient, error) -} - -type greeterClient struct { - cc grpc.ClientConnInterface -} - -func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { - return &greeterClient{cc} -} - -func (c *greeterClient) Hello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { - out := new(Response) - err := c.cc.Invoke(ctx, "/Greeter/Hello", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *greeterClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Greeter_StreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Greeter_serviceDesc.Streams[0], "/Greeter/Stream", opts...) - if err != nil { - return nil, err - } - x := &greeterStreamClient{stream} - return x, nil -} - -type Greeter_StreamClient interface { - Send(*Request) error - Recv() (*Response, error) - grpc.ClientStream -} - -type greeterStreamClient struct { - grpc.ClientStream -} - -func (x *greeterStreamClient) Send(m *Request) error { - return x.ClientStream.SendMsg(m) -} - -func (x *greeterStreamClient) Recv() (*Response, error) { - m := new(Response) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// GreeterServer is the server API for Greeter service. -// All implementations must embed UnimplementedGreeterServer -// for forward compatibility -type GreeterServer interface { - Hello(context.Context, *Request) (*Response, error) - Stream(Greeter_StreamServer) error - mustEmbedUnimplementedGreeterServer() -} - -// UnimplementedGreeterServer must be embedded to have forward compatible implementations. -type UnimplementedGreeterServer struct { -} - -func (*UnimplementedGreeterServer) Hello(context.Context, *Request) (*Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented") -} -func (*UnimplementedGreeterServer) Stream(Greeter_StreamServer) error { - return status.Errorf(codes.Unimplemented, "method Stream not implemented") -} -func (*UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {} - -func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { - s.RegisterService(&_Greeter_serviceDesc, srv) -} - -func _Greeter_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GreeterServer).Hello(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Greeter/Hello", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GreeterServer).Hello(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Greeter_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(GreeterServer).Stream(&greeterStreamServer{stream}) -} - -type Greeter_StreamServer interface { - Send(*Response) error - Recv() (*Request, error) - grpc.ServerStream -} - -type greeterStreamServer struct { - grpc.ServerStream -} - -func (x *greeterStreamServer) Send(m *Response) error { - return x.ServerStream.SendMsg(m) -} - -func (x *greeterStreamServer) Recv() (*Request, error) { - m := new(Request) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -var _Greeter_serviceDesc = grpc.ServiceDesc{ - ServiceName: "Greeter", - HandlerType: (*GreeterServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Hello", - Handler: _Greeter_Hello_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Stream", - Handler: _Greeter_Stream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "cmd/protoc-gen-micro/examples/greeter/greeter.proto", -} diff --git a/config/default.go b/config/default.go index c4ccb878..6971c76e 100644 --- a/config/default.go +++ b/config/default.go @@ -6,9 +6,7 @@ import ( "time" "github.com/unistack-org/micro/v3/config/loader" - "github.com/unistack-org/micro/v3/config/loader/memory" "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/reader/json" "github.com/unistack-org/micro/v3/config/source" ) @@ -42,19 +40,12 @@ func newConfig(opts ...Option) (Config, error) { } func (c *config) Init(opts ...Option) error { - c.opts = Options{ - Reader: json.NewReader(), - } + c.opts = Options{} c.exit = make(chan bool) for _, o := range opts { o(&c.opts) } - // default loader uses the configured reader - if c.opts.Loader == nil { - c.opts.Loader = memory.NewLoader(memory.WithReader(c.opts.Reader)) - } - err := c.opts.Loader.Load(c.opts.Source...) if err != nil { return err diff --git a/config/default_test.go b/config/default_test.go index fa16afa8..d19a02e8 100644 --- a/config/default_test.go +++ b/config/default_test.go @@ -1,3 +1,5 @@ +// +build ignore + package config import ( diff --git a/config/loader/memory/memory.go b/config/loader/memory/memory.go deleted file mode 100644 index 6ecf3946..00000000 --- a/config/loader/memory/memory.go +++ /dev/null @@ -1,459 +0,0 @@ -package memory - -import ( - "bytes" - "container/list" - "errors" - "fmt" - "strings" - "sync" - "time" - - "github.com/unistack-org/micro/v3/config/loader" - "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/reader/json" - "github.com/unistack-org/micro/v3/config/source" -) - -type memory struct { - exit chan bool - opts loader.Options - - sync.RWMutex - // the current snapshot - snap *loader.Snapshot - // the current values - vals reader.Values - // all the sets - sets []*source.ChangeSet - // all the sources - sources []source.Source - - watchers *list.List -} - -type updateValue struct { - version string - value reader.Value -} - -type watcher struct { - exit chan bool - path []string - value reader.Value - reader reader.Reader - version string - updates chan updateValue -} - -func (m *memory) watch(idx int, s source.Source) { - // watches a source for changes - watch := func(idx int, s source.Watcher) error { - for { - // get changeset - cs, err := s.Next() - if err != nil { - return err - } - - m.Lock() - - // save - m.sets[idx] = cs - - // merge sets - set, err := m.opts.Reader.Merge(m.sets...) - if err != nil { - m.Unlock() - return err - } - - // set values - m.vals, _ = m.opts.Reader.Values(set) - m.snap = &loader.Snapshot{ - ChangeSet: set, - Version: genVer(), - } - m.Unlock() - - // send watch updates - m.update() - } - } - - for { - // watch the source - w, err := s.Watch() - if err != nil { - time.Sleep(time.Second) - continue - } - - done := make(chan bool) - - // the stop watch func - go func() { - select { - case <-done: - case <-m.exit: - } - w.Stop() - }() - - // block watch - if err := watch(idx, w); err != nil { - // do something better - time.Sleep(time.Second) - } - - // close done chan - close(done) - - // if the config is closed exit - select { - case <-m.exit: - return - default: - } - } -} - -func (m *memory) loaded() bool { - var loaded bool - m.RLock() - if m.vals != nil { - loaded = true - } - m.RUnlock() - return loaded -} - -// reload reads the sets and creates new values -func (m *memory) reload() error { - m.Lock() - - // merge sets - set, err := m.opts.Reader.Merge(m.sets...) - if err != nil { - m.Unlock() - return err - } - - // set values - m.vals, _ = m.opts.Reader.Values(set) - m.snap = &loader.Snapshot{ - ChangeSet: set, - Version: genVer(), - } - - m.Unlock() - - // update watchers - m.update() - - return nil -} - -func (m *memory) update() { - watchers := make([]*watcher, 0, m.watchers.Len()) - - m.RLock() - for e := m.watchers.Front(); e != nil; e = e.Next() { - watchers = append(watchers, e.Value.(*watcher)) - } - - vals := m.vals - snap := m.snap - m.RUnlock() - - for _, w := range watchers { - if w.version >= snap.Version { - continue - } - - uv := updateValue{ - version: m.snap.Version, - value: vals.Get(w.path...), - } - - select { - case <-w.exit: - continue - default: - } - select { - case w.updates <- uv: - default: - } - } -} - -// Snapshot returns a snapshot of the current loaded config -func (m *memory) Snapshot() (*loader.Snapshot, error) { - if m.loaded() { - m.RLock() - snap := loader.Copy(m.snap) - m.RUnlock() - return snap, nil - } - - // not loaded, sync - if err := m.Sync(); err != nil { - return nil, err - } - - // make copy - m.RLock() - snap := loader.Copy(m.snap) - m.RUnlock() - - return snap, nil -} - -// Sync loads all the sources, calls the parser and updates the config -func (m *memory) Sync() error { - //nolint:prealloc - var sets []*source.ChangeSet - - m.Lock() - - // read the source - var gerr []string - - for _, source := range m.sources { - ch, err := source.Read() - if err != nil { - gerr = append(gerr, err.Error()) - continue - } - sets = append(sets, ch) - } - - // merge sets - set, err := m.opts.Reader.Merge(sets...) - if err != nil { - m.Unlock() - return err - } - - // set values - vals, err := m.opts.Reader.Values(set) - if err != nil { - m.Unlock() - return err - } - m.vals = vals - m.snap = &loader.Snapshot{ - ChangeSet: set, - Version: genVer(), - } - - m.Unlock() - - // update watchers - m.update() - - if len(gerr) > 0 { - return fmt.Errorf("source loading errors: %s", strings.Join(gerr, "\n")) - } - - return nil -} - -func (m *memory) Close() error { - select { - case <-m.exit: - return nil - default: - close(m.exit) - } - return nil -} - -func (m *memory) Get(path ...string) (reader.Value, error) { - if !m.loaded() { - if err := m.Sync(); err != nil { - return nil, err - } - } - - m.Lock() - defer m.Unlock() - - // did sync actually work? - if m.vals != nil { - return m.vals.Get(path...), nil - } - - // assuming vals is nil - // create new vals - - ch := m.snap.ChangeSet - - // we are truly screwed, trying to load in a hacked way - v, err := m.opts.Reader.Values(ch) - if err != nil { - return nil, err - } - - // lets set it just because - m.vals = v - - if m.vals != nil { - return m.vals.Get(path...), nil - } - - // ok we're going hardcore now - - return nil, errors.New("no values") -} - -func (m *memory) Load(sources ...source.Source) error { - var gerrors []string - - for _, source := range sources { - set, err := source.Read() - if err != nil { - gerrors = append(gerrors, - fmt.Sprintf("error loading source %s: %v", - source, - err)) - // continue processing - continue - } - m.Lock() - m.sources = append(m.sources, source) - m.sets = append(m.sets, set) - idx := len(m.sets) - 1 - m.Unlock() - go m.watch(idx, source) - } - - if err := m.reload(); err != nil { - gerrors = append(gerrors, err.Error()) - } - - // Return errors - if len(gerrors) != 0 { - return errors.New(strings.Join(gerrors, "\n")) - } - return nil -} - -func (m *memory) Watch(path ...string) (loader.Watcher, error) { - value, err := m.Get(path...) - if err != nil { - return nil, err - } - - m.Lock() - - w := &watcher{ - exit: make(chan bool), - path: path, - value: value, - reader: m.opts.Reader, - updates: make(chan updateValue, 1), - version: m.snap.Version, - } - - e := m.watchers.PushBack(w) - - m.Unlock() - - go func() { - <-w.exit - m.Lock() - m.watchers.Remove(e) - m.Unlock() - }() - - return w, nil -} - -func (m *memory) String() string { - return "memory" -} - -func (w *watcher) Next() (*loader.Snapshot, error) { - update := func(v reader.Value) *loader.Snapshot { - w.value = v - - cs := &source.ChangeSet{ - Data: v.Bytes(), - Format: w.reader.String(), - Source: "memory", - Timestamp: time.Now(), - } - cs.Checksum = cs.Sum() - - return &loader.Snapshot{ - ChangeSet: cs, - Version: w.version, - } - - } - - for { - select { - case <-w.exit: - return nil, errors.New("watcher stopped") - - case uv := <-w.updates: - if uv.version <= w.version { - continue - } - - v := uv.value - - w.version = uv.version - - if bytes.Equal(w.value.Bytes(), v.Bytes()) { - continue - } - - return update(v), nil - } - } -} - -func (w *watcher) Stop() error { - select { - case <-w.exit: - default: - close(w.exit) - close(w.updates) - } - - return nil -} - -func genVer() string { - return fmt.Sprintf("%d", time.Now().UnixNano()) -} - -func NewLoader(opts ...loader.Option) loader.Loader { - options := loader.Options{ - Reader: json.NewReader(), - } - - for _, o := range opts { - o(&options) - } - - m := &memory{ - exit: make(chan bool), - opts: options, - watchers: list.New(), - sources: options.Source, - } - - m.sets = make([]*source.ChangeSet, len(options.Source)) - - for i, s := range options.Source { - m.sets[i] = &source.ChangeSet{Source: s.String()} - go m.watch(i, s) - } - - return m -} diff --git a/config/loader/memory/options.go b/config/loader/memory/options.go deleted file mode 100644 index 44182f15..00000000 --- a/config/loader/memory/options.go +++ /dev/null @@ -1,21 +0,0 @@ -package memory - -import ( - "github.com/unistack-org/micro/v3/config/loader" - "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/source" -) - -// WithSource appends a source to list of sources -func WithSource(s source.Source) loader.Option { - return func(o *loader.Options) { - o.Source = append(o.Source, s) - } -} - -// WithReader sets the config reader -func WithReader(r reader.Reader) loader.Option { - return func(o *loader.Options) { - o.Reader = r - } -} diff --git a/config/reader/json/json.go b/config/reader/json/json.go deleted file mode 100644 index 297c4dc6..00000000 --- a/config/reader/json/json.go +++ /dev/null @@ -1,83 +0,0 @@ -package json - -import ( - "errors" - "time" - - "github.com/imdario/mergo" - "github.com/unistack-org/micro/v3/config/encoder" - "github.com/unistack-org/micro/v3/config/encoder/json" - "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/source" -) - -type jsonReader struct { - opts reader.Options - json encoder.Encoder -} - -func (j *jsonReader) Merge(changes ...*source.ChangeSet) (*source.ChangeSet, error) { - var merged map[string]interface{} - - for _, m := range changes { - if m == nil { - continue - } - - if len(m.Data) == 0 { - continue - } - - codec, ok := j.opts.Encoding[m.Format] - if !ok { - // fallback - codec = j.json - } - - var data map[string]interface{} - if err := codec.Decode(m.Data, &data); err != nil { - return nil, err - } - if err := mergo.Map(&merged, data, mergo.WithOverride); err != nil { - return nil, err - } - } - - b, err := j.json.Encode(merged) - if err != nil { - return nil, err - } - - cs := &source.ChangeSet{ - Timestamp: time.Now(), - Data: b, - Source: "json", - Format: j.json.String(), - } - cs.Checksum = cs.Sum() - - return cs, nil -} - -func (j *jsonReader) Values(ch *source.ChangeSet) (reader.Values, error) { - if ch == nil { - return nil, errors.New("changeset is nil") - } - if ch.Format != "json" { - return nil, errors.New("unsupported format") - } - return newValues(ch, j.opts) -} - -func (j *jsonReader) String() string { - return "json" -} - -// NewReader creates a json reader -func NewReader(opts ...reader.Option) reader.Reader { - options := reader.NewOptions(opts...) - return &jsonReader{ - json: json.NewEncoder(), - opts: options, - } -} diff --git a/config/reader/json/json_test.go b/config/reader/json/json_test.go deleted file mode 100644 index 48e0f8d7..00000000 --- a/config/reader/json/json_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package json - -import ( - "testing" - - "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/source" -) - -func TestReader(t *testing.T) { - data := []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`) - - testData := []struct { - path []string - value string - }{ - { - []string{"foo"}, - "bar", - }, - { - []string{"baz", "bar"}, - "cat", - }, - } - - values := newTestValues(t, data) - - for _, test := range testData { - if v := values.Get(test.path...).String(""); v != test.value { - t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path) - } - } -} - -func TestDisableReplaceEnvVars(t *testing.T) { - data := []byte(`{"foo": "bar", "baz": {"bar": "test/${test}"}}`) - - tests := []struct { - path []string - value string - opts []reader.Option - }{ - { - []string{"baz", "bar"}, - "test/", - nil, - }, - { - []string{"baz", "bar"}, - "test/${test}", - []reader.Option{reader.WithDisableReplaceEnvVars()}, - }, - } - - for _, test := range tests { - values := newTestValues(t, data, test.opts...) - - if v := values.Get(test.path...).String(""); v != test.value { - t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path) - } - } -} - -func newTestValues(t *testing.T, data []byte, opts ...reader.Option) reader.Values { - r := NewReader(opts...) - - c, err := r.Merge(&source.ChangeSet{Data: data}, &source.ChangeSet{}) - if err != nil { - t.Fatal(err) - } - - values, err := r.Values(c) - if err != nil { - t.Fatal(err) - } - - return values -} diff --git a/config/reader/json/values.go b/config/reader/json/values.go deleted file mode 100644 index e6224faa..00000000 --- a/config/reader/json/values.go +++ /dev/null @@ -1,205 +0,0 @@ -package json - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - simple "github.com/bitly/go-simplejson" - "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/source" -) - -type jsonValues struct { - ch *source.ChangeSet - sj *simple.Json -} - -type jsonValue struct { - *simple.Json -} - -func newValues(ch *source.ChangeSet, opts reader.Options) (reader.Values, error) { - sj := simple.New() - data := ch.Data - - if !opts.DisableReplaceEnvVars { - data, _ = reader.ReplaceEnvVars(ch.Data) - } - - if err := sj.UnmarshalJSON(data); err != nil { - sj.SetPath(nil, string(ch.Data)) - } - return &jsonValues{ch, sj}, nil -} - -func (j *jsonValues) Get(path ...string) reader.Value { - return &jsonValue{j.sj.GetPath(path...)} -} - -func (j *jsonValues) Del(path ...string) { - // delete the tree? - if len(path) == 0 { - j.sj = simple.New() - return - } - - if len(path) == 1 { - j.sj.Del(path[0]) - return - } - - vals := j.sj.GetPath(path[:len(path)-1]...) - vals.Del(path[len(path)-1]) - j.sj.SetPath(path[:len(path)-1], vals.Interface()) -} - -func (j *jsonValues) Set(val interface{}, path ...string) { - j.sj.SetPath(path, val) -} - -func (j *jsonValues) Bytes() []byte { - b, _ := j.sj.MarshalJSON() - return b -} - -func (j *jsonValues) Map() map[string]interface{} { - m, _ := j.sj.Map() - return m -} - -func (j *jsonValues) Scan(v interface{}) error { - b, err := j.sj.MarshalJSON() - if err != nil { - return err - } - return json.Unmarshal(b, v) -} - -func (j *jsonValues) String() string { - return "json" -} - -func (j *jsonValue) Bool(def bool) bool { - b, err := j.Json.Bool() - if err == nil { - return b - } - - str, ok := j.Interface().(string) - if !ok { - return def - } - - b, err = strconv.ParseBool(str) - if err != nil { - return def - } - - return b -} - -func (j *jsonValue) Int(def int) int { - i, err := j.Json.Int() - if err == nil { - return i - } - - str, ok := j.Interface().(string) - if !ok { - return def - } - - i, err = strconv.Atoi(str) - if err != nil { - return def - } - - return i -} - -func (j *jsonValue) String(def string) string { - return j.Json.MustString(def) -} - -func (j *jsonValue) Float64(def float64) float64 { - f, err := j.Json.Float64() - if err == nil { - return f - } - - str, ok := j.Interface().(string) - if !ok { - return def - } - - f, err = strconv.ParseFloat(str, 64) - if err != nil { - return def - } - - return f -} - -func (j *jsonValue) Duration(def time.Duration) time.Duration { - v, err := j.Json.String() - if err != nil { - return def - } - - value, err := time.ParseDuration(v) - if err != nil { - return def - } - - return value -} - -func (j *jsonValue) StringSlice(def []string) []string { - v, err := j.Json.String() - if err == nil { - sl := strings.Split(v, ",") - if len(sl) > 1 { - return sl - } - } - return j.Json.MustStringArray(def) -} - -func (j *jsonValue) StringMap(def map[string]string) map[string]string { - m, err := j.Json.Map() - if err != nil { - return def - } - - res := map[string]string{} - - for k, v := range m { - res[k] = fmt.Sprintf("%v", v) - } - - return res -} - -func (j *jsonValue) Scan(v interface{}) error { - b, err := j.Json.MarshalJSON() - if err != nil { - return err - } - return json.Unmarshal(b, v) -} - -func (j *jsonValue) Bytes() []byte { - b, err := j.Json.Bytes() - if err != nil { - // try return marshalled - b, err = j.Json.MarshalJSON() - if err != nil { - return []byte{} - } - return b - } - return b -} diff --git a/config/reader/json/values_test.go b/config/reader/json/values_test.go deleted file mode 100644 index 15748a48..00000000 --- a/config/reader/json/values_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package json - -import ( - "reflect" - "testing" - - "github.com/unistack-org/micro/v3/config/reader" - "github.com/unistack-org/micro/v3/config/source" -) - -func TestValues(t *testing.T) { - emptyStr := "" - testData := []struct { - csdata []byte - path []string - accepter interface{} - value interface{} - }{ - { - []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`), - []string{"foo"}, - emptyStr, - "bar", - }, - { - []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`), - []string{"baz", "bar"}, - emptyStr, - "cat", - }, - } - - for idx, test := range testData { - values, err := newValues(&source.ChangeSet{ - Data: test.csdata, - }, reader.Options{}) - if err != nil { - t.Fatal(err) - } - - err = values.Get(test.path...).Scan(&test.accepter) - if err != nil { - t.Fatal(err) - } - if test.accepter != test.value { - t.Fatalf("No.%d Expected %v got %v for path %v", idx, test.value, test.accepter, test.path) - } - } -} - -func TestStructArray(t *testing.T) { - type T struct { - Foo string - } - - emptyTSlice := []T{} - - testData := []struct { - csdata []byte - accepter []T - value []T - }{ - { - []byte(`[{"foo": "bar"}]`), - emptyTSlice, - []T{{Foo: "bar"}}, - }, - } - - for idx, test := range testData { - values, err := newValues(&source.ChangeSet{ - Data: test.csdata, - }, reader.Options{}) - if err != nil { - t.Fatal(err) - } - - err = values.Get().Scan(&test.accepter) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(test.accepter, test.value) { - t.Fatalf("No.%d Expected %v got %v", idx, test.value, test.accepter) - } - } -} diff --git a/config/source/env/README.md b/config/source/env/README.md deleted file mode 100644 index 61a18f8d..00000000 --- a/config/source/env/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# Env Source - -The env source reads config from environment variables - -## Format - -We expect environment variables to be in the standard format of FOO=bar - -Keys are converted to lowercase and split on underscore. - - -### Example - -``` -DATABASE_ADDRESS=127.0.0.1 -DATABASE_PORT=3306 -``` - -Becomes - -```json -{ - "database": { - "address": "127.0.0.1", - "port": 3306 - } -} -``` - -## Prefixes - -Environment variables can be namespaced so we only have access to a subset. Two options are available: - -``` -WithPrefix(p ...string) -WithStrippedPrefix(p ...string) -``` - -The former will preserve the prefix and make it a top level key in the config. The latter eliminates the prefix, reducing the nesting by one. - -#### Example: - -Given ENVs of: - -``` -APP_DATABASE_ADDRESS=127.0.0.1 -APP_DATABASE_PORT=3306 -VAULT_ADDR=vault:1337 -``` - -and a source initialized as follows: - -``` -src := env.NewSource( - env.WithPrefix("VAULT"), - env.WithStrippedPrefix("APP"), -) -``` - -The resulting config will be: - -``` -{ - "database": { - "address": "127.0.0.1", - "port": 3306 - }, - "vault": { - "addr": "vault:1337" - } -} -``` - - -## New Source - -Specify source with data - -```go -src := env.NewSource( - // optionally specify prefix - env.WithPrefix("MICRO"), -) -``` - -## Load Source - -Load the source into config - -```go -// Create new config -conf := config.NewConfig() - -// Load env source -conf.Load(src) -``` diff --git a/config/source/env/env.go b/config/source/env/env.go deleted file mode 100644 index 9bd839f8..00000000 --- a/config/source/env/env.go +++ /dev/null @@ -1,146 +0,0 @@ -package env - -import ( - "os" - "strconv" - "strings" - "time" - - "github.com/imdario/mergo" - "github.com/unistack-org/micro/v3/config/source" -) - -var ( - DefaultPrefixes = []string{} -) - -type env struct { - prefixes []string - strippedPrefixes []string - opts source.Options -} - -func (e *env) Read() (*source.ChangeSet, error) { - var changes map[string]interface{} - - for _, env := range os.Environ() { - - if len(e.prefixes) > 0 || len(e.strippedPrefixes) > 0 { - notFound := true - - if _, ok := matchPrefix(e.prefixes, env); ok { - notFound = false - } - - if match, ok := matchPrefix(e.strippedPrefixes, env); ok { - env = strings.TrimPrefix(env, match) - notFound = false - } - - if notFound { - continue - } - } - - pair := strings.SplitN(env, "=", 2) - value := pair[1] - keys := strings.Split(strings.ToLower(pair[0]), "_") - reverse(keys) - - tmp := make(map[string]interface{}) - for i, k := range keys { - if i == 0 { - if intValue, err := strconv.Atoi(value); err == nil { - tmp[k] = intValue - } else if boolValue, err := strconv.ParseBool(value); err == nil { - tmp[k] = boolValue - } else { - tmp[k] = value - } - continue - } - - tmp = map[string]interface{}{k: tmp} - } - - if err := mergo.Map(&changes, tmp); err != nil { - return nil, err - } - } - - b, err := e.opts.Encoder.Encode(changes) - if err != nil { - return nil, err - } - - cs := &source.ChangeSet{ - Format: e.opts.Encoder.String(), - Data: b, - Timestamp: time.Now(), - Source: e.String(), - } - cs.Checksum = cs.Sum() - - return cs, nil -} - -func matchPrefix(pre []string, s string) (string, bool) { - for _, p := range pre { - if strings.HasPrefix(s, p) { - return p, true - } - } - - return "", false -} - -func reverse(ss []string) { - for i := len(ss)/2 - 1; i >= 0; i-- { - opp := len(ss) - 1 - i - ss[i], ss[opp] = ss[opp], ss[i] - } -} - -func (e *env) Watch() (source.Watcher, error) { - return newWatcher() -} - -func (e *env) Write(cs *source.ChangeSet) error { - return nil -} - -func (e *env) String() string { - return "env" -} - -// NewSource returns a config source for parsing ENV variables. -// Underscores are delimiters for nesting, and all keys are lowercased. -// -// Example: -// "DATABASE_SERVER_HOST=localhost" will convert to -// -// { -// "database": { -// "server": { -// "host": "localhost" -// } -// } -// } -func NewSource(opts ...source.Option) source.Source { - options := source.NewOptions(opts...) - - var sp []string - var pre []string - if p, ok := options.Context.Value(strippedPrefixKey{}).([]string); ok { - sp = p - } - - if p, ok := options.Context.Value(prefixKey{}).([]string); ok { - pre = p - } - - if len(sp) > 0 || len(pre) > 0 { - pre = append(pre, DefaultPrefixes...) - } - return &env{prefixes: pre, strippedPrefixes: sp, opts: options} -} diff --git a/config/source/env/env_test.go b/config/source/env/env_test.go deleted file mode 100644 index eb5777a1..00000000 --- a/config/source/env/env_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package env - -import ( - "encoding/json" - "os" - "testing" - "time" - - "github.com/unistack-org/micro/v3/config/source" -) - -func TestEnv_Read(t *testing.T) { - expected := map[string]map[string]string{ - "database": { - "host": "localhost", - "password": "password", - "datasource": "user:password@tcp(localhost:port)/db?charset=utf8mb4&parseTime=True&loc=Local", - }, - } - - os.Setenv("DATABASE_HOST", "localhost") - os.Setenv("DATABASE_PASSWORD", "password") - os.Setenv("DATABASE_DATASOURCE", "user:password@tcp(localhost:port)/db?charset=utf8mb4&parseTime=True&loc=Local") - - source := NewSource() - c, err := source.Read() - if err != nil { - t.Error(err) - } - - var actual map[string]interface{} - if err := json.Unmarshal(c.Data, &actual); err != nil { - t.Error(err) - } - - actualDB := actual["database"].(map[string]interface{}) - - for k, v := range expected["database"] { - a := actualDB[k] - - if a != v { - t.Errorf("expected %v got %v", v, a) - } - } -} - -func TestEnvvar_Prefixes(t *testing.T) { - os.Setenv("APP_DATABASE_HOST", "localhost") - os.Setenv("APP_DATABASE_PASSWORD", "password") - os.Setenv("VAULT_ADDR", "vault:1337") - os.Setenv("MICRO_REGISTRY", "mdns") - - var prefixtests = []struct { - prefixOpts []source.Option - expectedKeys []string - }{ - {[]source.Option{WithPrefix("APP", "MICRO")}, []string{"app", "micro"}}, - {[]source.Option{WithPrefix("MICRO"), WithStrippedPrefix("APP")}, []string{"database", "micro"}}, - {[]source.Option{WithPrefix("MICRO"), WithStrippedPrefix("APP")}, []string{"database", "micro"}}, - } - - for _, pt := range prefixtests { - source := NewSource(pt.prefixOpts...) - - c, err := source.Read() - if err != nil { - t.Error(err) - } - - var actual map[string]interface{} - if err := json.Unmarshal(c.Data, &actual); err != nil { - t.Error(err) - } - - // assert other prefixes ignored - if l := len(actual); l != len(pt.expectedKeys) { - t.Errorf("expected %v top keys, got %v", len(pt.expectedKeys), l) - } - - for _, k := range pt.expectedKeys { - if !containsKey(actual, k) { - t.Errorf("expected key %v, not found", k) - } - } - } -} - -func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) { - src := NewSource(WithStrippedPrefix("GOMICRO_")) - w, err := src.Watch() - if err != nil { - t.Error(err) - } - - go func() { - time.Sleep(50 * time.Millisecond) - w.Stop() - }() - - if _, err := w.Next(); err != source.ErrWatcherStopped { - t.Errorf("expected watcher stopped error, got %v", err) - } -} - -func containsKey(m map[string]interface{}, s string) bool { - for k := range m { - if k == s { - return true - } - } - return false -} diff --git a/config/source/env/options.go b/config/source/env/options.go deleted file mode 100644 index ae8f9aed..00000000 --- a/config/source/env/options.go +++ /dev/null @@ -1,50 +0,0 @@ -package env - -import ( - "context" - - "strings" - - "github.com/unistack-org/micro/v3/config/source" -) - -type strippedPrefixKey struct{} -type prefixKey struct{} - -// WithStrippedPrefix sets the environment variable prefixes to scope to. -// These prefixes will be removed from the actual config entries. -func WithStrippedPrefix(p ...string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - - o.Context = context.WithValue(o.Context, strippedPrefixKey{}, appendUnderscore(p)) - } -} - -// WithPrefix sets the environment variable prefixes to scope to. -// These prefixes will not be removed. Each prefix will be considered a top level config entry. -func WithPrefix(p ...string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, prefixKey{}, appendUnderscore(p)) - } -} - -func appendUnderscore(prefixes []string) []string { - //nolint:prealloc - var result []string - for _, p := range prefixes { - if !strings.HasSuffix(p, "_") { - result = append(result, p+"_") - continue - } - - result = append(result, p) - } - - return result -} diff --git a/config/source/env/watcher.go b/config/source/env/watcher.go deleted file mode 100644 index cb2c1d14..00000000 --- a/config/source/env/watcher.go +++ /dev/null @@ -1,24 +0,0 @@ -package env - -import ( - "github.com/unistack-org/micro/v3/config/source" -) - -type watcher struct { - exit chan struct{} -} - -func (w *watcher) Next() (*source.ChangeSet, error) { - <-w.exit - - return nil, source.ErrWatcherStopped -} - -func (w *watcher) Stop() error { - close(w.exit) - return nil -} - -func newWatcher() (source.Watcher, error) { - return &watcher{exit: make(chan struct{})}, nil -} diff --git a/config/source/etcd/README.md b/config/source/etcd/README.md deleted file mode 100644 index a3025ad4..00000000 --- a/config/source/etcd/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Etcd Source - -The etcd source reads config from etcd key/values - -This source supports etcd version 3 and beyond. - -## Etcd Format - -The etcd source expects keys under the default prefix `/micro/config` (prefix can be changed) - -Values are expected to be JSON - -``` -// set database -etcdctl put /micro/config/database '{"address": "10.0.0.1", "port": 3306}' -// set cache -etcdctl put /micro/config/cache '{"address": "10.0.0.2", "port": 6379}' -``` - -Keys are split on `/` so access becomes - -``` -conf.Get("micro", "config", "database") -``` - -## New Source - -Specify source with data - -```go -etcdSource := etcd.NewSource( - // optionally specify etcd address; default to localhost:8500 - etcd.WithAddress("10.0.0.10:8500"), - // optionally specify prefix; defaults to /micro/config - etcd.WithPrefix("/my/prefix"), - // optionally strip the provided prefix from the keys, defaults to false - etcd.StripPrefix(true), -) -``` - -## Load Source - -Load the source into config - -```go -// Create new config -conf := config.NewConfig() - -// Load file source -conf.Load(etcdSource) -``` diff --git a/config/source/etcd/etcd.go b/config/source/etcd/etcd.go deleted file mode 100644 index cbb6ff30..00000000 --- a/config/source/etcd/etcd.go +++ /dev/null @@ -1,145 +0,0 @@ -package etcd - -import ( - "context" - "fmt" - "net" - "time" - - cetcd "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/mvcc/mvccpb" - "github.com/unistack-org/micro/v3/config/source" -) - -// Currently a single etcd reader -type etcd struct { - prefix string - stripPrefix string - opts source.Options - client *cetcd.Client - cerr error -} - -var ( - DefaultPrefix = "/micro/config/" -) - -func (c *etcd) Read() (*source.ChangeSet, error) { - if c.cerr != nil { - return nil, c.cerr - } - - rsp, err := c.client.Get(context.Background(), c.prefix, cetcd.WithPrefix()) - if err != nil { - return nil, err - } - - if rsp == nil || len(rsp.Kvs) == 0 { - return nil, fmt.Errorf("source not found: %s", c.prefix) - } - - kvs := make([]*mvccpb.KeyValue, 0, len(rsp.Kvs)) - for _, v := range rsp.Kvs { - kvs = append(kvs, (*mvccpb.KeyValue)(v)) - } - - data := makeMap(c.opts.Encoder, kvs, c.stripPrefix) - - b, err := c.opts.Encoder.Encode(data) - if err != nil { - return nil, fmt.Errorf("error reading source: %v", err) - } - - cs := &source.ChangeSet{ - Timestamp: time.Now(), - Source: c.String(), - Data: b, - Format: c.opts.Encoder.String(), - } - cs.Checksum = cs.Sum() - - return cs, nil -} - -func (c *etcd) String() string { - return "etcd" -} - -func (c *etcd) Watch() (source.Watcher, error) { - if c.cerr != nil { - return nil, c.cerr - } - cs, err := c.Read() - if err != nil { - return nil, err - } - return newWatcher(c.prefix, c.stripPrefix, c.client.Watcher, cs, c.opts) -} - -func (c *etcd) Write(cs *source.ChangeSet) error { - return nil -} - -func NewSource(opts ...source.Option) source.Source { - options := source.NewOptions(opts...) - - var endpoints []string - - // check if there are any addrs - addrs, ok := options.Context.Value(addressKey{}).([]string) - if ok { - for _, a := range addrs { - addr, port, err := net.SplitHostPort(a) - if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" { - port = "2379" - addr = a - endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port)) - } else if err == nil { - endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port)) - } - } - } - - if len(endpoints) == 0 { - endpoints = []string{"localhost:2379"} - } - - // check dial timeout option - dialTimeout, ok := options.Context.Value(dialTimeoutKey{}).(time.Duration) - if !ok { - dialTimeout = 3 * time.Second // default dial timeout - } - - config := cetcd.Config{ - Endpoints: endpoints, - DialTimeout: dialTimeout, - } - - u, ok := options.Context.Value(authKey{}).(*authCreds) - if ok { - config.Username = u.Username - config.Password = u.Password - } - - // use default config - client, err := cetcd.New(config) - - prefix := DefaultPrefix - sp := "" - f, ok := options.Context.Value(prefixKey{}).(string) - if ok { - prefix = f - } - - if b, ok := options.Context.Value(stripPrefixKey{}).(bool); ok && b { - sp = prefix - } - - return &etcd{ - prefix: prefix, - stripPrefix: sp, - opts: options, - client: client, - cerr: err, - } -} diff --git a/config/source/etcd/options.go b/config/source/etcd/options.go deleted file mode 100644 index 395ec109..00000000 --- a/config/source/etcd/options.go +++ /dev/null @@ -1,70 +0,0 @@ -package etcd - -import ( - "context" - "time" - - "github.com/unistack-org/micro/v3/config/source" -) - -type addressKey struct{} -type prefixKey struct{} -type stripPrefixKey struct{} -type authKey struct{} -type dialTimeoutKey struct{} - -type authCreds struct { - Username string - Password string -} - -// WithAddress sets the etcd address -func WithAddress(a ...string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, addressKey{}, a) - } -} - -// WithPrefix sets the key prefix to use -func WithPrefix(p string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, prefixKey{}, p) - } -} - -// StripPrefix indicates whether to remove the prefix from config entries, or leave it in place. -func StripPrefix(strip bool) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - - o.Context = context.WithValue(o.Context, stripPrefixKey{}, strip) - } -} - -// Auth allows you to specify username/password -func Auth(username, password string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, authKey{}, &authCreds{Username: username, Password: password}) - } -} - -// WithDialTimeout set the time out for dialing to etcd -func WithDialTimeout(timeout time.Duration) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, dialTimeoutKey{}, timeout) - } -} diff --git a/config/source/etcd/util.go b/config/source/etcd/util.go deleted file mode 100644 index 5784af7b..00000000 --- a/config/source/etcd/util.go +++ /dev/null @@ -1,89 +0,0 @@ -package etcd - -import ( - "strings" - - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/mvcc/mvccpb" - "github.com/unistack-org/micro/v3/config/encoder" -) - -func makeEvMap(e encoder.Encoder, data map[string]interface{}, kv []*clientv3.Event, stripPrefix string) map[string]interface{} { - if data == nil { - data = make(map[string]interface{}) - } - - for _, v := range kv { - switch mvccpb.Event_EventType(v.Type) { - case mvccpb.DELETE: - data = update(e, data, (*mvccpb.KeyValue)(v.Kv), "delete", stripPrefix) - default: - data = update(e, data, (*mvccpb.KeyValue)(v.Kv), "insert", stripPrefix) - } - } - - return data -} - -func makeMap(e encoder.Encoder, kv []*mvccpb.KeyValue, stripPrefix string) map[string]interface{} { - data := make(map[string]interface{}) - - for _, v := range kv { - data = update(e, data, v, "put", stripPrefix) - } - - return data -} - -func update(e encoder.Encoder, data map[string]interface{}, v *mvccpb.KeyValue, action, stripPrefix string) map[string]interface{} { - // remove prefix if non empty, and ensure leading / is removed as well - vkey := strings.TrimPrefix(strings.TrimPrefix(string(v.Key), stripPrefix), "/") - // split on prefix - haveSplit := strings.Contains(vkey, "/") - keys := strings.Split(vkey, "/") - - var vals interface{} - e.Decode(v.Value, &vals) - - if !haveSplit && len(keys) == 1 { - switch action { - case "delete": - data = make(map[string]interface{}) - default: - v, ok := vals.(map[string]interface{}) - if ok { - data = v - } - } - return data - } - - // set data for first iteration - kvals := data - // iterate the keys and make maps - for i, k := range keys { - kval, ok := kvals[k].(map[string]interface{}) - if !ok { - // create next map - kval = make(map[string]interface{}) - // set it - kvals[k] = kval - } - - // last key: write vals - if l := len(keys) - 1; i == l { - switch action { - case "delete": - delete(kvals, k) - default: - kvals[k] = vals - } - break - } - - // set kvals for next iterator - kvals = kval - } - - return data -} diff --git a/config/source/etcd/watcher.go b/config/source/etcd/watcher.go deleted file mode 100644 index 8162c3fb..00000000 --- a/config/source/etcd/watcher.go +++ /dev/null @@ -1,113 +0,0 @@ -package etcd - -import ( - "context" - "errors" - "sync" - "time" - - cetcd "github.com/coreos/etcd/clientv3" - "github.com/unistack-org/micro/v3/config/source" -) - -type watcher struct { - opts source.Options - name string - stripPrefix string - - sync.RWMutex - cs *source.ChangeSet - - ch chan *source.ChangeSet - exit chan bool -} - -func newWatcher(key, strip string, wc cetcd.Watcher, cs *source.ChangeSet, opts source.Options) (source.Watcher, error) { - w := &watcher{ - opts: opts, - name: "etcd", - stripPrefix: strip, - cs: cs, - ch: make(chan *source.ChangeSet), - exit: make(chan bool), - } - - ch := wc.Watch(context.Background(), key, cetcd.WithPrefix()) - - go w.run(wc, ch) - - return w, nil -} - -func (w *watcher) handle(evs []*cetcd.Event) { - w.RLock() - data := w.cs.Data - w.RUnlock() - - var vals map[string]interface{} - - // unpackage existing changeset - if err := w.opts.Encoder.Decode(data, &vals); err != nil { - return - } - - // update base changeset - d := makeEvMap(w.opts.Encoder, vals, evs, w.stripPrefix) - - // pack the changeset - b, err := w.opts.Encoder.Encode(d) - if err != nil { - return - } - - // create new changeset - cs := &source.ChangeSet{ - Timestamp: time.Now(), - Source: w.name, - Data: b, - Format: w.opts.Encoder.String(), - } - cs.Checksum = cs.Sum() - - // set base change set - w.Lock() - w.cs = cs - w.Unlock() - - // send update - w.ch <- cs -} - -func (w *watcher) run(wc cetcd.Watcher, ch cetcd.WatchChan) { - for { - select { - case rsp, ok := <-ch: - if !ok { - return - } - w.handle(rsp.Events) - case <-w.exit: - wc.Close() - return - } - } -} - -func (w *watcher) Next() (*source.ChangeSet, error) { - select { - case cs := <-w.ch: - return cs, nil - case <-w.exit: - return nil, errors.New("watcher stopped") - } -} - -func (w *watcher) Stop() error { - select { - case <-w.exit: - return nil - default: - close(w.exit) - } - return nil -} diff --git a/config/source/file/README.md b/config/source/file/README.md deleted file mode 100644 index 89c0930c..00000000 --- a/config/source/file/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# File Source - -The file source reads config from a file. - -It uses the File extension to determine the Format e.g `config.yaml` has the yaml format. -It does not make use of encoders or interpet the file data. If a file extension is not present -the source Format will default to the Encoder in options. - -## Example - -A config file format in json - -```json -{ - "hosts": { - "database": { - "address": "10.0.0.1", - "port": 3306 - }, - "cache": { - "address": "10.0.0.2", - "port": 6379 - } - } -} -``` - -## New Source - -Specify file source with path to file. Path is optional and will default to `config.json` - -```go -fileSource := file.NewSource( - file.WithPath("/tmp/config.json"), -) -``` - -## File Format - -To load different file formats e.g yaml, toml, xml simply specify them with their extension - -``` -fileSource := file.NewSource( - file.WithPath("/tmp/config.yaml"), -) -``` - -If you want to specify a file without extension, ensure you set the encoder to the same format - -``` -e := toml.NewEncoder() - -fileSource := file.NewSource( - file.WithPath("/tmp/config"), - source.WithEncoder(e), -) -``` - -## Load Source - -Load the source into config - -```go -// Create new config -conf := config.NewConfig() - -// Load file source -conf.Load(fileSource) -``` - diff --git a/config/source/file/file.go b/config/source/file/file.go deleted file mode 100644 index aba532b4..00000000 --- a/config/source/file/file.go +++ /dev/null @@ -1,70 +0,0 @@ -// Package file is a file source. Expected format is json -package file - -import ( - "io/ioutil" - "os" - - "github.com/unistack-org/micro/v3/config/source" -) - -type file struct { - path string - data []byte - opts source.Options -} - -var ( - DefaultPath = "config.json" -) - -func (f *file) Read() (*source.ChangeSet, error) { - fh, err := os.Open(f.path) - if err != nil { - return nil, err - } - defer fh.Close() - b, err := ioutil.ReadAll(fh) - if err != nil { - return nil, err - } - info, err := fh.Stat() - if err != nil { - return nil, err - } - - cs := &source.ChangeSet{ - Format: format(f.path, f.opts.Encoder), - Source: f.String(), - Timestamp: info.ModTime(), - Data: b, - } - cs.Checksum = cs.Sum() - - return cs, nil -} - -func (f *file) String() string { - return "file" -} - -func (f *file) Watch() (source.Watcher, error) { - if _, err := os.Stat(f.path); err != nil { - return nil, err - } - return newWatcher(f) -} - -func (f *file) Write(cs *source.ChangeSet) error { - return nil -} - -func NewSource(opts ...source.Option) source.Source { - options := source.NewOptions(opts...) - path := DefaultPath - f, ok := options.Context.Value(filePathKey{}).(string) - if ok { - path = f - } - return &file{opts: options, path: path} -} diff --git a/config/source/file/file_test.go b/config/source/file/file_test.go deleted file mode 100644 index afc0c546..00000000 --- a/config/source/file/file_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package file_test - -import ( - "fmt" - "os" - "path/filepath" - "testing" - "time" - - "github.com/unistack-org/micro/v3/config" - "github.com/unistack-org/micro/v3/config/source/file" -) - -func TestConfig(t *testing.T) { - data := []byte(`{"foo": "bar"}`) - path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano())) - fh, err := os.Create(path) - if err != nil { - t.Error(err) - } - defer func() { - fh.Close() - os.Remove(path) - }() - _, err = fh.Write(data) - if err != nil { - t.Error(err) - } - - conf, err := config.NewConfig() - if err != nil { - t.Fatal(err) - } - conf.Load(file.NewSource(file.WithPath(path))) - // simulate multiple close - go conf.Close() - go conf.Close() -} - -func TestFile(t *testing.T) { - data := []byte(`{"foo": "bar"}`) - path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano())) - fh, err := os.Create(path) - if err != nil { - t.Error(err) - } - defer func() { - fh.Close() - os.Remove(path) - }() - - _, err = fh.Write(data) - if err != nil { - t.Error(err) - } - - f := file.NewSource(file.WithPath(path)) - c, err := f.Read() - if err != nil { - t.Error(err) - } - if string(c.Data) != string(data) { - t.Logf("%+v", c) - t.Error("data from file does not match") - } -} diff --git a/config/source/file/format.go b/config/source/file/format.go deleted file mode 100644 index f6a68605..00000000 --- a/config/source/file/format.go +++ /dev/null @@ -1,15 +0,0 @@ -package file - -import ( - "strings" - - "github.com/unistack-org/micro/v3/config/encoder" -) - -func format(p string, e encoder.Encoder) string { - parts := strings.Split(p, ".") - if len(parts) > 1 { - return parts[len(parts)-1] - } - return e.String() -} diff --git a/config/source/file/format_test.go b/config/source/file/format_test.go deleted file mode 100644 index ed53f287..00000000 --- a/config/source/file/format_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package file - -import ( - "testing" - - "github.com/unistack-org/micro/v3/config/source" -) - -func TestFormat(t *testing.T) { - opts := source.NewOptions() - e := opts.Encoder - - testCases := []struct { - p string - f string - }{ - {"/foo/bar.json", "json"}, - {"/foo/bar.yaml", "yaml"}, - {"/foo/bar.xml", "xml"}, - {"/foo/bar.conf.ini", "ini"}, - {"conf", e.String()}, - } - - for _, d := range testCases { - f := format(d.p, e) - if f != d.f { - t.Fatalf("%s: expected %s got %s", d.p, d.f, f) - } - } - -} diff --git a/config/source/file/options.go b/config/source/file/options.go deleted file mode 100644 index 78664387..00000000 --- a/config/source/file/options.go +++ /dev/null @@ -1,19 +0,0 @@ -package file - -import ( - "context" - - "github.com/unistack-org/micro/v3/config/source" -) - -type filePathKey struct{} - -// WithPath sets the path to file -func WithPath(p string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, filePathKey{}, p) - } -} diff --git a/config/source/file/watcher.go b/config/source/file/watcher.go deleted file mode 100644 index 9ac152ce..00000000 --- a/config/source/file/watcher.go +++ /dev/null @@ -1,77 +0,0 @@ -//+build !linux - -package file - -import ( - "os" - - "github.com/fsnotify/fsnotify" - "github.com/unistack-org/micro/v3/config/source" -) - -type watcher struct { - f *file - - fw *fsnotify.Watcher - exit chan bool -} - -func newWatcher(f *file) (source.Watcher, error) { - fw, err := fsnotify.NewWatcher() - if err != nil { - return nil, err - } - - fw.Add(f.path) - - return &watcher{ - f: f, - fw: fw, - exit: make(chan bool), - }, nil -} - -func (w *watcher) Next() (*source.ChangeSet, error) { - // is it closed? - select { - case <-w.exit: - return nil, source.ErrWatcherStopped - default: - } - - for { - // try get the event - select { - case event, _ := <-w.fw.Events: - if event.Op == fsnotify.Rename { - // check existence of file, and add watch again - _, err := os.Stat(event.Name) - if err == nil || os.IsExist(err) { - w.fw.Add(event.Name) - } - } - - // ARCH: Darwin Kernel Version 18.7.0 - // ioutil.WriteFile truncates it before writing, but the problem is that - // you will receive two events(fsnotify.Chmod and fsnotify.Write). - // We can solve this problem by ignoring fsnotify.Chmod event. - if event.Op&fsnotify.Write != fsnotify.Write { - continue - } - - c, err := w.f.Read() - if err != nil { - return nil, err - } - return c, nil - case err := <-w.fw.Errors: - return nil, err - case <-w.exit: - return nil, source.ErrWatcherStopped - } - } -} - -func (w *watcher) Stop() error { - return w.fw.Close() -} diff --git a/config/source/file/watcher_linux.go b/config/source/file/watcher_linux.go deleted file mode 100644 index f4835c5b..00000000 --- a/config/source/file/watcher_linux.go +++ /dev/null @@ -1,80 +0,0 @@ -//+build linux - -package file - -import ( - "os" - "reflect" - - "github.com/fsnotify/fsnotify" - "github.com/unistack-org/micro/v3/config/source" -) - -type watcher struct { - f *file - - fw *fsnotify.Watcher - exit chan bool -} - -func newWatcher(f *file) (source.Watcher, error) { - fw, err := fsnotify.NewWatcher() - if err != nil { - return nil, err - } - - fw.Add(f.path) - - return &watcher{ - f: f, - fw: fw, - exit: make(chan bool), - }, nil -} - -func (w *watcher) Next() (*source.ChangeSet, error) { - // is it closed? - select { - case <-w.exit: - return nil, source.ErrWatcherStopped - default: - } - - for { - // try get the event - select { - case event, _ := <-w.fw.Events: - if event.Op == fsnotify.Rename { - // check existence of file, and add watch again - _, err := os.Stat(event.Name) - if err == nil || os.IsExist(err) { - w.fw.Add(event.Name) - } - } - - c, err := w.f.Read() - if err != nil { - return nil, err - } - - // ARCH: Linux centos-7.shared 3.10.0-693.5.2.el7.x86_64 - // Sometimes, ioutil.WriteFile triggers multiple fsnotify.Write events, which may be a bug. - - // Detect if the file has changed - if reflect.DeepEqual(c.Data, w.f.data) { - continue - } - w.f.data = c.Data - - return c, nil - case err := <-w.fw.Errors: - return nil, err - case <-w.exit: - return nil, source.ErrWatcherStopped - } - } -} - -func (w *watcher) Stop() error { - return w.fw.Close() -} diff --git a/config/source/flag/README.md b/config/source/flag/README.md deleted file mode 100644 index 79e5cb54..00000000 --- a/config/source/flag/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Flag Source - -The flag source reads config from flags - -## Format - -We expect the use of the `flag` package. Upper case flags will be lower cased. Dashes will be used as delimiters. - -### Example - -``` -dbAddress := flag.String("database_address", "127.0.0.1", "the db address") -dbPort := flag.Int("database_port", 3306, "the db port) -``` - -Becomes - -```json -{ - "database": { - "address": "127.0.0.1", - "port": 3306 - } -} -``` - -## New Source - -```go -flagSource := flag.NewSource( - // optionally enable reading of unset flags and their default - // values into config, defaults to false - IncludeUnset(true) -) -``` - -## Load Source - -Load the source into config - -```go -// Create new config -conf := config.NewConfig() - -// Load flag source -conf.Load(flagSource) -``` diff --git a/config/source/flag/flag.go b/config/source/flag/flag.go deleted file mode 100644 index 0eeb8c4e..00000000 --- a/config/source/flag/flag.go +++ /dev/null @@ -1,101 +0,0 @@ -package flag - -import ( - "errors" - "flag" - "strings" - "time" - - "github.com/imdario/mergo" - "github.com/unistack-org/micro/v3/config/source" -) - -type flagsrc struct { - opts source.Options -} - -func (fs *flagsrc) Read() (*source.ChangeSet, error) { - if !flag.Parsed() { - return nil, errors.New("flags not parsed") - } - - var changes map[string]interface{} - - visitFn := func(f *flag.Flag) { - n := strings.ToLower(f.Name) - keys := strings.FieldsFunc(n, split) - reverse(keys) - - tmp := make(map[string]interface{}) - for i, k := range keys { - if i == 0 { - tmp[k] = f.Value - continue - } - - tmp = map[string]interface{}{k: tmp} - } - - mergo.Map(&changes, tmp) // need to sort error handling - } - - unset, ok := fs.opts.Context.Value(includeUnsetKey{}).(bool) - if ok && unset { - flag.VisitAll(visitFn) - } else { - flag.Visit(visitFn) - } - - b, err := fs.opts.Encoder.Encode(changes) - if err != nil { - return nil, err - } - - cs := &source.ChangeSet{ - Format: fs.opts.Encoder.String(), - Data: b, - Timestamp: time.Now(), - Source: fs.String(), - } - cs.Checksum = cs.Sum() - - return cs, nil -} - -func split(r rune) bool { - return r == '-' || r == '_' -} - -func reverse(ss []string) { - for i := len(ss)/2 - 1; i >= 0; i-- { - opp := len(ss) - 1 - i - ss[i], ss[opp] = ss[opp], ss[i] - } -} - -func (fs *flagsrc) Watch() (source.Watcher, error) { - return source.NewNoopWatcher() -} - -func (fs *flagsrc) Write(cs *source.ChangeSet) error { - return nil -} - -func (fs *flagsrc) String() string { - return "flag" -} - -// NewSource returns a config source for integrating parsed flags. -// Hyphens are delimiters for nesting, and all keys are lowercased. -// -// Example: -// dbhost := flag.String("database-host", "localhost", "the db host name") -// -// { -// "database": { -// "host": "localhost" -// } -// } -func NewSource(opts ...source.Option) source.Source { - return &flagsrc{opts: source.NewOptions(opts...)} -} diff --git a/config/source/flag/flag_test.go b/config/source/flag/flag_test.go deleted file mode 100644 index e412c904..00000000 --- a/config/source/flag/flag_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package flag - -import ( - "encoding/json" - "flag" - "testing" -) - -var ( - dbuser = flag.String("database-user", "default", "db user") - dbhost = flag.String("database-host", "", "db host") - dbpw = flag.String("database-password", "", "db pw") -) - -func initTestFlags() { - flag.Set("database-host", "localhost") - flag.Set("database-password", "some-password") - flag.Parse() -} - -func TestFlagsrc_Read(t *testing.T) { - initTestFlags() - source := NewSource() - c, err := source.Read() - if err != nil { - t.Error(err) - } - - var actual map[string]interface{} - if err := json.Unmarshal(c.Data, &actual); err != nil { - t.Error(err) - } - - actualDB := actual["database"].(map[string]interface{}) - if actualDB["host"] != *dbhost { - t.Errorf("expected %v got %v", *dbhost, actualDB["host"]) - } - - if actualDB["password"] != *dbpw { - t.Errorf("expected %v got %v", *dbpw, actualDB["password"]) - } - - // unset flags should not be loaded - if actualDB["user"] != nil { - t.Errorf("expected %v got %v", nil, actualDB["user"]) - } -} - -func TestFlagsrc_ReadAll(t *testing.T) { - initTestFlags() - source := NewSource(IncludeUnset(true)) - c, err := source.Read() - if err != nil { - t.Error(err) - } - - var actual map[string]interface{} - if err := json.Unmarshal(c.Data, &actual); err != nil { - t.Error(err) - } - - actualDB := actual["database"].(map[string]interface{}) - - // unset flag defaults should be loaded - if actualDB["user"] != *dbuser { - t.Errorf("expected %v got %v", *dbuser, actualDB["user"]) - } -} diff --git a/config/source/flag/options.go b/config/source/flag/options.go deleted file mode 100644 index ee10b322..00000000 --- a/config/source/flag/options.go +++ /dev/null @@ -1,20 +0,0 @@ -package flag - -import ( - "context" - - "github.com/unistack-org/micro/v3/config/source" -) - -type includeUnsetKey struct{} - -// IncludeUnset toggles the loading of unset flags and their respective default values. -// Default behavior is to ignore any unset flags. -func IncludeUnset(b bool) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, includeUnsetKey{}, true) - } -} diff --git a/config/source/memory/README.md b/config/source/memory/README.md deleted file mode 100644 index adef980b..00000000 --- a/config/source/memory/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Memory Source - -The memory source provides in-memory data as a source - -## Memory Format - -The expected data format is json - -```json -data := []byte(`{ - "hosts": { - "database": { - "address": "10.0.0.1", - "port": 3306 - }, - "cache": { - "address": "10.0.0.2", - "port": 6379 - } - } -}`) -``` - -## New Source - -Specify source with data - -```go -memorySource := memory.NewSource( - memory.WithJSON(data), -) -``` - -## Load Source - -Load the source into config - -```go -// Create new config -conf := config.NewConfig() - -// Load memory source -conf.Load(memorySource) -``` diff --git a/config/source/memory/memory.go b/config/source/memory/memory.go deleted file mode 100644 index 0e040938..00000000 --- a/config/source/memory/memory.go +++ /dev/null @@ -1,99 +0,0 @@ -// Package memory is a memory source -package memory - -import ( - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/config/source" -) - -type memory struct { - sync.RWMutex - ChangeSet *source.ChangeSet - Watchers map[string]*watcher -} - -func (s *memory) Read() (*source.ChangeSet, error) { - s.RLock() - cs := &source.ChangeSet{ - Format: s.ChangeSet.Format, - Timestamp: s.ChangeSet.Timestamp, - Data: s.ChangeSet.Data, - Checksum: s.ChangeSet.Checksum, - Source: s.ChangeSet.Source, - } - s.RUnlock() - return cs, nil -} - -func (s *memory) Watch() (source.Watcher, error) { - w := &watcher{ - Id: uuid.New().String(), - Updates: make(chan *source.ChangeSet, 100), - Source: s, - } - - s.Lock() - s.Watchers[w.Id] = w - s.Unlock() - return w, nil -} - -func (m *memory) Write(cs *source.ChangeSet) error { - m.Update(cs) - return nil -} - -// Update allows manual updates of the config data. -func (s *memory) Update(c *source.ChangeSet) { - // don't process nil - if c == nil { - return - } - - // hash the file - s.Lock() - // update changeset - s.ChangeSet = &source.ChangeSet{ - Data: c.Data, - Format: c.Format, - Source: "memory", - Timestamp: time.Now(), - } - s.ChangeSet.Checksum = s.ChangeSet.Sum() - - // update watchers - for _, w := range s.Watchers { - select { - case w.Updates <- s.ChangeSet: - default: - } - } - s.Unlock() -} - -func (s *memory) String() string { - return "memory" -} - -func NewSource(opts ...source.Option) source.Source { - var options source.Options - for _, o := range opts { - o(&options) - } - - s := &memory{ - Watchers: make(map[string]*watcher), - } - - if options.Context != nil { - c, ok := options.Context.Value(changeSetKey{}).(*source.ChangeSet) - if ok { - s.Update(c) - } - } - - return s -} diff --git a/config/source/memory/options.go b/config/source/memory/options.go deleted file mode 100644 index 96c5becd..00000000 --- a/config/source/memory/options.go +++ /dev/null @@ -1,41 +0,0 @@ -package memory - -import ( - "context" - - "github.com/unistack-org/micro/v3/config/source" -) - -type changeSetKey struct{} - -func withData(d []byte, f string) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, changeSetKey{}, &source.ChangeSet{ - Data: d, - Format: f, - }) - } -} - -// WithChangeSet allows a changeset to be set -func WithChangeSet(cs *source.ChangeSet) source.Option { - return func(o *source.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, changeSetKey{}, cs) - } -} - -// WithJSON allows the source data to be set to json -func WithJSON(d []byte) source.Option { - return withData(d, "json") -} - -// WithYAML allows the source data to be set to yaml -func WithYAML(d []byte) source.Option { - return withData(d, "yaml") -} diff --git a/config/source/memory/watcher.go b/config/source/memory/watcher.go deleted file mode 100644 index 725dc049..00000000 --- a/config/source/memory/watcher.go +++ /dev/null @@ -1,23 +0,0 @@ -package memory - -import ( - "github.com/unistack-org/micro/v3/config/source" -) - -type watcher struct { - Id string - Updates chan *source.ChangeSet - Source *memory -} - -func (w *watcher) Next() (*source.ChangeSet, error) { - cs := <-w.Updates - return cs, nil -} - -func (w *watcher) Stop() error { - w.Source.Lock() - delete(w.Source.Watchers, w.Id) - w.Source.Unlock() - return nil -} diff --git a/debug/log/kubernetes/kubernetes_test.go b/debug/log/kubernetes/kubernetes_test.go index 6d15e1c6..178de391 100644 --- a/debug/log/kubernetes/kubernetes_test.go +++ b/debug/log/kubernetes/kubernetes_test.go @@ -13,7 +13,7 @@ import ( ) func TestKubernetes(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { t.Skip() } diff --git a/events/memory/memory.go b/events/memory/memory.go deleted file mode 100644 index a3c8158b..00000000 --- a/events/memory/memory.go +++ /dev/null @@ -1,181 +0,0 @@ -package memory - -import ( - "encoding/json" - "fmt" - "sync" - "time" - - "github.com/google/uuid" - "github.com/pkg/errors" - "github.com/unistack-org/micro/v3/events" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/store" - "github.com/unistack-org/micro/v3/store/memory" -) - -// NewStream returns an initialized memory stream -func NewStream(opts ...Option) (events.Stream, error) { - // parse the options - var options Options - for _, o := range opts { - o(&options) - } - if options.Store == nil { - options.Store = memory.NewStore() - } - - return &mem{store: options.Store}, nil -} - -type subscriber struct { - Queue string - Topic string - Channel chan events.Event -} - -type mem struct { - store store.Store - - subs []*subscriber - sync.RWMutex -} - -func (m *mem) Publish(topic string, msg interface{}, opts ...events.PublishOption) error { - // validate the topic - if len(topic) == 0 { - return events.ErrMissingTopic - } - - // parse the options - options := events.PublishOptions{ - Timestamp: time.Now(), - } - for _, o := range opts { - o(&options) - } - - // encode the message if it's not already encoded - var payload []byte - if p, ok := msg.([]byte); ok { - payload = p - } else { - p, err := json.Marshal(msg) - if err != nil { - return events.ErrEncodingMessage - } - payload = p - } - - // construct the event - event := &events.Event{ - ID: uuid.New().String(), - Topic: topic, - Timestamp: options.Timestamp, - Metadata: options.Metadata, - Payload: payload, - } - - // serialize the event to bytes - bytes, err := json.Marshal(event) - if err != nil { - return errors.Wrap(err, "Error encoding event") - } - - // write to the store - key := fmt.Sprintf("%v/%v", event.Topic, event.ID) - if err := m.store.Write(&store.Record{Key: key, Value: bytes}); err != nil { - return errors.Wrap(err, "Error writing event to store") - } - - // send to the subscribers async - go m.handleEvent(event) - - return nil -} - -func (m *mem) Subscribe(topic string, opts ...events.SubscribeOption) (<-chan events.Event, error) { - // validate the topic - if len(topic) == 0 { - return nil, events.ErrMissingTopic - } - - // parse the options - options := events.SubscribeOptions{ - Queue: uuid.New().String(), - } - for _, o := range opts { - o(&options) - } - - // setup the subscriber - sub := &subscriber{ - Channel: make(chan events.Event), - Topic: topic, - Queue: options.Queue, - } - - // register the subscriber - m.Lock() - m.subs = append(m.subs, sub) - m.Unlock() - - // lookup previous events if the start time option was passed - if options.StartAtTime.Unix() > 0 { - go m.lookupPreviousEvents(sub, options.StartAtTime) - } - - // return the channel - return sub.Channel, nil -} - -// lookupPreviousEvents finds events for a subscriber which occured before a given time and sends -// them into the subscribers channel -func (m *mem) lookupPreviousEvents(sub *subscriber, startTime time.Time) { - // lookup all events which match the topic (a blank topic will return all results) - recs, err := m.store.Read(sub.Topic+"/", store.ReadPrefix()) - if err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("Error looking up previous events: %v", err) - return - } else if err != nil { - return - } - - // loop through the records and send it to the channel if it matches - for _, r := range recs { - var ev events.Event - if err := json.Unmarshal(r.Value, &ev); err != nil { - continue - } - if ev.Timestamp.Unix() < startTime.Unix() { - continue - } - - sub.Channel <- ev - } -} - -// handleEvents sends the event to any registered subscribers. -func (m *mem) handleEvent(ev *events.Event) { - m.RLock() - subs := m.subs - m.RUnlock() - - // filteredSubs is a KV map of the queue name and subscribers. This is used to prevent a message - // being sent to two subscribers with the same queue. - filteredSubs := map[string]*subscriber{} - - // filter down to subscribers who are interested in this topic - for _, sub := range subs { - if len(sub.Topic) == 0 || sub.Topic == ev.Topic { - filteredSubs[sub.Queue] = sub - } - } - - // send the message to each channel async (since one channel might be blocked) - for _, sub := range subs { - go func(s *subscriber) { - s.Channel <- *ev - }(sub) - } -} diff --git a/events/memory/memory_test.go b/events/memory/memory_test.go deleted file mode 100644 index c84d0578..00000000 --- a/events/memory/memory_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package memory - -import ( - "sync" - "testing" - "time" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/unistack-org/micro/v3/events" -) - -type testPayload struct { - Message string -} - -func TestStream(t *testing.T) { - stream, err := NewStream() - assert.Nilf(t, err, "NewStream should not return an error") - assert.NotNilf(t, stream, "NewStream should return a stream object") - - // TestMissingTopic will test the topic validation on publish - t.Run("TestMissingTopic", func(t *testing.T) { - err := stream.Publish("", nil) - assert.Equalf(t, err, events.ErrMissingTopic, "Publishing to a blank topic should return an error") - }) - - // TestSubscribeTopic will publish a message to the test topic. The subscriber will subscribe to the - // same test topic. - t.Run("TestSubscribeTopic", func(t *testing.T) { - payload := &testPayload{Message: "HelloWorld"} - metadata := map[string]string{"foo": "bar"} - - // create the subscriber - evChan, err := stream.Subscribe("test") - assert.Nilf(t, err, "Subscribe should not return an error") - - // setup the subscriber async - var wg sync.WaitGroup - - go func() { - timeout := time.NewTimer(time.Millisecond * 250) - - select { - case event, _ := <-evChan: - assert.NotNilf(t, event, "The message was nil") - assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") - - var result testPayload - err = event.Unmarshal(&result) - assert.Nil(t, err, "Error decoding result") - assert.Equal(t, result, *payload, "Payload didn't match") - - wg.Done() - case <-timeout.C: - t.Fatalf("Event was not recieved") - } - }() - - err = stream.Publish("test", payload, events.WithMetadata(metadata)) - assert.Nil(t, err, "Publishing a valid message should not return an error") - wg.Add(1) - - // wait for the subscriber to recieve the message or timeout - wg.Wait() - }) - - // TestSubscribeQueue will publish a message to a random topic. Two subscribers will then consume - // the message from the firehose topic with different queues. The second subscriber will be registered - // after the message is published to test durability. - t.Run("TestSubscribeQueue", func(t *testing.T) { - topic := uuid.New().String() - payload := &testPayload{Message: "HelloWorld"} - metadata := map[string]string{"foo": "bar"} - - // create the first subscriber - evChan1, err := stream.Subscribe(topic) - assert.Nilf(t, err, "Subscribe should not return an error") - - // setup the subscriber async - var wg sync.WaitGroup - - go func() { - timeout := time.NewTimer(time.Millisecond * 250) - - select { - case event, _ := <-evChan1: - assert.NotNilf(t, event, "The message was nil") - assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") - - var result testPayload - err = event.Unmarshal(&result) - assert.Nil(t, err, "Error decoding result") - assert.Equal(t, result, *payload, "Payload didn't match") - - wg.Done() - case <-timeout.C: - t.Fatalf("Event was not recieved") - } - }() - - err = stream.Publish(topic, payload, events.WithMetadata(metadata)) - assert.Nil(t, err, "Publishing a valid message should not return an error") - wg.Add(2) - - // create the second subscriber - evChan2, err := stream.Subscribe(topic, - events.WithQueue("second_queue"), - events.WithStartAtTime(time.Now().Add(time.Minute*-1)), - ) - assert.Nilf(t, err, "Subscribe should not return an error") - - go func() { - timeout := time.NewTimer(time.Millisecond * 250) - - select { - case event, _ := <-evChan2: - assert.NotNilf(t, event, "The message was nil") - assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") - - var result testPayload - err = event.Unmarshal(&result) - assert.Nil(t, err, "Error decoding result") - assert.Equal(t, result, *payload, "Payload didn't match") - - wg.Done() - case <-timeout.C: - t.Fatalf("Event was not recieved") - } - }() - - // wait for the subscriber to recieve the message or timeout - wg.Wait() - }) -} diff --git a/events/memory/options.go b/events/memory/options.go deleted file mode 100644 index 1078e00d..00000000 --- a/events/memory/options.go +++ /dev/null @@ -1,18 +0,0 @@ -package memory - -import "github.com/unistack-org/micro/v3/store" - -// Options which are used to configure the in-memory stream -type Options struct { - Store store.Store -} - -// Option is a function which configures options -type Option func(o *Options) - -// Store sets the store to use -func Store(s store.Store) Option { - return func(o *Options) { - o.Store = s - } -} diff --git a/events/nats/nats.go b/events/nats/nats.go deleted file mode 100644 index 2355cca5..00000000 --- a/events/nats/nats.go +++ /dev/null @@ -1,162 +0,0 @@ -package nats - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/google/uuid" - "github.com/nats-io/nats.go" - stan "github.com/nats-io/stan.go" - "github.com/pkg/errors" - - "github.com/unistack-org/micro/v3/events" - "github.com/unistack-org/micro/v3/logger" -) - -const ( - defaultClusterID = "micro" -) - -// NewStream returns an initialized nats stream or an error if the connection to the nats -// server could not be established -func NewStream(opts ...Option) (events.Stream, error) { - // parse the options - options := Options{ - ClientID: uuid.New().String(), - ClusterID: defaultClusterID, - } - for _, o := range opts { - o(&options) - } - - // connect to nats - nopts := nats.GetDefaultOptions() - if options.TLSConfig != nil { - nopts.Secure = true - nopts.TLSConfig = options.TLSConfig - } - if len(options.Address) > 0 { - nopts.Servers = []string{options.Address} - } - conn, err := nopts.Connect() - if err != nil { - return nil, fmt.Errorf("Error connecting to nats at %v with tls enabled (%v): %v", options.Address, nopts.TLSConfig != nil, err) - } - - // connect to the cluster - clusterConn, err := stan.Connect(options.ClusterID, options.ClientID, stan.NatsConn(conn)) - if err != nil { - return nil, fmt.Errorf("Error connecting to nats cluster %v: %v", options.ClusterID, err) - } - - return &stream{clusterConn}, nil -} - -type stream struct { - conn stan.Conn -} - -// Publish a message to a topic -func (s *stream) Publish(topic string, msg interface{}, opts ...events.PublishOption) error { - // validate the topic - if len(topic) == 0 { - return events.ErrMissingTopic - } - - // parse the options - options := events.PublishOptions{ - Timestamp: time.Now(), - } - for _, o := range opts { - o(&options) - } - - // encode the message if it's not already encoded - var payload []byte - if p, ok := msg.([]byte); ok { - payload = p - } else { - p, err := json.Marshal(msg) - if err != nil { - return events.ErrEncodingMessage - } - payload = p - } - - // construct the event - event := &events.Event{ - ID: uuid.New().String(), - Topic: topic, - Timestamp: options.Timestamp, - Metadata: options.Metadata, - Payload: payload, - } - - // serialize the event to bytes - bytes, err := json.Marshal(event) - if err != nil { - return errors.Wrap(err, "Error encoding event") - } - - // publish the event to the topic's channel - if _, err := s.conn.PublishAsync(event.Topic, bytes, nil); err != nil { - return errors.Wrap(err, "Error publishing message to topic") - } - - return nil -} - -// Subscribe to a topic -func (s *stream) Subscribe(topic string, opts ...events.SubscribeOption) (<-chan events.Event, error) { - // validate the topic - if len(topic) == 0 { - return nil, events.ErrMissingTopic - } - - // parse the options - options := events.SubscribeOptions{ - Queue: uuid.New().String(), - } - for _, o := range opts { - o(&options) - } - - // setup the subscriber - c := make(chan events.Event) - handleMsg := func(m *stan.Msg) { - // decode the message - var evt events.Event - if err := json.Unmarshal(m.Data, &evt); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("Error decoding message: %v", err) - } - // not ackknowledging the message is the way to indicate an error occured - return - } - - // push onto the channel and wait for the consumer to take the event off before we acknowledge it. - c <- evt - - if err := m.Ack(); err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("Error acknowledging message: %v", err) - } - } - - // setup the options - subOpts := []stan.SubscriptionOption{ - stan.DurableName(topic), - stan.SetManualAckMode(), - } - if options.StartAtTime.Unix() > 0 { - stan.StartAtTime(options.StartAtTime) - } - - // connect the subscriber - _, err := s.conn.QueueSubscribe(topic, options.Queue, handleMsg, subOpts...) - if err != nil { - return nil, errors.Wrap(err, "Error subscribing to topic") - } - - return c, nil -} diff --git a/events/nats/nats_test.go b/events/nats/nats_test.go deleted file mode 100644 index ac5b0bdc..00000000 --- a/events/nats/nats_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package nats - -import ( - "net" - "os/exec" - "sync" - "testing" - "time" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/unistack-org/micro/v3/events" -) - -type testPayload struct { - Message string -} - -func TestStream(t *testing.T) { - t.Skip() - _, err := exec.LookPath("nats-streaming-server") - if err != nil { - t.Skipf("Skipping nats test, nats-streaming-server binary is not detected") - } - - conn, err := net.DialTimeout("tcp", ":4222", time.Millisecond*100) - if err != nil { - t.Skipf("Skipping nats test, could not connect to cluster on port 4222: %v", err) - } - if err := conn.Close(); err != nil { - t.Fatalf("Error closing test tcp connection to nats cluster") - } - - stream, err := NewStream(ClusterID("test-cluster")) - assert.Nilf(t, err, "NewStream should not return an error") - assert.NotNilf(t, stream, "NewStream should return a stream object") - - // TestMissingTopic will test the topic validation on publish - t.Run("TestMissingTopic", func(t *testing.T) { - err := stream.Publish("", nil) - assert.Equalf(t, err, events.ErrMissingTopic, "Publishing to a blank topic should return an error") - }) - - // TestSubscribeTopic will publish a message to the test topic. The subscriber will subscribe to the - // same test topic. - t.Run("TestSubscribeTopic", func(t *testing.T) { - payload := &testPayload{Message: "HelloWorld"} - metadata := map[string]string{"foo": "bar"} - - // create the subscriber - evChan, err := stream.Subscribe("test") - assert.Nilf(t, err, "Subscribe should not return an error") - - // setup the subscriber async - var wg sync.WaitGroup - - go func() { - timeout := time.NewTimer(time.Millisecond * 250) - - select { - case event, _ := <-evChan: - assert.NotNilf(t, event, "The message was nil") - assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") - - var result testPayload - err = event.Unmarshal(&result) - assert.Nil(t, err, "Error decoding result") - assert.Equal(t, result, *payload, "Payload didn't match") - - wg.Done() - case <-timeout.C: - t.Fatalf("Event was not recieved") - } - }() - - err = stream.Publish("test", payload, events.WithMetadata(metadata)) - assert.Nil(t, err, "Publishing a valid message should not return an error") - wg.Add(1) - - // wait for the subscriber to recieve the message or timeout - wg.Wait() - }) - - // TestSubscribeQueue will publish a message to a random topic. Two subscribers will then consume - // the message from the firehose topic with different queues. The second subscriber will be registered - // after the message is published to test durability. - t.Run("TestSubscribeQueue", func(t *testing.T) { - topic := uuid.New().String() - payload := &testPayload{Message: "HelloWorld"} - metadata := map[string]string{"foo": "bar"} - - // create the first subscriber - evChan1, err := stream.Subscribe(topic) - assert.Nilf(t, err, "Subscribe should not return an error") - - // setup the subscriber async - var wg sync.WaitGroup - - go func() { - timeout := time.NewTimer(time.Millisecond * 250) - - select { - case event, _ := <-evChan1: - assert.NotNilf(t, event, "The message was nil") - assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") - - var result testPayload - err = event.Unmarshal(&result) - assert.Nil(t, err, "Error decoding result") - assert.Equal(t, result, *payload, "Payload didn't match") - - wg.Done() - case <-timeout.C: - t.Fatalf("Event was not recieved") - } - }() - - err = stream.Publish(topic, payload, events.WithMetadata(metadata)) - assert.Nil(t, err, "Publishing a valid message should not return an error") - wg.Add(2) - - // create the second subscriber - evChan2, err := stream.Subscribe(topic, - events.WithQueue("second_queue"), - events.WithStartAtTime(time.Now().Add(time.Minute*-1)), - ) - assert.Nilf(t, err, "Subscribe should not return an error") - - go func() { - timeout := time.NewTimer(time.Millisecond * 250) - - select { - case event, _ := <-evChan2: - assert.NotNilf(t, event, "The message was nil") - assert.Equal(t, event.Metadata, metadata, "Metadata didn't match") - - var result testPayload - err = event.Unmarshal(&result) - assert.Nil(t, err, "Error decoding result") - assert.Equal(t, result, *payload, "Payload didn't match") - - wg.Done() - case <-timeout.C: - t.Fatalf("Event was not recieved") - } - }() - - // wait for the subscriber to recieve the message or timeout - wg.Wait() - }) -} diff --git a/events/nats/options.go b/events/nats/options.go deleted file mode 100644 index a4781734..00000000 --- a/events/nats/options.go +++ /dev/null @@ -1,42 +0,0 @@ -package nats - -import "crypto/tls" - -// Options which are used to configure the nats stream -type Options struct { - ClusterID string - ClientID string - Address string - TLSConfig *tls.Config -} - -// Option is a function which configures options -type Option func(o *Options) - -// ClusterID sets the cluster id for the nats connection -func ClusterID(id string) Option { - return func(o *Options) { - o.ClusterID = id - } -} - -// ClientID sets the client id for the nats connection -func ClientID(id string) Option { - return func(o *Options) { - o.ClientID = id - } -} - -// Address of the nats cluster -func Address(addr string) Option { - return func(o *Options) { - o.Address = addr - } -} - -// TLSConfig to use when connecting to the cluster -func TLSConfig(t *tls.Config) Option { - return func(o *Options) { - o.TLSConfig = t - } -} diff --git a/go.mod b/go.mod index 987fe5b3..5b3c0c97 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,12 @@ go 1.15 require ( github.com/BurntSushi/toml v0.3.1 - github.com/bitly/go-simplejson v0.5.0 - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b github.com/caddyserver/certmagic v0.10.6 - github.com/coreos/bbolt v0.0.0-00010101000000-000000000000 // indirect - github.com/coreos/etcd v3.3.18+incompatible - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect - github.com/davecgh/go-spew v1.1.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1 github.com/evanphx/json-patch/v5 v5.0.0 - github.com/fsnotify/fsnotify v1.4.9 - github.com/fsouza/go-dockerclient v1.6.0 + github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/ghodss/yaml v1.0.0 github.com/go-acme/lego/v3 v3.4.0 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee @@ -26,45 +18,21 @@ require ( github.com/golang/protobuf v1.4.2 github.com/google/uuid v1.1.1 github.com/gorilla/handlers v1.4.2 - github.com/gorilla/websocket v1.4.2 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.2.1 // indirect - github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/hashicorp/hcl v1.0.0 github.com/hpcloud/tail v1.0.0 - github.com/imdario/mergo v0.3.9 - github.com/jonboulle/clockwork v0.2.0 // indirect - github.com/kr/pretty v0.2.0 - github.com/lib/pq v1.7.0 - github.com/lucas-clemente/quic-go v0.18.0 + github.com/kr/pretty v0.2.0 // indirect github.com/miekg/dns v1.1.27 - github.com/mitchellh/hashstructure v1.0.0 - github.com/nats-io/nats-streaming-server v0.18.0 // indirect - github.com/nats-io/nats.go v1.10.0 - github.com/nats-io/stan.go v0.7.0 github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pkg/errors v0.9.1 + github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.7.0 - github.com/soheilhy/cmux v0.1.4 // indirect github.com/stretchr/testify v1.5.1 github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf - github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 // indirect github.com/xanzy/go-gitlab v0.35.1 - github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect - go.etcd.io/bbolt v1.3.5 - go.uber.org/zap v1.13.0 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 golang.org/x/net v0.0.0-20200707034311-ab3426394381 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 - google.golang.org/grpc v1.29.1 google.golang.org/protobuf v1.25.0 gopkg.in/square/go-jose.v2 v2.4.1 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect -) - -replace ( - github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.3 - github.com/coreos/etcd => github.com/ozonru/etcd v3.3.20-grpc1.27-origmodule+incompatible - github.com/imdario/mergo => github.com/imdario/mergo v0.3.8 - google.golang.org/grpc => google.golang.org/grpc v1.27.0 + gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index 3ebc7ee5..1f589874 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -12,15 +11,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= @@ -36,11 +28,6 @@ github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvd github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c h1:YMP6olTU903X3gxQJckdmiP8/zkSMq4kN3uipsU9XjU= -github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -51,26 +38,15 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caddyserver/certmagic v0.10.6 h1:sCya6FmfaN74oZE46kqfaFOVoROD/mF36rTQfjN7TZc= github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU= @@ -79,29 +55,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -112,15 +67,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23 h1:oqgGT9O61YAYvI41EBsLePOr+LE6roB0xY4gpkZuFSE= -github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -132,30 +78,22 @@ github.com/evanphx/json-patch/v5 v5.0.0 h1:dKTrUeykyQwKb/kx7Z+4ukDs6l+4L41HqG1XH github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsouza/go-dockerclient v1.6.0 h1:f7j+AX94143JL1H3TiqSMkM4EcLDI0De1qD4GGn3Hig= -github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A= github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -163,31 +101,19 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g= github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -208,7 +134,6 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -218,8 +143,6 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= @@ -229,54 +152,26 @@ github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YAR github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.1 h1:V59tBiPuMkySHwJkuq/OYkK0WnOLwCwD3UkTbEMr12U= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM= -github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/raft v1.1.2 h1:oxEL5DDeurYxLd3UbcY/hccgSPhLLpiBZ1YxtWEq59c= -github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.2.0 h1:J2SLSdy7HgElq8ekSl2Mxh6vrRNFxqbXGenYH2I02Vs= -github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -285,8 +180,6 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -299,38 +192,23 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= -github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= -github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= -github.com/lucas-clemente/quic-go v0.18.0 h1:JhQDdqxdwdmGdKsKgXi1+coHRoGhvU6z0rNzOJqZ/4o= -github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg= -github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= -github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= -github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -338,65 +216,22 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= -github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.7 h1:jCoQwDvRYJy3OpOTHeYfvIPLP46BMeDmH7XEJg/r42I= -github.com/nats-io/nats-server/v2 v2.1.7/go.mod h1:rbRrRE/Iv93O/rUvZ9dh4NfT0Cm9HWjW/BqOWLGgYiE= -github.com/nats-io/nats-streaming-server v0.18.0 h1:+RDozeN9scwCm0Wc2fYlvGcP144hvxvSOtxZ8FE21ME= -github.com/nats-io/nats-streaming-server v0.18.0/go.mod h1:Y9Aiif2oANuoKazQrs4wXtF3jqt6p97ODQg68lR5TnY= -github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY= -github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= -github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nats-io/stan.go v0.7.0 h1:sMVHD9RkxPOl6PJfDVBQd+gbxWkApeYl6GrH+10msO4= -github.com/nats-io/stan.go v0.7.0/go.mod h1:Ci6mUIpGQTjl++MqK2XzkWI/0vF+Bl72uScx7ejSYmU= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= -github.com/ozonru/etcd v3.3.20-grpc1.27-origmodule+incompatible h1:CAG0PUvo1fen+ZEfxKJjFIc8GuuN5RuaBuCAuaP2Hno= -github.com/ozonru/etcd v3.3.20-grpc1.27-origmodule+incompatible/go.mod h1:iIubILNIN6Jq9h8uiSLrN9L1tuj3iSSFwz3R61skm/A= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -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/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -406,9 +241,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= @@ -420,66 +253,31 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -487,69 +285,33 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 h1:j6JEOq5QWFker+d7mFQYOhjTZonQ7YkLTHm56dbn+yM= -github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= github.com/xanzy/go-gitlab v0.35.1 h1:jJSgT0NxjCvrSZf7Gvn2NxxV9xAYkTjYrKW8XwWhrfY= github.com/xanzy/go-gitlab v0.35.1/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -558,15 +320,14 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= @@ -574,23 +335,17 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b h1:GgiSbuUyC0BlbUmHQBgFqu32eiRR/CEYdjOjOd4zE6Y= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -601,18 +356,13 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -621,56 +371,42 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -685,20 +421,13 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa h1:5E4dL8+NgFOgjwbTKz+OOEGGhP+ectTmF842l6KjupQ= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -706,17 +435,12 @@ google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -725,9 +449,14 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -749,7 +478,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= @@ -765,23 +493,13 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/model/mud/entity.go b/model/mud/entity.go deleted file mode 100644 index 111fd7ca..00000000 --- a/model/mud/entity.go +++ /dev/null @@ -1,52 +0,0 @@ -package mud - -import ( - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/model" -) - -type mudEntity struct { - id string - name string - value interface{} - codec codec.Marshaler - attributes map[string]interface{} -} - -func (m *mudEntity) Attributes() map[string]interface{} { - return m.attributes -} - -func (m *mudEntity) Id() string { - return m.id -} - -func (m *mudEntity) Name() string { - return m.name -} - -func (m *mudEntity) Value() interface{} { - return m.value -} - -func (m *mudEntity) Read(v interface{}) error { - switch m.value.(type) { - case []byte: - b := m.value.([]byte) - return m.codec.Unmarshal(b, v) - default: - v = m.value - } - return nil -} - -func newEntity(name string, value interface{}, codec codec.Marshaler) model.Entity { - return &mudEntity{ - id: uuid.New().String(), - name: name, - value: value, - codec: codec, - attributes: make(map[string]interface{}), - } -} diff --git a/model/mud/mud.go b/model/mud/mud.go deleted file mode 100644 index efb064a1..00000000 --- a/model/mud/mud.go +++ /dev/null @@ -1,104 +0,0 @@ -// Package mud is the micro data model implementation -package mud - -import ( - "github.com/unistack-org/micro/v3/codec/json" - "github.com/unistack-org/micro/v3/model" - "github.com/unistack-org/micro/v3/store" - "github.com/unistack-org/micro/v3/store/memory" - memsync "github.com/unistack-org/micro/v3/sync/memory" -) - -type mudModel struct { - options model.Options -} - -func (m *mudModel) Init(opts ...model.Option) error { - for _, o := range opts { - o(&m.options) - } - return nil -} - -func (m *mudModel) NewEntity(name string, value interface{}) model.Entity { - // TODO: potentially pluralise name for tables - return newEntity(name, value, m.options.Codec) -} - -func (m *mudModel) Create(e model.Entity) error { - // lock on the name of entity - if err := m.options.Sync.Lock(e.Name()); err != nil { - return err - } - // TODO: deal with the error - defer m.options.Sync.Unlock(e.Name()) - - // TODO: potentially add encode to entity? - v, err := m.options.Codec.Marshal(e.Value()) - if err != nil { - return err - } - - // TODO: include metadata and set database - return m.options.Store.Write(&store.Record{ - Key: e.Id(), - Value: v, - }, store.WriteTo(m.options.Database, e.Name())) -} - -func (m *mudModel) Read(opts ...model.ReadOption) ([]model.Entity, error) { - var options model.ReadOptions - for _, o := range opts { - o(&options) - } - // TODO: implement the options that allow querying - return nil, nil -} - -func (m *mudModel) Update(e model.Entity) error { - // TODO: read out the record first, update the fields and store - - // lock on the name of entity - if err := m.options.Sync.Lock(e.Name()); err != nil { - return err - } - // TODO: deal with the error - defer m.options.Sync.Unlock(e.Name()) - - // TODO: potentially add encode to entity? - v, err := m.options.Codec.Marshal(e.Value()) - if err != nil { - return err - } - - // TODO: include metadata and set database - return m.options.Store.Write(&store.Record{ - Key: e.Id(), - Value: v, - }, store.WriteTo(m.options.Database, e.Name())) -} - -func (m *mudModel) Delete(opts ...model.DeleteOption) error { - var options model.DeleteOptions - for _, o := range opts { - o(&options) - } - // TODO: implement the options that allow deleting - return nil -} - -func (m *mudModel) String() string { - return "mud" -} - -func NewModel(opts ...model.Option) model.Model { - options := model.Options{ - Codec: new(json.Marshaler), - Sync: memsync.NewSync(), - Store: memory.NewStore(), - } - - return &mudModel{ - options: options, - } -} diff --git a/network/mucp/mucp.go b/network/mucp/mucp.go deleted file mode 100644 index 11782203..00000000 --- a/network/mucp/mucp.go +++ /dev/null @@ -1,1854 +0,0 @@ -package mucp - -import ( - "errors" - "fmt" - "hash/fnv" - "io" - "math" - "math/rand" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "github.com/unistack-org/micro/v3/client" - cmucp "github.com/unistack-org/micro/v3/client/mucp" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/network" - pb "github.com/unistack-org/micro/v3/network/mucp/proto" - "github.com/unistack-org/micro/v3/proxy" - "github.com/unistack-org/micro/v3/registry/noop" - "github.com/unistack-org/micro/v3/resolver/dns" - "github.com/unistack-org/micro/v3/router" - "github.com/unistack-org/micro/v3/server" - smucp "github.com/unistack-org/micro/v3/server/mucp" - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/tunnel" - bun "github.com/unistack-org/micro/v3/tunnel/broker" - tun "github.com/unistack-org/micro/v3/tunnel/transport" - "github.com/unistack-org/micro/v3/util/backoff" -) - -var ( - // DefaultName is default network name - DefaultName = "go.micro" - // DefaultAddress is default network address - DefaultAddress = ":0" - // AnnounceTime defines time interval to periodically announce node neighbours - AnnounceTime = 1 * time.Second - // KeepAliveTime is the time in which we want to have sent a message to a peer - KeepAliveTime = 30 * time.Second - // SyncTime is the time a network node requests full sync from the network - SyncTime = 1 * time.Minute - // PruneTime defines time interval to periodically check nodes that need to be pruned - // due to their not announcing their presence within this time interval - PruneTime = 90 * time.Second - // MaxDepth defines max depth of peer topology - MaxDepth uint = 3 - // NetworkChannel is the name of the tunnel channel for passing network messages - NetworkChannel = "network" - // ControlChannel is the name of the tunnel channel for passing control message - ControlChannel = "control" - // DefaultLink is default network link - DefaultLink = "network" - // MaxConnections is the max number of network client connections - MaxConnections = 3 - // MaxPeerErrors is the max number of peer errors before we remove it from network graph - MaxPeerErrors = 3 - // ErrPeerExists is returned when adding a peer which already exists - ErrPeerExists = errors.New("peer already exists") - // ErrPeerNotFound is returned when a peer could not be found in node topology - ErrPeerNotFound = errors.New("peer not found") - // ErrClientNotFound is returned when client for tunnel channel could not be found - ErrClientNotFound = errors.New("client not found") - // ErrPeerLinkNotFound is returned when peer link could not be found in tunnel Links - ErrPeerLinkNotFound = errors.New("peer link not found") - // ErrPeerMaxExceeded is returned when peer has reached its max error count limit - ErrPeerMaxExceeded = errors.New("peer max errors exceeded") -) - -// network implements Network interface -type mucpNetwork struct { - // node is network node - *node - // options configure the network - options network.Options - // rtr is network router - router router.Router - // proxy is network proxy - proxy proxy.Proxy - // tunnel is network tunnel - tunnel tunnel.Tunnel - // server is network server - server server.Server - // client is network client - client client.Client - - // tunClient is a map of tunnel channel clients - tunClient map[string]tunnel.Session - // peerLinks is a map of links for each peer - peerLinks map[string]tunnel.Link - - sync.RWMutex - // connected marks the network as connected - connected bool - // closed closes the network - closed chan bool - // whether we've discovered by the network - discovered chan bool -} - -// message is network message -type message struct { - // msg is transport message - msg *transport.Message - // session is tunnel session - session tunnel.Session -} - -// NewNetwork returns a new network node -func NewNetwork(opts ...network.Option) network.Network { - // create default options - options := network.DefaultOptions() - // initialize network options - for _, o := range opts { - o(&options) - } - - // set the address to a hashed address - hasher := fnv.New64() - hasher.Write([]byte(options.Address + options.Id)) - address := fmt.Sprintf("%d", hasher.Sum64()) - - // set the address to advertise - var advertise string - var peerAddress string - - if len(options.Advertise) > 0 { - advertise = options.Advertise - peerAddress = options.Advertise - } else { - advertise = options.Address - peerAddress = address - } - - // init tunnel address to the network bind address - options.Tunnel.Init( - tunnel.Address(options.Address), - ) - - // init router Id to the network id - options.Router.Init( - router.Id(options.Id), - router.Address(peerAddress), - ) - - // create tunnel client with tunnel transport - tunTransport := tun.NewTransport( - tun.WithTunnel(options.Tunnel), - ) - - // create the tunnel broker - tunBroker := bun.NewBroker( - bun.WithTunnel(options.Tunnel), - ) - - // server is network server - // TODO: use the real registry - server := smucp.NewServer( - server.Id(options.Id), - server.Address(peerAddress), - server.Advertise(advertise), - server.Name(options.Name), - server.Transport(tunTransport), - server.Broker(tunBroker), - server.Registry(noop.NewRegistry()), - ) - - // client is network client - client := cmucp.NewClient( - client.Broker(tunBroker), - client.Transport(tunTransport), - client.Router(options.Router), - ) - - network := &mucpNetwork{ - node: &node{ - id: options.Id, - address: peerAddress, - peers: make(map[string]*node), - status: newStatus(), - }, - options: options, - router: options.Router, - proxy: options.Proxy, - tunnel: options.Tunnel, - server: server, - client: client, - tunClient: make(map[string]tunnel.Session), - peerLinks: make(map[string]tunnel.Link), - discovered: make(chan bool, 1), - } - - network.node.network = network - - return network -} - -func (n *mucpNetwork) Init(opts ...network.Option) error { - n.Lock() - defer n.Unlock() - - // TODO: maybe only allow reinit of certain opts - for _, o := range opts { - o(&n.options) - } - - return nil -} - -// Options returns network options -func (n *mucpNetwork) Options() network.Options { - n.RLock() - defer n.RUnlock() - - options := n.options - - return options -} - -// Name returns network name -func (n *mucpNetwork) Name() string { - n.RLock() - defer n.RUnlock() - - name := n.options.Name - - return name -} - -// acceptNetConn accepts connections from NetworkChannel -func (n *mucpNetwork) acceptNetConn(l tunnel.Listener, recv chan *message) { - var i int - for { - // accept a connection - conn, err := l.Accept() - if err != nil { - sleep := backoff.Do(i) - logger.Debugf("Network tunnel [%s] accept error: %v, backing off for %v", ControlChannel, err, sleep) - time.Sleep(sleep) - i++ - continue - } - - select { - case <-n.closed: - if err := conn.Close(); err != nil { - logger.Debugf("Network tunnel [%s] failed to close connection: %v", NetworkChannel, err) - } - return - default: - // go handle NetworkChannel connection - go n.handleNetConn(conn, recv) - } - } -} - -// acceptCtrlConn accepts connections from ControlChannel -func (n *mucpNetwork) acceptCtrlConn(l tunnel.Listener, recv chan *message) { - var i int - for { - // accept a connection - conn, err := l.Accept() - if err != nil { - sleep := backoff.Do(i) - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network tunnel [%s] accept error: %v, backing off for %v", ControlChannel, err, sleep) - } - time.Sleep(sleep) - i++ - continue - } - - select { - case <-n.closed: - if err := conn.Close(); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network tunnel [%s] failed to close connection: %v", ControlChannel, err) - } - } - return - default: - // go handle ControlChannel connection - go n.handleCtrlConn(conn, recv) - } - } -} - -// maskRoute will mask the route so that we apply the right values -func (n *mucpNetwork) maskRoute(r *pb.Route) { - hasher := fnv.New64() - // the routes service address - address := r.Address - - // only hash the address if we're advertising our own local routes - // avoid hashing * based routes - if r.Router == n.Id() && r.Address != "*" { - // hash the service before advertising it - hasher.Reset() - // routes for multiple instances of a service will be collapsed here. - // TODO: once we store labels in the table this may need to change - // to include the labels in case they differ but highly unlikely - hasher.Write([]byte(r.Service + n.Address())) - address = fmt.Sprintf("%d", hasher.Sum64()) - } - - // calculate route metric to advertise - metric := n.getRouteMetric(r.Router, r.Gateway, r.Link) - - // NOTE: we override Gateway, Link and Address here - r.Address = address - r.Gateway = n.Address() - r.Link = DefaultLink - r.Metric = metric -} - -// advertise advertises routes to the network -func (n *mucpNetwork) advertise(eventChan <-chan *router.Event) { - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) - - for { - select { - // process local events and randomly fire them at other nodes - case event := <-eventChan: - // create a proto advert - var pbEvents []*pb.Event - - // make a copy of the route - route := &pb.Route{ - Service: event.Route.Service, - Address: event.Route.Address, - Gateway: event.Route.Gateway, - Network: event.Route.Network, - Router: event.Route.Router, - Link: event.Route.Link, - Metric: event.Route.Metric, - } - - // override the various values - n.maskRoute(route) - - e := &pb.Event{ - Type: pb.EventType(event.Type), - Timestamp: event.Timestamp.UnixNano(), - Route: route, - } - - pbEvents = append(pbEvents, e) - - msg := &pb.Advert{ - Id: n.Id(), - Type: pb.AdvertType(event.Type), - Timestamp: event.Timestamp.UnixNano(), - Events: pbEvents, - } - - // get a list of node peers - peers := n.Peers() - - // continue if there is no one to send to - if len(peers) == 0 { - continue - } - - // advertise to max 3 peers - max := len(peers) - if max > 3 { - max = 3 - } - - for i := 0; i < max; i++ { - if peer := n.node.GetPeerNode(peers[rnd.Intn(len(peers))].Id()); peer != nil { - if err := n.sendTo("advert", ControlChannel, peer, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to advertise routes to %s: %v", peer.Id(), err) - } - } - } - } - case <-n.closed: - return - } - } -} - -// initNodes initializes tunnel with a list of resolved nodes -func (n *mucpNetwork) initNodes(startup bool) { - nodes, err := n.resolveNodes() - // NOTE: this condition never fires - // as resolveNodes() never returns error - if err != nil && !startup { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to init nodes: %v", err) - } - return - } - - // strip self - var init []string - - // our current address - advertised := n.server.Options().Advertise - - for _, node := range nodes { - // skip self - if node == advertised { - continue - } - // add the node - init = append(init, node) - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - // initialize the tunnel - logger.Tracef("Network initialising nodes %+v\n", init) - } - - n.tunnel.Init( - tunnel.Nodes(nodes...), - ) -} - -// resolveNodes resolves network nodes to addresses -func (n *mucpNetwork) resolveNodes() ([]string, error) { - nodeMap := make(map[string]bool) - - // collect network node addresses - //nolint:prealloc - var nodes []string - - // use the DNS resolver to expand peers - dns := &dns.Resolver{} - - // append seed nodes if we have them - for _, node := range n.options.Nodes { - // resolve anything that looks like a host name - records, err := dns.Resolve(node) - if err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Failed to resolve %v %v", node, err) - } - continue - } - - // add to the node map - for _, record := range records { - if _, ok := nodeMap[record.Address]; !ok { - nodes = append(nodes, record.Address) - } - nodeMap[record.Address] = true - } - } - - return nodes, nil -} - -// handleNetConn handles network announcement messages -func (n *mucpNetwork) handleNetConn(s tunnel.Session, msg chan *message) { - for { - m := new(transport.Message) - if err := s.Recv(m); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err) - } - switch err { - case io.EOF, tunnel.ErrReadTimeout: - s.Close() - return - } - continue - } - - // check if peer is set - peer := m.Header["Micro-Peer"] - - // check who the message is intended for - if len(peer) > 0 && peer != n.options.Id { - continue - } - - select { - case msg <- &message{ - msg: m, - session: s, - }: - case <-n.closed: - return - } - } -} - -// handleCtrlConn handles ControlChannel connections -func (n *mucpNetwork) handleCtrlConn(s tunnel.Session, msg chan *message) { - for { - m := new(transport.Message) - if err := s.Recv(m); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network tunnel [%s] receive error: %v", ControlChannel, err) - } - switch err { - case io.EOF, tunnel.ErrReadTimeout: - s.Close() - return - } - continue - } - - // check if peer is set - peer := m.Header["Micro-Peer"] - - // check who the message is intended for - if len(peer) > 0 && peer != n.options.Id { - continue - } - - select { - case msg <- &message{ - msg: m, - session: s, - }: - case <-n.closed: - return - } - } -} - -// getHopCount queries network graph and returns hop count for given router -// NOTE: this should be called getHopeMetric -// - Routes for local services have hop count 1 -// - Routes with ID of adjacent nodes have hop count 10 -// - Routes by peers of the advertiser have hop count 100 -// - Routes beyond node neighbourhood have hop count 1000 -func (n *mucpNetwork) getHopCount(rtr string) int { - // make sure node.peers are not modified - n.node.RLock() - defer n.node.RUnlock() - - // we are the origin of the route - if rtr == n.options.Id { - return 1 - } - - // the route origin is our peer - if _, ok := n.node.peers[rtr]; ok { - return 10 - } - - // the route origin is the peer of our peer - for _, peer := range n.node.peers { - for id := range peer.peers { - if rtr == id { - return 100 - } - } - } - // otherwise we are three hops away - return 1000 -} - -// getRouteMetric calculates router metric and returns it -// Route metric is calculated based on link status and route hopd count -func (n *mucpNetwork) getRouteMetric(router string, gateway string, link string) int64 { - // set the route metric - n.RLock() - defer n.RUnlock() - - // local links are marked as 1 - if link == "local" && gateway == "" { - return 1 - } - - // local links from other gateways as 2 - if link == "local" && gateway != "" { - return 2 - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Network looking up %s link to gateway: %s", link, gateway) - } - // attempt to find link based on gateway address - lnk, ok := n.peerLinks[gateway] - if !ok { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to find a link to gateway: %s", gateway) - } - // no link found so infinite metric returned - return math.MaxInt64 - } - - // calculating metric - - delay := lnk.Delay() - hops := n.getHopCount(router) - length := lnk.Length() - - // make sure delay is non-zero - if delay == 0 { - delay = 1 - } - - // make sure length is non-zero - if length == 0 { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Link length is 0 %v %v", link, lnk.Length()) - } - length = 10e9 - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Network calculated metric %v delay %v length %v distance %v", (delay*length*int64(hops))/10e6, delay, length, hops) - } - - return (delay * length * int64(hops)) / 10e6 -} - -// processCtrlChan processes messages received on ControlChannel -func (n *mucpNetwork) processCtrlChan(listener tunnel.Listener) { - defer listener.Close() - - // receive control message queue - recv := make(chan *message, 128) - - // accept ControlChannel connections - go n.acceptCtrlConn(listener, recv) - - for { - select { - case m := <-recv: - // switch on type of message and take action - switch m.msg.Header["Micro-Method"] { - case "advert": - pbAdvert := &pb.Advert{} - - if err := proto.Unmarshal(m.msg.Body, pbAdvert); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network fail to unmarshal advert message: %v", err) - } - continue - } - - // don't process your own messages - if pbAdvert.Id == n.Id() { - continue - } - - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network received advert message from: %s", pbAdvert.Id) - } - - // lookup advertising node in our peer topology - advertNode := n.node.GetPeerNode(pbAdvert.Id) - if advertNode == nil { - // if we can't find the node in our topology (MaxDepth) we skipp prcessing adverts - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network skipping advert message from unknown peer: %s", pbAdvert.Id) - } - continue - } - - for _, event := range pbAdvert.Events { - // for backwards compatibility reasons - if event == nil || event.Route == nil { - continue - } - - // we know the advertising node is not the origin of the route - if pbAdvert.Id != event.Route.Router { - // if the origin router is not the advertising node peer - // we can't rule out potential routing loops so we bail here - if peer := advertNode.GetPeerNode(event.Route.Router); peer == nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network skipping advert message from peer: %s", pbAdvert.Id) - } - continue - } - } - - route := router.Route{ - Service: event.Route.Service, - Address: event.Route.Address, - Gateway: event.Route.Gateway, - Network: event.Route.Network, - Router: event.Route.Router, - Link: event.Route.Link, - Metric: event.Route.Metric, - } - - // calculate route metric and add to the advertised metric - // we need to make sure we do not overflow math.MaxInt64 - metric := n.getRouteMetric(event.Route.Router, event.Route.Gateway, event.Route.Link) - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Network metric for router %s and gateway %s: %v", event.Route.Router, event.Route.Gateway, metric) - } - - // check we don't overflow max int 64 - if d := route.Metric + metric; d <= 0 { - // set to max int64 if we overflow - route.Metric = math.MaxInt64 - } else { - // set the combined value of metrics otherwise - route.Metric = d - } - - // update the local table - if err := n.router.Table().Update(route); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to process advert %s: %v", event.Id, err) - } - } - } - } - case <-n.closed: - return - } - } -} - -// processNetChan processes messages received on NetworkChannel -func (n *mucpNetwork) processNetChan(listener tunnel.Listener) { - defer listener.Close() - - // receive network message queue - recv := make(chan *message, 128) - - // accept NetworkChannel connections - go n.acceptNetConn(listener, recv) - - for { - select { - case m := <-recv: - // switch on type of message and take action - switch m.msg.Header["Micro-Method"] { - case "connect": - // mark the time the message has been received - now := time.Now() - - pbConnect := &pb.Connect{} - if err := proto.Unmarshal(m.msg.Body, pbConnect); err != nil { - logger.Debugf("Network tunnel [%s] connect unmarshal error: %v", NetworkChannel, err) - continue - } - - // don't process your own messages - if pbConnect.Node.Id == n.options.Id { - continue - } - - logger.Debugf("Network received connect message from: %s", pbConnect.Node.Id) - - peer := &node{ - id: pbConnect.Node.Id, - address: pbConnect.Node.Address, - link: m.msg.Header["Micro-Link"], - peers: make(map[string]*node), - status: newStatus(), - lastSeen: now, - } - - // update peer links - - // TODO: should we do this only if we manage to add a peer - // What should we do if the peer links failed to be updated? - if err := n.updatePeerLinks(peer); err != nil { - logger.Debugf("Network failed updating peer links: %s", err) - } - - // add peer to the list of node peers - if err := n.AddPeer(peer); err == ErrPeerExists { - logger.Tracef("Network peer exists, refreshing: %s", peer.id) - // update lastSeen time for the peer - if err := n.RefreshPeer(peer.id, peer.link, now); err != nil { - logger.Debugf("Network failed refreshing peer %s: %v", peer.id, err) - } - } - - // we send the sync message because someone has sent connect - // and wants to either connect or reconnect to the network - // The faster it gets the network config (routes and peer graph) - // the faster the network converges to a stable state - - go func() { - // get node peer graph to send back to the connecting node - node := PeersToProto(n.node, MaxDepth) - - msg := &pb.Sync{ - Peer: node, - } - - // get a list of the best routes for each service in our routing table - routes, err := n.getProtoRoutes() - if err != nil { - logger.Debugf("Network node %s failed listing routes: %v", n.id, err) - } - // attached the routes to the message - msg.Routes = routes - - // send sync message to the newly connected peer - if err := n.sendTo("sync", NetworkChannel, peer, msg); err != nil { - logger.Debugf("Network failed to send sync message: %v", err) - } - }() - case "peer": - // mark the time the message has been received - now := time.Now() - pbPeer := &pb.Peer{} - - if err := proto.Unmarshal(m.msg.Body, pbPeer); err != nil { - logger.Debugf("Network tunnel [%s] peer unmarshal error: %v", NetworkChannel, err) - continue - } - - // don't process your own messages - if pbPeer.Node.Id == n.options.Id { - continue - } - - logger.Debugf("Network received peer message from: %s %s", pbPeer.Node.Id, pbPeer.Node.Address) - - peer := &node{ - id: pbPeer.Node.Id, - address: pbPeer.Node.Address, - link: m.msg.Header["Micro-Link"], - peers: make(map[string]*node), - status: newPeerStatus(pbPeer), - lastSeen: now, - } - - // update peer links - - // TODO: should we do this only if we manage to add a peer - // What should we do if the peer links failed to be updated? - if err := n.updatePeerLinks(peer); err != nil { - logger.Debugf("Network failed updating peer links: %s", err) - } - - // if it's a new peer i.e. we do not have it in our graph, we request full sync - if err := n.node.AddPeer(peer); err == nil { - go func() { - // marshal node graph into protobuf - node := PeersToProto(n.node, MaxDepth) - - msg := &pb.Sync{ - Peer: node, - } - - // get a list of the best routes for each service in our routing table - routes, err := n.getProtoRoutes() - if err != nil { - logger.Debugf("Network node %s failed listing routes: %v", n.id, err) - } - // attached the routes to the message - msg.Routes = routes - - // send sync message to the newly connected peer - if err := n.sendTo("sync", NetworkChannel, peer, msg); err != nil { - logger.Debugf("Network failed to send sync message: %v", err) - } - }() - - continue - // if we already have the peer in our graph, skip further steps - } else if err != ErrPeerExists { - logger.Debugf("Network got error adding peer %v", err) - continue - } - - logger.Tracef("Network peer exists, refreshing: %s", pbPeer.Node.Id) - - // update lastSeen time for the peer - if err := n.RefreshPeer(peer.id, peer.link, now); err != nil { - logger.Debugf("Network failed refreshing peer %s: %v", pbPeer.Node.Id, err) - } - - // NOTE: we don't unpack MaxDepth topology - peer = UnpackPeerTopology(pbPeer, now, MaxDepth-1) - // update the link - peer.link = m.msg.Header["Micro-Link"] - - logger.Tracef("Network updating topology of node: %s", n.node.id) - if err := n.node.UpdatePeer(peer); err != nil { - logger.Debugf("Network failed to update peers: %v", err) - } - - // tell the connect loop that we've been discovered - // so it stops sending connect messages out - select { - case n.discovered <- true: - default: - // don't block here - } - case "sync": - // record the timestamp of the message receipt - now := time.Now() - - pbSync := &pb.Sync{} - if err := proto.Unmarshal(m.msg.Body, pbSync); err != nil { - logger.Debugf("Network tunnel [%s] sync unmarshal error: %v", NetworkChannel, err) - continue - } - - // don't process your own messages - if pbSync.Peer.Node.Id == n.options.Id { - continue - } - - logger.Debugf("Network received sync message from: %s", pbSync.Peer.Node.Id) - - peer := &node{ - id: pbSync.Peer.Node.Id, - address: pbSync.Peer.Node.Address, - link: m.msg.Header["Micro-Link"], - peers: make(map[string]*node), - status: newPeerStatus(pbSync.Peer), - lastSeen: now, - } - - // update peer links - - // TODO: should we do this only if we manage to add a peer - // What should we do if the peer links failed to be updated? - if err := n.updatePeerLinks(peer); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed updating peer links: %s", err) - } - } - - // add peer to the list of node peers - if err := n.node.AddPeer(peer); err == ErrPeerExists { - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Network peer exists, refreshing: %s", peer.id) - } - // update lastSeen time for the existing node - if err := n.RefreshPeer(peer.id, peer.link, now); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed refreshing peer %s: %v", peer.id, err) - } - } - } - - // when we receive a sync message we update our routing table - // and send a peer message back to the network to announce our presence - - // add all the routes we have received in the sync message - for _, pbRoute := range pbSync.Routes { - // unmarshal the routes received from remote peer - route := ProtoToRoute(pbRoute) - // continue if we are the originator of the route - if route.Router == n.router.Options().Id { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node %s skipping route addition: route already present", n.id) - } - continue - } - - metric := n.getRouteMetric(route.Router, route.Gateway, route.Link) - // check we don't overflow max int 64 - if d := route.Metric + metric; d <= 0 { - // set to max int64 if we overflow - route.Metric = math.MaxInt64 - } else { - // set the combined value of metrics otherwise - route.Metric = d - } - - q := []router.QueryOption{ - router.QueryService(route.Service), - router.QueryLink(route.Link), - } - - routes, err := n.router.Table().Query(q...) - if err != nil && err != router.ErrRouteNotFound { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node %s failed listing best routes for %s: %v", n.id, route.Service, err) - } - continue - } - - // we found no routes for the given service - // create the new route we have just received - if len(routes) == 0 { - if err := n.router.Table().Create(route); err != nil && err != router.ErrDuplicateRoute { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node %s failed to add route: %v", n.id, err) - } - } - continue - } - - // find the best route for the given service - // from the routes that we would advertise - bestRoute := routes[0] - for _, r := range routes[0:] { - if bestRoute.Metric > r.Metric { - bestRoute = r - } - } - - // Take the best route to given service and: - // only add new routes if the metric is better - // than the metric of our best route - - if bestRoute.Metric <= route.Metric { - continue - } - - // add route to the routing table - if err := n.router.Table().Create(route); err != nil && err != router.ErrDuplicateRoute { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node %s failed to add route: %v", n.id, err) - } - } - } - - // update your sync timestamp - // NOTE: this might go away as we will be doing full table advert to random peer - if err := n.RefreshSync(now); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed refreshing sync time: %v", err) - } - } - - go func() { - // get node peer graph to send back to the syncing node - msg := PeersToProto(n.node, MaxDepth) - - // advertise yourself to the new node - if err := n.sendTo("peer", NetworkChannel, peer, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to advertise peers: %v", err) - } - } - }() - case "close": - pbClose := &pb.Close{} - if err := proto.Unmarshal(m.msg.Body, pbClose); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network tunnel [%s] close unmarshal error: %v", NetworkChannel, err) - } - continue - } - - // don't process your own messages - if pbClose.Node.Id == n.options.Id { - continue - } - - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network received close message from: %s", pbClose.Node.Id) - } - - peer := &node{ - id: pbClose.Node.Id, - address: pbClose.Node.Address, - } - - if err := n.DeletePeerNode(peer.id); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to delete node %s routes: %v", peer.id, err) - } - } - - if err := n.prunePeerRoutes(peer); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed pruning peer %s routes: %v", peer.id, err) - } - } - - // NOTE: we should maybe advertise this to the network so we converge faster on closed nodes - // as opposed to our waiting until the node eventually gets pruned; something to think about - - // delete peer from the peerLinks - n.Lock() - delete(n.peerLinks, pbClose.Node.Address) - n.Unlock() - } - case <-n.closed: - return - } - } -} - -// pruneRoutes prunes routes return by given query -func (n *mucpNetwork) pruneRoutes(q ...router.QueryOption) error { - routes, err := n.router.Table().Query(q...) - if err != nil && err != router.ErrRouteNotFound { - return err - } - - for _, route := range routes { - if err := n.router.Table().Delete(route); err != nil && err != router.ErrRouteNotFound { - return err - } - } - - return nil -} - -// pruneNodeRoutes prunes routes that were either originated by or routable via given node -func (n *mucpNetwork) prunePeerRoutes(peer *node) error { - // lookup all routes originated by router - q := []router.QueryOption{ - router.QueryRouter(peer.id), - router.QueryLink("*"), - } - if err := n.pruneRoutes(q...); err != nil { - return err - } - - // lookup all routes routable via gw - q = []router.QueryOption{ - router.QueryGateway(peer.address), - router.QueryLink("*"), - } - if err := n.pruneRoutes(q...); err != nil { - return err - } - - return nil -} - -// manage the process of announcing to peers and prune any peer nodes that have not been -// seen for a period of time. Also removes all the routes either originated by or routable -// by the stale nodes. it also resolves nodes periodically and adds them to the tunnel -func (n *mucpNetwork) manage() { - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) - announce := time.NewTicker(AnnounceTime) - defer announce.Stop() - prune := time.NewTicker(PruneTime) - defer prune.Stop() - netsync := time.NewTicker(SyncTime) - defer netsync.Stop() - - // list of links we've sent to - links := make(map[string]time.Time) - - for { - select { - case <-n.closed: - return - case <-announce.C: - current := make(map[string]time.Time) - - // build link map of current links - for _, link := range n.tunnel.Links() { - if n.isLoopback(link) { - continue - } - // get an existing timestamp if it exists - current[link.Id()] = links[link.Id()] - } - - // replace link map - // we do this because a growing map is not - // garbage collected - links = current - - n.RLock() - var i int - // create a list of peers to send to - var peers []*node - - // check peers to see if they need to be sent to - for _, peer := range n.peers { - if i >= 3 { - break - } - - // get last sent - lastSent := links[peer.link] - - // check when we last sent to the peer - // and send a peer message if we haven't - if lastSent.IsZero() || time.Since(lastSent) > KeepAliveTime { - link := peer.link - id := peer.id - - // might not exist for some weird reason - if len(link) == 0 { - // set the link via peer links - l, ok := n.peerLinks[peer.address] - if ok { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network link not found for peer %s cannot announce", peer.id) - } - continue - } - link = l.Id() - } - - // add to the list of peers we're going to send to - peers = append(peers, &node{ - id: id, - link: link, - }) - - // increment our count - i++ - } - } - - n.RUnlock() - - // peers to proto - msg := PeersToProto(n.node, MaxDepth) - - // we're only going to send to max 3 peers at any given tick - for _, peer := range peers { - // advertise yourself to the network - if err := n.sendTo("peer", NetworkChannel, peer, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to advertise peer %s: %v", peer.id, err) - } - continue - } - - // update last sent time - links[peer.link] = time.Now() - } - - // now look at links we may not have sent to. this may occur - // where a connect message was lost - for link, lastSent := range links { - if !lastSent.IsZero() || time.Since(lastSent) < KeepAliveTime { - continue - } - - peer := &node{ - // unknown id of the peer - link: link, - } - - // unknown link and peer so lets do the connect flow - if err := n.sendTo("connect", NetworkChannel, peer, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to connect %s: %v", peer.id, err) - } - continue - } - - links[peer.link] = time.Now() - } - case <-prune.C: - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node %s pruning stale peers", n.id) - } - pruned := n.PruneStalePeers(PruneTime) - - for id, peer := range pruned { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network peer exceeded prune time: %s", id) - } - n.Lock() - delete(n.peerLinks, peer.address) - n.Unlock() - - if err := n.prunePeerRoutes(peer); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed pruning peer %s routes: %v", id, err) - } - } - } - - // get a list of all routes - routes, err := n.options.Router.Table().List() - if err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed listing routes when pruning peers: %v", err) - } - continue - } - - // collect all the router IDs in the routing table - routers := make(map[string]bool) - - for _, route := range routes { - // don't process routes originated by ourselves - if route.Router == n.Id() { - continue - } - - // check if its been processed - if _, ok := routers[route.Router]; ok { - continue - } - - // mark as processed - routers[route.Router] = true - - // if the router is in our peer graph do NOT delete routes originated by it - if peer := n.node.GetPeerNode(route.Router); peer != nil { - continue - } - - // otherwise delete all the routes originated by it - if err := n.pruneRoutes(router.QueryRouter(route.Router)); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed deleting routes by %s: %v", route.Router, err) - } - } - } - case <-netsync.C: - // get a list of node peers - peers := n.Peers() - - // skip when there are no peers - if len(peers) == 0 { - continue - } - - // pick a random peer from the list of peers and request full sync - peer := n.node.GetPeerNode(peers[rnd.Intn(len(peers))].Id()) - // skip if we can't find randomly selected peer - if peer == nil { - continue - } - - go func() { - // get node peer graph to send back to the connecting node - node := PeersToProto(n.node, MaxDepth) - - msg := &pb.Sync{ - Peer: node, - } - - // get a list of the best routes for each service in our routing table - routes, err := n.getProtoRoutes() - if err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node %s failed listing routes: %v", n.id, err) - } - } - // attached the routes to the message - msg.Routes = routes - - // send sync message to the newly connected peer - if err := n.sendTo("sync", NetworkChannel, peer, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to send sync message: %v", err) - } - } - }() - } - } -} - -// getAdvertProtoRoutes returns a list of routes to advertise to remote peer -// based on the advertisement strategy encoded in protobuf -// It returns error if the routes failed to be retrieved from the routing table -func (n *mucpNetwork) getProtoRoutes() ([]*pb.Route, error) { - routes, err := n.router.Table().List() - if err != nil && err != router.ErrRouteNotFound { - return nil, err - } - - // encode the routes to protobuf - pbRoutes := make([]*pb.Route, 0, len(routes)) - for _, route := range routes { - // generate new route proto - pbRoute := RouteToProto(route) - // mask the route before outbounding - n.maskRoute(pbRoute) - // add to list of routes - pbRoutes = append(pbRoutes, pbRoute) - } - - return pbRoutes, nil -} - -func (n *mucpNetwork) sendConnect() { - // send connect message to NetworkChannel - // NOTE: in theory we could do this as soon as - // Dial to NetworkChannel succeeds, but instead - // we initialize all other node resources first - msg := &pb.Connect{ - Node: &pb.Node{ - Id: n.node.id, - Address: n.node.address, - }, - } - - if err := n.sendMsg("connect", NetworkChannel, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to send connect message: %s", err) - } - } -} - -// sendTo sends a message to a specific node as a one off. -// we need this because when links die, we have no discovery info, -// and sending to an existing multicast link doesn't immediately work -func (n *mucpNetwork) sendTo(method, channel string, peer *node, msg proto.Message) error { - body, err := proto.Marshal(msg) - if err != nil { - return err - } - - // Create a unicast connection to the peer but don't do the open/accept flow - c, err := n.tunnel.Dial(channel, tunnel.DialWait(false), tunnel.DialLink(peer.link)) - if err != nil { - if peerNode := n.GetPeerNode(peer.id); peerNode != nil { - // update node status when error happens - peerNode.status.err.Update(err) - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network increment peer %v error count to: %d", peerNode, peerNode, peerNode.status.Error().Count()) - } - if count := peerNode.status.Error().Count(); count == MaxPeerErrors { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network peer %v error count exceeded %d. Prunning.", peerNode, MaxPeerErrors) - } - n.PrunePeer(peerNode.id) - } - } - return err - } - defer c.Close() - - id := peer.id - - if len(id) == 0 { - id = peer.link - } - - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network sending %s message from: %s to %s", method, n.options.Id, id) - } - tmsg := &transport.Message{ - Header: map[string]string{ - "Micro-Method": method, - }, - Body: body, - } - - // setting the peer header - if len(peer.id) > 0 { - tmsg.Header["Micro-Peer"] = peer.id - } - - if err := c.Send(tmsg); err != nil { - // TODO: Lookup peer in our graph - if peerNode := n.GetPeerNode(peer.id); peerNode != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network found peer %s: %v", peer.id, peerNode) - } - // update node status when error happens - peerNode.status.err.Update(err) - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network increment node peer %p %v count to: %d", peerNode, peerNode, peerNode.status.Error().Count()) - } - if count := peerNode.status.Error().Count(); count == MaxPeerErrors { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network node peer %v count exceeded %d: %d", peerNode, MaxPeerErrors, peerNode.status.Error().Count()) - } - n.PrunePeer(peerNode.id) - } - } - return err - } - - return nil -} - -// sendMsg sends a message to the tunnel channel -func (n *mucpNetwork) sendMsg(method, channel string, msg proto.Message) error { - body, err := proto.Marshal(msg) - if err != nil { - return err - } - - // check if the channel client is initialized - n.RLock() - client, ok := n.tunClient[channel] - if !ok || client == nil { - n.RUnlock() - return ErrClientNotFound - } - n.RUnlock() - - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network sending %s message from: %s", method, n.options.Id) - } - - return client.Send(&transport.Message{ - Header: map[string]string{ - "Micro-Method": method, - }, - Body: body, - }) -} - -// updatePeerLinks updates link for a given peer -func (n *mucpNetwork) updatePeerLinks(peer *node) error { - n.Lock() - defer n.Unlock() - - linkId := peer.link - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Network looking up link %s in the peer links", linkId) - } - - // lookup the peer link - var peerLink tunnel.Link - - for _, link := range n.tunnel.Links() { - if link.Id() == linkId { - peerLink = link - break - } - } - - if peerLink == nil { - return ErrPeerLinkNotFound - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - // if the peerLink is found in the returned links update peerLinks - logger.Tracef("Network updating peer links for peer %s", peer.address) - } - - // lookup a link and update it if better link is available - if link, ok := n.peerLinks[peer.address]; ok { - // if the existing has better Length then the new, replace it - if link.Length() < peerLink.Length() { - n.peerLinks[peer.address] = peerLink - } - return nil - } - - // add peerLink to the peerLinks map - n.peerLinks[peer.address] = peerLink - - return nil -} - -// isLoopback checks if a link is a loopback to ourselves -func (n *mucpNetwork) isLoopback(link tunnel.Link) bool { - // skip loopback - if link.Loopback() { - return true - } - - // our advertise address - loopback := n.server.Options().Advertise - // actual address - address := n.tunnel.Address() - - // if remote is ourselves - switch link.Remote() { - case loopback, address: - return true - } - - return false -} - -// connect will wait for a link to be established and send the connect -// message. We're trying to ensure convergence pretty quickly. So we want -// to hear back. In the case we become completely disconnected we'll -// connect again once a new link is established -func (n *mucpNetwork) connect() { - // discovered lets us know what we received a peer message back - var discovered bool - var attempts int - - for { - // connected is used to define if the link is connected - var connected bool - - // check the links state - for _, link := range n.tunnel.Links() { - // skip loopback - if n.isLoopback(link) { - continue - } - - if link.State() == "connected" { - connected = true - break - } - } - - // if we're not connected wait - if !connected { - // reset discovered - discovered = false - // sleep for a second - time.Sleep(time.Second) - // now try again - continue - } - - // we're connected but are we discovered? - if !discovered { - // recreate the clients because all the tunnel links are gone - // so we haven't send discovery beneath - // NOTE: when starting the tunnel for the first time we might be recreating potentially - // well functioning tunnel clients as "discovered" will be false until the - // n.discovered channel is read at some point later on. - if err := n.createClients(); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Failed to recreate network/control clients: %v", err) - } - continue - } - - // send the connect message - n.sendConnect() - } - - // check if we've been discovered - select { - case <-n.discovered: - discovered = true - attempts = 0 - case <-n.closed: - return - case <-time.After(time.Second + backoff.Do(attempts)): - // we have to try again - attempts++ - } - } -} - -// Connect connects the network -func (n *mucpNetwork) Connect() error { - n.Lock() - defer n.Unlock() - - // connect network tunnel - if err := n.tunnel.Connect(); err != nil { - return err - } - - // return if already connected - if n.connected { - // initialise the nodes - n.initNodes(false) - // send the connect message - go n.sendConnect() - return nil - } - - // initialise the nodes - n.initNodes(true) - - // set our internal node address - // if advertise address is not set - if len(n.options.Advertise) == 0 { - n.server.Init(server.Advertise(n.tunnel.Address())) - } - - // listen on NetworkChannel - netListener, err := n.tunnel.Listen( - NetworkChannel, - tunnel.ListenMode(tunnel.Multicast), - ) - if err != nil { - return err - } - - // listen on ControlChannel - ctrlListener, err := n.tunnel.Listen( - ControlChannel, - tunnel.ListenMode(tunnel.Multicast), - ) - if err != nil { - return err - } - - // dial into ControlChannel to send route adverts - ctrlClient, err := n.tunnel.Dial( - ControlChannel, - tunnel.DialMode(tunnel.Multicast), - ) - if err != nil { - return err - } - - n.tunClient[ControlChannel] = ctrlClient - - // dial into NetworkChannel to send network messages - netClient, err := n.tunnel.Dial( - NetworkChannel, - tunnel.DialMode(tunnel.Multicast), - ) - if err != nil { - return err - } - - n.tunClient[NetworkChannel] = netClient - - // create closed channel - n.closed = make(chan bool) - - // start advertising routes - watcher, err := n.options.Router.Watch() - if err != nil { - return err - } - - advertChan, err := watcher.Chan() - if err != nil { - return err - } - - // start the server - if err := n.server.Start(); err != nil { - return err - } - - // advertise service routes - go n.advertise(advertChan) - // listen to network messages - go n.processNetChan(netListener) - // accept and process routes - go n.processCtrlChan(ctrlListener) - // manage connection once links are established - go n.connect() - // resolve nodes, broadcast announcements and prune stale nodes - go n.manage() - - // we're now connected - n.connected = true - - return nil -} - -func (n *mucpNetwork) close() error { - // stop the server - if err := n.server.Stop(); err != nil { - return err - } - - // close the router - if err := n.router.Close(); err != nil { - return err - } - - // close the tunnel - if err := n.tunnel.Close(); err != nil { - return err - } - - return nil -} - -// createClients is used to create new clients in the event we lose all the tunnels -func (n *mucpNetwork) createClients() error { - // dial into ControlChannel to send route adverts - ctrlClient, err := n.tunnel.Dial(ControlChannel, tunnel.DialMode(tunnel.Multicast)) - if err != nil { - return err - } - - // dial into NetworkChannel to send network messages - netClient, err := n.tunnel.Dial(NetworkChannel, tunnel.DialMode(tunnel.Multicast)) - if err != nil { - return err - } - - n.Lock() - defer n.Unlock() - - // set the control client - c, ok := n.tunClient[ControlChannel] - if ok { - c.Close() - } - n.tunClient[ControlChannel] = ctrlClient - - // set the network client - c, ok = n.tunClient[NetworkChannel] - if ok { - c.Close() - } - n.tunClient[NetworkChannel] = netClient - - return nil -} - -// Close closes network connection -func (n *mucpNetwork) Close() error { - n.Lock() - - if !n.connected { - n.Unlock() - return nil - } - - select { - case <-n.closed: - n.Unlock() - return nil - default: - close(n.closed) - - // set connected to false - n.connected = false - - // unlock the lock otherwise we'll deadlock sending the close - n.Unlock() - - msg := &pb.Close{ - Node: &pb.Node{ - Id: n.node.id, - Address: n.node.address, - }, - } - - if err := n.sendMsg("close", NetworkChannel, msg); err != nil { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Network failed to send close message: %s", err) - } - } - <-time.After(time.Millisecond * 100) - } - - return n.close() -} - -// Client returns network client -func (n *mucpNetwork) Client() client.Client { - return n.client -} - -// Server returns network server -func (n *mucpNetwork) Server() server.Server { - return n.server -} - -// RouteToProto encodes route into protobuf and returns it -func RouteToProto(route router.Route) *pb.Route { - return &pb.Route{ - Service: route.Service, - Address: route.Address, - Gateway: route.Gateway, - Network: route.Network, - Router: route.Router, - Link: route.Link, - Metric: int64(route.Metric), - } -} - -// ProtoToRoute decodes protobuf route into router route and returns it -func ProtoToRoute(route *pb.Route) router.Route { - return router.Route{ - Service: route.Service, - Address: route.Address, - Gateway: route.Gateway, - Network: route.Network, - Router: route.Router, - Link: route.Link, - Metric: route.Metric, - } -} diff --git a/network/mucp/node.go b/network/mucp/node.go deleted file mode 100644 index 19a22179..00000000 --- a/network/mucp/node.go +++ /dev/null @@ -1,529 +0,0 @@ -package mucp - -import ( - "container/list" - "errors" - "sync" - "time" - - "github.com/unistack-org/micro/v3/network" - pb "github.com/unistack-org/micro/v3/network/mucp/proto" -) - -// nodeError tracks node errors -type nodeError struct { - sync.RWMutex - count int - msg error -} - -// Increment increments node error count -func (e *nodeError) Update(err error) { - e.Lock() - defer e.Unlock() - - e.count++ - e.msg = err -} - -// Count returns node error count -func (e *nodeError) Count() int { - e.RLock() - defer e.RUnlock() - - return e.count -} - -func (e *nodeError) Msg() string { - e.RLock() - defer e.RUnlock() - - if e.msg != nil { - return e.msg.Error() - } - - return "" -} - -// status returns node status -type status struct { - sync.RWMutex - err *nodeError -} - -// newStatus creates -func newStatus() *status { - return &status{ - err: new(nodeError), - } -} - -func newPeerStatus(peer *pb.Peer) *status { - status := &status{ - err: new(nodeError), - } - - // if Node.Status is nil, return empty status - if peer.Node.Status == nil { - return status - } - - // if peer.Node.Status.Error is NOT nil, update status fields - if err := peer.Node.Status.GetError(); err != nil { - status.err.count = int(peer.Node.Status.Error.Count) - status.err.msg = errors.New(peer.Node.Status.Error.Msg) - } - - return status -} - -func (s *status) Error() network.Error { - s.RLock() - defer s.RUnlock() - - return &nodeError{ - count: s.err.count, - msg: s.err.msg, - } -} - -// node is network node -type node struct { - sync.RWMutex - // id is node id - id string - // address is node address - address string - // link on which we communicate with the peer - link string - // peers are nodes with direct link to this node - peers map[string]*node - // network returns the node network - network network.Network - // lastSeen keeps track of node lifetime and updates - lastSeen time.Time - // lastSync keeps track of node last sync request - lastSync time.Time - // err tracks node status - status *status -} - -// Id is node ide -func (n *node) Id() string { - return n.id -} - -// Address returns node address -func (n *node) Address() string { - return n.address -} - -// Network returns node network -func (n *node) Network() network.Network { - return n.network -} - -// Status returns node status -func (n *node) Status() network.Status { - n.RLock() - defer n.RUnlock() - - return &status{ - err: &nodeError{ - count: n.status.err.count, - msg: n.status.err.msg, - }, - } -} - -// walk walks the node graph until some condition is met -func (n *node) walk(until func(peer *node) bool, action func(parent, peer *node)) map[string]*node { - // track the visited nodes - visited := make(map[string]*node) - // queue of the nodes to visit - queue := list.New() - - // push node to the back of queue - queue.PushBack(n) - // mark the node as visited - visited[n.id] = n - - // keep iterating over the queue until its empty - for queue.Len() > 0 { - // pop the node from the front of the queue - qnode := queue.Front() - if until(qnode.Value.(*node)) { - return visited - } - // iterate through all of the node peers - // mark the visited nodes; enqueue the non-visited - for id, peer := range qnode.Value.(*node).peers { - action(qnode.Value.(*node), peer) - if _, ok := visited[id]; !ok { - visited[id] = peer - queue.PushBack(peer) - } - } - // remove the node from the queue - queue.Remove(qnode) - } - - return visited -} - -// AddPeer adds a new peer to node topology -// It returns false if the peer already exists -func (n *node) AddPeer(peer *node) error { - n.Lock() - defer n.Unlock() - - // get node topology: we need to check if the peer - // we are trying to add is already in our graph - top := n.getTopology(MaxDepth) - - untilFoundPeer := func(n *node) bool { - return n.id == peer.id - } - - justWalk := func(paent, node *node) {} - - visited := top.walk(untilFoundPeer, justWalk) - - peerNode, inTop := visited[peer.id] - - if _, ok := n.peers[peer.id]; !ok { - if inTop { - // just create a new edge to the existing peer - // but make sure you update the peer link - peerNode.link = peer.link - n.peers[peer.id] = peerNode - return nil - } - n.peers[peer.id] = peer - return nil - } - - return ErrPeerExists -} - -// DeletePeer deletes a peer from node peers -// It returns true if the peer has been deleted -func (n *node) DeletePeer(id string) bool { - n.Lock() - defer n.Unlock() - - delete(n.peers, id) - - return true -} - -// UpdatePeer updates a peer if it already exists -// It returns error if the peer does not exist -func (n *node) UpdatePeer(peer *node) error { - n.Lock() - defer n.Unlock() - - if _, ok := n.peers[peer.id]; ok { - n.peers[peer.id] = peer - return nil - } - - return ErrPeerNotFound -} - -// RefreshPeer updates node last seen timestamp -// It returns false if the peer has not been found. -func (n *node) RefreshPeer(id, link string, now time.Time) error { - n.Lock() - defer n.Unlock() - - peer, ok := n.peers[id] - if !ok { - return ErrPeerNotFound - } - - // set peer link - peer.link = link - // set last seen - peer.lastSeen = now - - return nil -} - -// RefreshSync refreshes nodes sync time -func (n *node) RefreshSync(now time.Time) error { - n.Lock() - defer n.Unlock() - - n.lastSync = now - - return nil -} - -// Nodes returns a slice of all nodes in the whole node topology -func (n *node) Nodes() []network.Node { - // we need to freeze the network graph here - // otherwise we might get inconsistent results - n.RLock() - defer n.RUnlock() - - // NOTE: this should never be true - untilNoMorePeers := func(node *node) bool { - return node == nil - } - justWalk := func(parent, node *node) {} - - visited := n.walk(untilNoMorePeers, justWalk) - - nodes := make([]network.Node, 0, len(visited)) - // collect all the nodes and return them - for _, node := range visited { - nodes = append(nodes, node) - } - - return nodes -} - -// GetPeerNode returns a node from node MaxDepth topology -// It returns nil if the peer was not found -func (n *node) GetPeerNode(id string) *node { - // get node topology up to MaxDepth - top := n.Topology(MaxDepth) - - untilFoundPeer := func(n *node) bool { - return n.id == id - } - justWalk := func(paent, node *node) {} - - visited := top.walk(untilFoundPeer, justWalk) - - peerNode, ok := visited[id] - if !ok { - return nil - } - - return peerNode -} - -// DeletePeerNode removes peer node from node topology -func (n *node) DeletePeerNode(id string) error { - n.Lock() - defer n.Unlock() - - untilNoMorePeers := func(node *node) bool { - return node == nil - } - - deleted := make(map[string]*node) - deletePeer := func(parent, node *node) { - if node.id != n.id && node.id == id { - delete(parent.peers, node.id) - deleted[node.id] = node - } - } - - n.walk(untilNoMorePeers, deletePeer) - - if _, ok := deleted[id]; !ok { - return ErrPeerNotFound - } - - return nil -} - -// PrunePeer prunes the peers with the given id -func (n *node) PrunePeer(id string) { - n.Lock() - defer n.Unlock() - - untilNoMorePeers := func(node *node) bool { - return node == nil - } - - prunePeer := func(parent, node *node) { - if node.id != n.id && node.id == id { - delete(parent.peers, node.id) - } - } - - n.walk(untilNoMorePeers, prunePeer) -} - -// PruneStalePeerNodes prunes the peers that have not been seen for longer than pruneTime -// It returns a map of the the nodes that got pruned -func (n *node) PruneStalePeers(pruneTime time.Duration) map[string]*node { - n.Lock() - defer n.Unlock() - - untilNoMorePeers := func(node *node) bool { - return node == nil - } - - pruned := make(map[string]*node) - pruneStalePeer := func(parent, node *node) { - if node.id != n.id && time.Since(node.lastSeen) > PruneTime { - delete(parent.peers, node.id) - pruned[node.id] = node - } - } - - n.walk(untilNoMorePeers, pruneStalePeer) - - return pruned -} - -// getTopology traverses node graph and builds node topology -// NOTE: this function is not thread safe -func (n *node) getTopology(depth uint) *node { - // make a copy of yourself - node := &node{ - id: n.id, - address: n.address, - peers: make(map[string]*node), - network: n.network, - status: n.status, - lastSeen: n.lastSeen, - } - - // return if we reach requested depth or we have no more peers - if depth == 0 || len(n.peers) == 0 { - return node - } - - // decrement the depth - depth-- - - // iterate through our peers and update the node peers - for _, peer := range n.peers { - nodePeer := peer.getTopology(depth) - if _, ok := node.peers[nodePeer.id]; !ok { - node.peers[nodePeer.id] = nodePeer - } - } - - return node -} - -// Topology returns a copy of the node topology down to given depth -// NOTE: the returned node is a node graph - not a single node -func (n *node) Topology(depth uint) *node { - n.RLock() - defer n.RUnlock() - - return n.getTopology(depth) -} - -// Peers returns node peers up to MaxDepth -func (n *node) Peers() []network.Node { - n.RLock() - defer n.RUnlock() - - peers := make([]network.Node, 0, len(n.peers)) - for _, nodePeer := range n.peers { - peer := nodePeer.getTopology(MaxDepth) - peers = append(peers, peer) - } - - return peers -} - -// UnpackPeerTopology unpacks pb.Peer into node topology of given depth -func UnpackPeerTopology(pbPeer *pb.Peer, lastSeen time.Time, depth uint) *node { - peerNode := &node{ - id: pbPeer.Node.Id, - address: pbPeer.Node.Address, - peers: make(map[string]*node), - status: newPeerStatus(pbPeer), - lastSeen: lastSeen, - } - - // return if have either reached the depth or have no more peers - if depth == 0 || len(pbPeer.Peers) == 0 { - return peerNode - } - - // decrement the depth - depth-- - - peers := make(map[string]*node) - for _, pbPeer := range pbPeer.Peers { - peer := UnpackPeerTopology(pbPeer, lastSeen, depth) - peers[pbPeer.Node.Id] = peer - } - - peerNode.peers = peers - - return peerNode -} - -func peerProtoTopology(peer network.Node, depth uint) *pb.Peer { - node := &pb.Node{ - Id: peer.Id(), - Address: peer.Address(), - Status: &pb.Status{ - Error: &pb.Error{ - Count: uint32(peer.Status().Error().Count()), - Msg: peer.Status().Error().Msg(), - }, - }, - } - - // set the network name if network is not nil - if peer.Network() != nil { - node.Network = peer.Network().Name() - } - - pbPeers := &pb.Peer{ - Node: node, - Peers: make([]*pb.Peer, 0), - } - - // return if we reached the end of topology or depth - if depth == 0 || len(peer.Peers()) == 0 { - return pbPeers - } - - // decrement the depth - depth-- - - // iterate through peers of peers aka pops - for _, pop := range peer.Peers() { - peer := peerProtoTopology(pop, depth) - pbPeers.Peers = append(pbPeers.Peers, peer) - } - - return pbPeers -} - -// PeersToProto returns node peers graph encoded into protobuf -func PeersToProto(node network.Node, depth uint) *pb.Peer { - // network node aka root node - pbNode := &pb.Node{ - Id: node.Id(), - Address: node.Address(), - Status: &pb.Status{ - Error: &pb.Error{ - Count: uint32(node.Status().Error().Count()), - Msg: node.Status().Error().Msg(), - }, - }, - } - - // set the network name if network is not nil - if node.Network() != nil { - pbNode.Network = node.Network().Name() - } - - // we will build proto topology into this - pbPeers := &pb.Peer{ - Node: pbNode, - Peers: make([]*pb.Peer, 0), - } - - for _, peer := range node.Peers() { - pbPeer := peerProtoTopology(peer, depth) - pbPeers.Peers = append(pbPeers.Peers, pbPeer) - } - - return pbPeers -} diff --git a/network/mucp/node_test.go b/network/mucp/node_test.go deleted file mode 100644 index 21c1a5a3..00000000 --- a/network/mucp/node_test.go +++ /dev/null @@ -1,368 +0,0 @@ -package mucp - -import ( - "testing" - "time" - - "github.com/unistack-org/micro/v3/network" - pb "github.com/unistack-org/micro/v3/network/mucp/proto" -) - -var ( - testNodeId = "testNode" - testNodeAddress = "testAddress" - testNodeNetName = "testNetwork" - testNodePeerIds = []string{"peer1", "peer2", "peer3"} - testPeerOfPeerIds = []string{"peer11", "peer12"} -) - -func testSetup() *node { - testNode := &node{ - id: testNodeId, - address: testNodeAddress, - peers: make(map[string]*node), - network: NewNetwork(network.Name(testNodeNetName)), - status: newStatus(), - } - - // add some peers to the node - for _, id := range testNodePeerIds { - testNode.peers[id] = &node{ - id: id, - address: testNode.address + "-" + id, - peers: make(map[string]*node), - network: testNode.network, - status: newStatus(), - } - } - - // add peers to peer1 - // NOTE: these are peers of peers! - for _, id := range testPeerOfPeerIds { - testNode.peers["peer1"].peers[id] = &node{ - id: id, - address: testNode.address + "-" + id, - peers: make(map[string]*node), - network: testNode.network, - status: newStatus(), - } - } - - // connect peer1 with peer2 - testNode.peers["peer1"].peers["peer2"] = testNode.peers["peer2"] - // connect peer2 with peer3 - testNode.peers["peer2"].peers["peer3"] = testNode.peers["peer3"] - - return testNode -} - -func TestNodeId(t *testing.T) { - node := testSetup() - if node.Id() != testNodeId { - t.Errorf("Expected id: %s, found: %s", testNodeId, node.Id()) - } -} - -func TestNodeAddress(t *testing.T) { - node := testSetup() - if node.Address() != testNodeAddress { - t.Errorf("Expected address: %s, found: %s", testNodeAddress, node.Address()) - } -} -func TestNodeNetwork(t *testing.T) { - node := testSetup() - if node.Network().Name() != testNodeNetName { - t.Errorf("Expected network: %s, found: %s", testNodeNetName, node.Network().Name()) - } -} - -func TestNodes(t *testing.T) { - // single node - single := &node{ - id: testNodeId, - address: testNodeAddress, - peers: make(map[string]*node), - network: NewNetwork(network.Name(testNodeNetName)), - } - // get all the nodes including yourself - nodes := single.Nodes() - nodeCount := 1 - - if len(nodes) != nodeCount { - t.Errorf("Expected to find %d nodes, found: %d", nodeCount, len(nodes)) - } - - // complicated node graph - node := testSetup() - // get all the nodes including yourself - nodes = node.Nodes() - - // compile a list of ids of all nodes in the network into map for easy indexing - nodeIds := make(map[string]bool) - // add yourself - nodeIds[node.id] = true - // add peer Ids - for _, id := range testNodePeerIds { - nodeIds[id] = true - } - // add peer1 peers i.e. peers of peer - for _, id := range testPeerOfPeerIds { - nodeIds[id] = true - } - - // we should return the correct number of nodes - if len(nodes) != len(nodeIds) { - t.Errorf("Expected %d nodes, found: %d", len(nodeIds), len(nodes)) - } - - // iterate through the list of nodes and makes sure all have been returned - for _, node := range nodes { - if _, ok := nodeIds[node.Id()]; !ok { - t.Errorf("Expected to find %s node", node.Id()) - } - } - - // this is a leaf node - id := "peer11" - if nodePeer := node.GetPeerNode(id); nodePeer == nil { - t.Errorf("Expected to find %s node", id) - } -} - -func collectPeerIds(peer network.Node, ids map[string]bool) map[string]bool { - if len(peer.Peers()) == 0 { - return ids - } - - // iterate through the whole graph - for _, peer := range peer.Peers() { - ids = collectPeerIds(peer, ids) - if _, ok := ids[peer.Id()]; !ok { - ids[peer.Id()] = true - } - } - - return ids -} - -func TestPeers(t *testing.T) { - // single node - single := &node{ - id: testNodeId, - address: testNodeAddress, - peers: make(map[string]*node), - network: NewNetwork(network.Name(testNodeNetName)), - } - // get node peers - peers := single.Peers() - // there should be no peers - peerCount := 0 - - if len(peers) != peerCount { - t.Errorf("Expected to find %d nodes, found: %d", peerCount, len(peers)) - } - - // complicated node graph - node := testSetup() - // list of ids of nodes of MaxDepth - peerIds := make(map[string]bool) - // add peer Ids - for _, id := range testNodePeerIds { - peerIds[id] = true - } - // add peers of peers to peerIds - for _, id := range testPeerOfPeerIds { - peerIds[id] = true - } - // get node peers - peers = node.Peers() - - // we will collect all returned Peer Ids into this map - resPeerIds := make(map[string]bool) - for _, peer := range peers { - resPeerIds[peer.Id()] = true - resPeerIds = collectPeerIds(peer, resPeerIds) - } - - // if correct, we must collect all peerIds - if len(resPeerIds) != len(peerIds) { - t.Errorf("Expected to find %d peers, found: %d", len(peerIds), len(resPeerIds)) - } - - for id := range resPeerIds { - if _, ok := peerIds[id]; !ok { - t.Errorf("Expected to find %s peer", id) - } - } -} - -func TestDeletePeerNode(t *testing.T) { - // complicated node graph - node := testSetup() - - nodeCount := len(node.Nodes()) - - // should not find non-existent peer node - if err := node.DeletePeerNode("foobar"); err != ErrPeerNotFound { - t.Errorf("Expected: %v, got: %v", ErrPeerNotFound, err) - } - - // lets pick one of the peer1 peers - if err := node.DeletePeerNode(testPeerOfPeerIds[0]); err != nil { - t.Errorf("Error deleting peer node: %v", err) - } - - nodeDelCount := len(node.Nodes()) - - if nodeDelCount != nodeCount-1 { - t.Errorf("Expected node count: %d, got: %d", nodeCount-1, nodeDelCount) - } -} - -func TestPrunePeer(t *testing.T) { - // complicated node graph - node := testSetup() - - before := node.Nodes() - - node.PrunePeer("peer3") - - now := node.Nodes() - - if len(now) != len(before)-1 { - t.Errorf("Expected pruned node count: %d, got: %d", len(before)-1, len(now)) - } -} - -func TestPruneStalePeers(t *testing.T) { - // complicated node graph - node := testSetup() - nodes := node.Nodes() - // this will delete all nodes besides the root node - pruneTime := 10 * time.Millisecond - time.Sleep(pruneTime) - - // should delete all nodes besides (root) node - pruned := node.PruneStalePeers(pruneTime) - - if len(pruned) != len(nodes)-1 { - t.Errorf("Expected pruned node count: %d, got: %d", len(nodes)-1, len(pruned)) - } - - // complicated node graph - node = testSetup() - nodes = node.Nodes() - - // set prune time to 100ms and wait for half of it - pruneTime = 100 * time.Millisecond - time.Sleep(pruneTime) - - // update the time of peer1 - node.peers["peer1"].lastSeen = time.Now() - - // should prune all but the root nodes and peer1 - pruned = node.PruneStalePeers(pruneTime) - - if len(pruned) != len(nodes)-2 { - t.Errorf("Expected pruned node count: %d, got: %d", len(nodes)-2, len(pruned)) - } -} - -func TestUnpackPeerTopology(t *testing.T) { - pbPeer := &pb.Peer{ - Node: &pb.Node{ - Id: "newPeer", - Address: "newPeerAddress", - Status: &pb.Status{ - Error: &pb.Error{}, - }, - }, - Peers: make([]*pb.Peer, 0), - } - // it should add pbPeer to the single node peers - peer := UnpackPeerTopology(pbPeer, time.Now(), 5) - if peer.id != pbPeer.Node.Id { - t.Errorf("Expected peer id %s, found: %s", pbPeer.Node.Id, peer.id) - } - - node := testSetup() - // build a simple topology to update node peer1 - peer1 := node.peers["peer1"] - pbPeer1Node := &pb.Node{ - Id: peer1.id, - Address: peer1.address, - Status: &pb.Status{ - Error: &pb.Error{}, - }, - } - - pbPeer111 := &pb.Peer{ - Node: &pb.Node{ - Id: "peer111", - Address: "peer111Address", - Status: &pb.Status{ - Error: &pb.Error{}, - }, - }, - Peers: make([]*pb.Peer, 0), - } - - pbPeer121 := &pb.Peer{ - Node: &pb.Node{ - Id: "peer121", - Address: "peer121Address", - Status: &pb.Status{ - Error: &pb.Error{}, - }, - }, - Peers: make([]*pb.Peer, 0), - } - // topology to update - pbPeer1 := &pb.Peer{ - Node: pbPeer1Node, - Peers: []*pb.Peer{pbPeer111, pbPeer121}, - } - // unpack peer1 topology - peer = UnpackPeerTopology(pbPeer1, time.Now(), 5) - // make sure peer1 topology has been correctly updated - newPeerIds := []string{pbPeer111.Node.Id, pbPeer121.Node.Id} - for _, id := range newPeerIds { - if _, ok := peer.peers[id]; !ok { - t.Errorf("Expected %s to be a peer of %s", id, "peer1") - } - } -} - -func TestPeersToProto(t *testing.T) { - // single node - single := &node{ - id: testNodeId, - address: testNodeAddress, - peers: make(map[string]*node), - network: NewNetwork(network.Name(testNodeNetName)), - status: newStatus(), - } - topCount := 0 - - protoPeers := PeersToProto(single, 0) - - if len(protoPeers.Peers) != topCount { - t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers)) - } - - // complicated node graph - node := testSetup() - topCount = 3 - // list of ids of nodes of depth 1 i.e. node peers - peerIds := make(map[string]bool) - // add peer Ids - for _, id := range testNodePeerIds { - peerIds[id] = true - } - // depth 1 should give us immmediate neighbours only - protoPeers = PeersToProto(node, 1) - - if len(protoPeers.Peers) != topCount { - t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers)) - } -} diff --git a/network/mucp/proto/network.pb.go b/network/mucp/proto/network.pb.go deleted file mode 100644 index de78d9d6..00000000 --- a/network/mucp/proto/network.pb.go +++ /dev/null @@ -1,1101 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: network/mucp/proto/network.proto - -package go_micro_network_mucp - -import ( - proto "github.com/golang/protobuf/proto" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -// AdvertType defines the type of advert -type AdvertType int32 - -const ( - AdvertType_AdvertAnnounce AdvertType = 0 - AdvertType_AdvertUpdate AdvertType = 1 -) - -// Enum value maps for AdvertType. -var ( - AdvertType_name = map[int32]string{ - 0: "AdvertAnnounce", - 1: "AdvertUpdate", - } - AdvertType_value = map[string]int32{ - "AdvertAnnounce": 0, - "AdvertUpdate": 1, - } -) - -func (x AdvertType) Enum() *AdvertType { - p := new(AdvertType) - *p = x - return p -} - -func (x AdvertType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (AdvertType) Descriptor() protoreflect.EnumDescriptor { - return file_network_mucp_proto_network_proto_enumTypes[0].Descriptor() -} - -func (AdvertType) Type() protoreflect.EnumType { - return &file_network_mucp_proto_network_proto_enumTypes[0] -} - -func (x AdvertType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use AdvertType.Descriptor instead. -func (AdvertType) EnumDescriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{0} -} - -// EventType defines the type of event -type EventType int32 - -const ( - EventType_Create EventType = 0 - EventType_Delete EventType = 1 - EventType_Update EventType = 2 -) - -// Enum value maps for EventType. -var ( - EventType_name = map[int32]string{ - 0: "Create", - 1: "Delete", - 2: "Update", - } - EventType_value = map[string]int32{ - "Create": 0, - "Delete": 1, - "Update": 2, - } -) - -func (x EventType) Enum() *EventType { - p := new(EventType) - *p = x - return p -} - -func (x EventType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (EventType) Descriptor() protoreflect.EnumDescriptor { - return file_network_mucp_proto_network_proto_enumTypes[1].Descriptor() -} - -func (EventType) Type() protoreflect.EnumType { - return &file_network_mucp_proto_network_proto_enumTypes[1] -} - -func (x EventType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use EventType.Descriptor instead. -func (EventType) EnumDescriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{1} -} - -// Advert is router advertsement streamed by Watch -type Advert struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // id of the advertising router - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // type of advertisement - Type AdvertType `protobuf:"varint,2,opt,name=type,proto3,enum=go.micro.network.mucp.AdvertType" json:"type,omitempty"` - // unix timestamp of the advertisement - Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // TTL of the Advert - Ttl int64 `protobuf:"varint,4,opt,name=ttl,proto3" json:"ttl,omitempty"` - // events is a list of advertised events - Events []*Event `protobuf:"bytes,5,rep,name=events,proto3" json:"events,omitempty"` -} - -func (x *Advert) Reset() { - *x = Advert{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Advert) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Advert) ProtoMessage() {} - -func (x *Advert) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_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 Advert.ProtoReflect.Descriptor instead. -func (*Advert) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{0} -} - -func (x *Advert) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Advert) GetType() AdvertType { - if x != nil { - return x.Type - } - return AdvertType_AdvertAnnounce -} - -func (x *Advert) GetTimestamp() int64 { - if x != nil { - return x.Timestamp - } - return 0 -} - -func (x *Advert) GetTtl() int64 { - if x != nil { - return x.Ttl - } - return 0 -} - -func (x *Advert) GetEvents() []*Event { - if x != nil { - return x.Events - } - return nil -} - -// Event is routing table event -type Event struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // the unique event id - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // type of event - Type EventType `protobuf:"varint,2,opt,name=type,proto3,enum=go.micro.network.mucp.EventType" json:"type,omitempty"` - // unix timestamp of event - Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // service route - Route *Route `protobuf:"bytes,4,opt,name=route,proto3" json:"route,omitempty"` -} - -func (x *Event) Reset() { - *x = Event{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Event) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Event) ProtoMessage() {} - -func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_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 Event.ProtoReflect.Descriptor instead. -func (*Event) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{1} -} - -func (x *Event) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Event) GetType() EventType { - if x != nil { - return x.Type - } - return EventType_Create -} - -func (x *Event) GetTimestamp() int64 { - if x != nil { - return x.Timestamp - } - return 0 -} - -func (x *Event) GetRoute() *Route { - if x != nil { - return x.Route - } - return nil -} - -// Route is a service route -type Route struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // service for the route - Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` - // the address that advertise this route - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - // gateway as the next hop - Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"` - // the network for this destination - Network string `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"` - // router if the router id - Router string `protobuf:"bytes,5,opt,name=router,proto3" json:"router,omitempty"` - // the network link - Link string `protobuf:"bytes,6,opt,name=link,proto3" json:"link,omitempty"` - // the metric / score of this route - Metric int64 `protobuf:"varint,7,opt,name=metric,proto3" json:"metric,omitempty"` - // metadata for the route - Metadata map[string]string `protobuf:"bytes,8,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *Route) Reset() { - *x = Route{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Route) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Route) ProtoMessage() {} - -func (x *Route) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[2] - 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 Route.ProtoReflect.Descriptor instead. -func (*Route) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{2} -} - -func (x *Route) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -func (x *Route) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *Route) GetGateway() string { - if x != nil { - return x.Gateway - } - return "" -} - -func (x *Route) GetNetwork() string { - if x != nil { - return x.Network - } - return "" -} - -func (x *Route) GetRouter() string { - if x != nil { - return x.Router - } - return "" -} - -func (x *Route) GetLink() string { - if x != nil { - return x.Link - } - return "" -} - -func (x *Route) GetMetric() int64 { - if x != nil { - return x.Metric - } - return 0 -} - -func (x *Route) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -// Error tracks network errors -type Error struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Count uint32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` - Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` -} - -func (x *Error) Reset() { - *x = Error{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Error) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Error) ProtoMessage() {} - -func (x *Error) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Error.ProtoReflect.Descriptor instead. -func (*Error) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{3} -} - -func (x *Error) GetCount() uint32 { - if x != nil { - return x.Count - } - return 0 -} - -func (x *Error) GetMsg() string { - if x != nil { - return x.Msg - } - return "" -} - -// Status is node status -type Status struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Error *Error `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` -} - -func (x *Status) Reset() { - *x = Status{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Status) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Status) ProtoMessage() {} - -func (x *Status) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[4] - 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 Status.ProtoReflect.Descriptor instead. -func (*Status) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{4} -} - -func (x *Status) GetError() *Error { - if x != nil { - return x.Error - } - return nil -} - -// Node is network node -type Node struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // node id - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // node address - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - // the network - Network string `protobuf:"bytes,3,opt,name=network,proto3" json:"network,omitempty"` - // associated metadata - Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // node status - Status *Status `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` -} - -func (x *Node) Reset() { - *x = Node{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Node) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Node) ProtoMessage() {} - -func (x *Node) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Node.ProtoReflect.Descriptor instead. -func (*Node) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{5} -} - -func (x *Node) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Node) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *Node) GetNetwork() string { - if x != nil { - return x.Network - } - return "" -} - -func (x *Node) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Node) GetStatus() *Status { - if x != nil { - return x.Status - } - return nil -} - -// Connect is sent when the node connects to the network -type Connect struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // network mode - Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` -} - -func (x *Connect) Reset() { - *x = Connect{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Connect) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Connect) ProtoMessage() {} - -func (x *Connect) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[6] - 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 Connect.ProtoReflect.Descriptor instead. -func (*Connect) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{6} -} - -func (x *Connect) GetNode() *Node { - if x != nil { - return x.Node - } - return nil -} - -// Close is sent when the node disconnects from the network -type Close struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // network node - Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` -} - -func (x *Close) Reset() { - *x = Close{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Close) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Close) ProtoMessage() {} - -func (x *Close) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[7] - 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 Close.ProtoReflect.Descriptor instead. -func (*Close) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{7} -} - -func (x *Close) GetNode() *Node { - if x != nil { - return x.Node - } - return nil -} - -// Peer is used to advertise node peers -type Peer struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // network node - Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` - // node peers - Peers []*Peer `protobuf:"bytes,2,rep,name=peers,proto3" json:"peers,omitempty"` -} - -func (x *Peer) Reset() { - *x = Peer{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Peer) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Peer) ProtoMessage() {} - -func (x *Peer) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[8] - 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 Peer.ProtoReflect.Descriptor instead. -func (*Peer) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{8} -} - -func (x *Peer) GetNode() *Node { - if x != nil { - return x.Node - } - return nil -} - -func (x *Peer) GetPeers() []*Peer { - if x != nil { - return x.Peers - } - return nil -} - -// Sync is network sync message -type Sync struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // peer origin - Peer *Peer `protobuf:"bytes,1,opt,name=peer,proto3" json:"peer,omitempty"` - // node routes - Routes []*Route `protobuf:"bytes,2,rep,name=routes,proto3" json:"routes,omitempty"` -} - -func (x *Sync) Reset() { - *x = Sync{} - if protoimpl.UnsafeEnabled { - mi := &file_network_mucp_proto_network_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Sync) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Sync) ProtoMessage() {} - -func (x *Sync) ProtoReflect() protoreflect.Message { - mi := &file_network_mucp_proto_network_proto_msgTypes[9] - 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 Sync.ProtoReflect.Descriptor instead. -func (*Sync) Descriptor() ([]byte, []int) { - return file_network_mucp_proto_network_proto_rawDescGZIP(), []int{9} -} - -func (x *Sync) GetPeer() *Peer { - if x != nil { - return x.Peer - } - return nil -} - -func (x *Sync) GetRoutes() []*Route { - if x != nil { - return x.Routes - } - return nil -} - -var File_network_mucp_proto_network_proto protoreflect.FileDescriptor - -var file_network_mucp_proto_network_proto_rawDesc = []byte{ - 0x0a, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6d, 0x75, 0x63, 0x70, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x15, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x22, 0xb5, 0x01, 0x0a, 0x06, 0x41, 0x64, - 0x76, 0x65, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x41, 0x64, 0x76, 0x65, 0x72, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x34, 0x0a, 0x06, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, - 0x75, 0x63, 0x70, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x34, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, - 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, - 0x70, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, - 0x32, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x22, 0xb8, 0x02, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, - 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, - 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x46, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, - 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, - 0x75, 0x63, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 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, 0x2f, - 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, - 0x3c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, - 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x85, 0x02, - 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x67, - 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, - 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 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, 0x3a, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x12, 0x2f, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, - 0x65, 0x22, 0x38, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x6e, 0x6f, - 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x6a, 0x0a, 0x04, 0x50, - 0x65, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, - 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x50, 0x65, 0x65, 0x72, - 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x6d, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, - 0x2f, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, - 0x12, 0x34, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x75, 0x63, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2a, 0x32, 0x0a, 0x0a, 0x41, 0x64, 0x76, 0x65, 0x72, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x64, 0x76, 0x65, 0x72, 0x74, 0x41, 0x6e, - 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x64, 0x76, 0x65, - 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x01, 0x2a, 0x2f, 0x0a, 0x09, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x10, 0x01, 0x12, - 0x0a, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x02, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, -} - -var ( - file_network_mucp_proto_network_proto_rawDescOnce sync.Once - file_network_mucp_proto_network_proto_rawDescData = file_network_mucp_proto_network_proto_rawDesc -) - -func file_network_mucp_proto_network_proto_rawDescGZIP() []byte { - file_network_mucp_proto_network_proto_rawDescOnce.Do(func() { - file_network_mucp_proto_network_proto_rawDescData = protoimpl.X.CompressGZIP(file_network_mucp_proto_network_proto_rawDescData) - }) - return file_network_mucp_proto_network_proto_rawDescData -} - -var file_network_mucp_proto_network_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_network_mucp_proto_network_proto_msgTypes = make([]protoimpl.MessageInfo, 12) -var file_network_mucp_proto_network_proto_goTypes = []interface{}{ - (AdvertType)(0), // 0: go.micro.network.mucp.AdvertType - (EventType)(0), // 1: go.micro.network.mucp.EventType - (*Advert)(nil), // 2: go.micro.network.mucp.Advert - (*Event)(nil), // 3: go.micro.network.mucp.Event - (*Route)(nil), // 4: go.micro.network.mucp.Route - (*Error)(nil), // 5: go.micro.network.mucp.Error - (*Status)(nil), // 6: go.micro.network.mucp.Status - (*Node)(nil), // 7: go.micro.network.mucp.Node - (*Connect)(nil), // 8: go.micro.network.mucp.Connect - (*Close)(nil), // 9: go.micro.network.mucp.Close - (*Peer)(nil), // 10: go.micro.network.mucp.Peer - (*Sync)(nil), // 11: go.micro.network.mucp.Sync - nil, // 12: go.micro.network.mucp.Route.MetadataEntry - nil, // 13: go.micro.network.mucp.Node.MetadataEntry -} -var file_network_mucp_proto_network_proto_depIdxs = []int32{ - 0, // 0: go.micro.network.mucp.Advert.type:type_name -> go.micro.network.mucp.AdvertType - 3, // 1: go.micro.network.mucp.Advert.events:type_name -> go.micro.network.mucp.Event - 1, // 2: go.micro.network.mucp.Event.type:type_name -> go.micro.network.mucp.EventType - 4, // 3: go.micro.network.mucp.Event.route:type_name -> go.micro.network.mucp.Route - 12, // 4: go.micro.network.mucp.Route.metadata:type_name -> go.micro.network.mucp.Route.MetadataEntry - 5, // 5: go.micro.network.mucp.Status.error:type_name -> go.micro.network.mucp.Error - 13, // 6: go.micro.network.mucp.Node.metadata:type_name -> go.micro.network.mucp.Node.MetadataEntry - 6, // 7: go.micro.network.mucp.Node.status:type_name -> go.micro.network.mucp.Status - 7, // 8: go.micro.network.mucp.Connect.node:type_name -> go.micro.network.mucp.Node - 7, // 9: go.micro.network.mucp.Close.node:type_name -> go.micro.network.mucp.Node - 7, // 10: go.micro.network.mucp.Peer.node:type_name -> go.micro.network.mucp.Node - 10, // 11: go.micro.network.mucp.Peer.peers:type_name -> go.micro.network.mucp.Peer - 10, // 12: go.micro.network.mucp.Sync.peer:type_name -> go.micro.network.mucp.Peer - 4, // 13: go.micro.network.mucp.Sync.routes:type_name -> go.micro.network.mucp.Route - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name -} - -func init() { file_network_mucp_proto_network_proto_init() } -func file_network_mucp_proto_network_proto_init() { - if File_network_mucp_proto_network_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_network_mucp_proto_network_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Advert); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Event); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Route); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Error); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Status); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Node); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Connect); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Close); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Peer); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_network_mucp_proto_network_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Sync); 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_network_mucp_proto_network_proto_rawDesc, - NumEnums: 2, - NumMessages: 12, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_network_mucp_proto_network_proto_goTypes, - DependencyIndexes: file_network_mucp_proto_network_proto_depIdxs, - EnumInfos: file_network_mucp_proto_network_proto_enumTypes, - MessageInfos: file_network_mucp_proto_network_proto_msgTypes, - }.Build() - File_network_mucp_proto_network_proto = out.File - file_network_mucp_proto_network_proto_rawDesc = nil - file_network_mucp_proto_network_proto_goTypes = nil - file_network_mucp_proto_network_proto_depIdxs = nil -} diff --git a/network/mucp/proto/network.pb.micro.go b/network/mucp/proto/network.pb.micro.go deleted file mode 100644 index e435bd8e..00000000 --- a/network/mucp/proto/network.pb.micro.go +++ /dev/null @@ -1,21 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: network/mucp/proto/network.proto - -package go_micro_network_mucp - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package diff --git a/network/mucp/proto/network.proto b/network/mucp/proto/network.proto deleted file mode 100644 index 627e0e66..00000000 --- a/network/mucp/proto/network.proto +++ /dev/null @@ -1,115 +0,0 @@ -syntax = "proto3"; - -package go.micro.network.mucp; - -// AdvertType defines the type of advert -enum AdvertType { - AdvertAnnounce = 0; - AdvertUpdate = 1; -} - -// Advert is router advertsement streamed by Watch -message Advert { - // id of the advertising router - string id = 1; - // type of advertisement - AdvertType type = 2; - // unix timestamp of the advertisement - int64 timestamp = 3; - // TTL of the Advert - int64 ttl = 4; - // events is a list of advertised events - repeated Event events = 5; -} - -// EventType defines the type of event -enum EventType { - Create = 0; - Delete = 1; - Update = 2; -} - -// Event is routing table event -message Event { - // the unique event id - string id = 1; - // type of event - EventType type = 2; - // unix timestamp of event - int64 timestamp = 3; - // service route - Route route = 4; -} - -// Route is a service route -message Route { - // service for the route - string service = 1; - // the address that advertise this route - string address = 2; - // gateway as the next hop - string gateway = 3; - // the network for this destination - string network = 4; - // router if the router id - string router = 5; - // the network link - string link = 6; - // the metric / score of this route - int64 metric = 7; - // metadata for the route - map metadata = 8; -} - -// Error tracks network errors -message Error { - uint32 count = 1; - string msg = 2; -} - -// Status is node status -message Status { - Error error = 1; -} - -// Node is network node -message Node { - // node id - string id = 1; - // node address - string address = 2; - // the network - string network = 3; - // associated metadata - map metadata = 4; - // node status - Status status = 5; -} - -// Connect is sent when the node connects to the network -message Connect { - // network mode - Node node = 1; -} - -// Close is sent when the node disconnects from the network -message Close { - // network node - Node node = 1; -} - -// Peer is used to advertise node peers -message Peer { - // network node - Node node = 1; - // node peers - repeated Peer peers = 2; -} - -// Sync is network sync message -message Sync { - // peer origin - Peer peer = 1; - // node routes - repeated Route routes = 2; -} diff --git a/network/options.go b/network/options.go index aad1d2f1..000dc36c 100644 --- a/network/options.go +++ b/network/options.go @@ -3,11 +3,8 @@ package network import ( "github.com/google/uuid" "github.com/unistack-org/micro/v3/proxy" - "github.com/unistack-org/micro/v3/proxy/mucp" "github.com/unistack-org/micro/v3/router" - regRouter "github.com/unistack-org/micro/v3/router/registry" "github.com/unistack-org/micro/v3/tunnel" - tmucp "github.com/unistack-org/micro/v3/tunnel/mucp" ) type Option func(*Options) @@ -94,8 +91,5 @@ func DefaultOptions() Options { Id: uuid.New().String(), Name: "go.micro", Address: ":0", - Tunnel: tmucp.NewTunnel(), - Router: regRouter.NewRouter(), - Proxy: mucp.NewProxy(), } } diff --git a/proxy/grpc/grpc.go b/proxy/grpc/grpc.go deleted file mode 100644 index 75e5b57f..00000000 --- a/proxy/grpc/grpc.go +++ /dev/null @@ -1,247 +0,0 @@ -// Package grpc is a grpc proxy built for the go-micro/server -package grpc - -import ( - "context" - "io" - "strings" - - "github.com/unistack-org/micro/v3/client" - grpcc "github.com/unistack-org/micro/v3/client/grpc" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/proxy" - "github.com/unistack-org/micro/v3/server" - "google.golang.org/grpc" -) - -// Proxy will transparently proxy requests to an endpoint. -// If no endpoint is specified it will call a service using the client. -type Proxy struct { - // embed options - options proxy.Options - - // The client to use for outbound requests in the local network - Client client.Client - - // Endpoint to route all calls to - Endpoint string -} - -// read client request and write to server -func readLoop(r server.Request, s client.Stream) error { - // request to backend server - req := s.Request() - - for { - // get data from client - // no need to decode it - body, err := r.Read() - if err == io.EOF { - return nil - } - - if err != nil { - return err - } - - // get the header from client - hdr := r.Header() - msg := &codec.Message{ - Type: codec.Request, - Header: hdr, - Body: body, - } - - // write the raw request - err = req.Codec().Write(msg, nil) - if err == io.EOF { - return nil - } else if err != nil { - return err - } - } -} - -// ProcessMessage acts as a message exchange and forwards messages to ongoing topics -// TODO: should we look at p.Endpoint and only send to the local endpoint? probably -func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error { - // TODO: check that we're not broadcast storming by sending to the same topic - // that we're actually subscribed to - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy received message for %s", msg.Topic()) - } - - // directly publish to the local client - return p.Client.Publish(ctx, msg) -} - -// ServeRequest honours the server.Router interface -func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { - // service name to call - service := req.Service() - // endpoint to call - endpoint := req.Endpoint() - - if len(service) == 0 { - return errors.BadRequest("go.micro.proxy", "service name is blank") - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy received request for %s %s", service, endpoint) - } - - // no retries with the proxy - opts := []client.CallOption{ - client.WithRetries(0), - } - - // call a specific backend - if len(p.Endpoint) > 0 { - // address:port - if parts := strings.Split(p.Endpoint, ":"); len(parts) > 1 { - opts = append(opts, client.WithAddress(p.Endpoint)) - // use as service name - } else { - service = p.Endpoint - } - } - - // serve the normal way - return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...) -} - -func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error { - // read initial request - body, err := req.Read() - if err != nil { - return err - } - - // create new request with raw bytes body - creq := link.NewRequest(service, endpoint, &bytes.Frame{Data: body}, client.WithContentType(req.ContentType())) - - // not a stream so make a client.Call request - if !req.Stream() { - crsp := new(bytes.Frame) - - // make a call to the backend - if err := link.Call(ctx, creq, crsp, opts...); err != nil { - return err - } - - // write the response - if err := rsp.Write(crsp.Data); err != nil { - return err - } - - return nil - } - - // new context with cancel - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // create new stream - stream, err := link.Stream(ctx, creq, opts...) - if err != nil { - return err - } - defer stream.Close() - - // with a grpc stream we have to refire the initial request - // client request to start the server side - - // get the header from client - msg := &codec.Message{ - Type: codec.Request, - Header: req.Header(), - Body: body, - } - - // write the raw request - err = stream.Request().Codec().Write(msg, nil) - if err == io.EOF { - return nil - } else if err != nil { - return err - } - - // create client request read loop if streaming - go func() { - err := readLoop(req, stream) - if err != nil && err != io.EOF { - // cancel the context - cancel() - } - }() - - // get raw response - resp := stream.Response() - - // create server response write loop - for { - // read backend response body - body, err := resp.Read() - if err != nil { - // when we're done if its a grpc stream we have to set the trailer - if cc, ok := stream.(grpc.ClientStream); ok { - if ss, ok := resp.Codec().(grpc.ServerStream); ok { - ss.SetTrailer(cc.Trailer()) - } - } - } - - if err == io.EOF { - return nil - } else if err != nil { - return err - } - - // read backend response header - hdr := resp.Header() - - // write raw response header to client - rsp.WriteHeader(hdr) - - // write raw response body to client - err = rsp.Write(body) - if err == io.EOF { - return nil - } else if err != nil { - return err - } - } -} - -func (p *Proxy) String() string { - return "grpc" -} - -// NewProxy returns a new proxy which will route based on mucp headers -func NewProxy(opts ...proxy.Option) proxy.Proxy { - var options proxy.Options - - for _, o := range opts { - o(&options) - } - - // create a new grpc proxy - p := new(Proxy) - p.options = options - - // set the client - p.Client = options.Client - // set the endpoint - p.Endpoint = options.Endpoint - - // set the default client - if p.Client == nil { - p.Client = grpcc.NewClient() - } - - return p -} diff --git a/proxy/http/http.go b/proxy/http/http.go deleted file mode 100644 index a467d3fa..00000000 --- a/proxy/http/http.go +++ /dev/null @@ -1,211 +0,0 @@ -// Package http provides a micro rpc to http proxy -package http - -import ( - "bytes" - "context" - "io" - "io/ioutil" - "net/http" - "net/url" - "path" - - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/proxy" - "github.com/unistack-org/micro/v3/server" -) - -// Proxy will proxy rpc requests as http POST requests. It is a server.Proxy -type Proxy struct { - options proxy.Options - - // The http backend to call - Endpoint string -} - -func getMethod(hdr map[string]string) string { - switch hdr["Micro-Method"] { - case "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH": - return hdr["Micro-Method"] - default: - return "POST" - } -} - -func getEndpoint(hdr map[string]string) string { - ep := hdr["Micro-Endpoint"] - if len(ep) > 0 && ep[0] == '/' { - return ep - } - return "" -} - -func getTopic(hdr map[string]string) string { - ep := hdr["Micro-Topic"] - if len(ep) > 0 && ep[0] == '/' { - return ep - } - return "/" + hdr["Micro-Topic"] -} - -// ProcessMessage handles incoming asynchronous messages -func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error { - if p.Endpoint == "" { - p.Endpoint = proxy.DefaultEndpoint - } - - // get the header - hdr := msg.Header() - - // get topic - // use /topic as endpoint - endpoint := getTopic(hdr) - - // set the endpoint - if len(endpoint) == 0 { - endpoint = p.Endpoint - } else { - // add endpoint to backend - u, err := url.Parse(p.Endpoint) - if err != nil { - return errors.InternalServerError(msg.Topic(), err.Error()) - } - u.Path = path.Join(u.Path, endpoint) - endpoint = u.String() - } - - // send to backend - hreq, err := http.NewRequest("POST", endpoint, bytes.NewReader(msg.Body())) - if err != nil { - return errors.InternalServerError(msg.Topic(), err.Error()) - } - - // set the headers - for k, v := range hdr { - hreq.Header.Set(k, v) - } - - // make the call - hrsp, err := http.DefaultClient.Do(hreq) - if err != nil { - return errors.InternalServerError(msg.Topic(), err.Error()) - } - - // read body - b, err := ioutil.ReadAll(hrsp.Body) - hrsp.Body.Close() - if err != nil { - return errors.InternalServerError(msg.Topic(), err.Error()) - } - - if hrsp.StatusCode != 200 { - return errors.New(msg.Topic(), string(b), int32(hrsp.StatusCode)) - } - - return nil -} - -// ServeRequest honours the server.Router interface -func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { - if p.Endpoint == "" { - p.Endpoint = proxy.DefaultEndpoint - } - - for { - // get data - body, err := req.Read() - if err == io.EOF { - return nil - } - if err != nil { - return err - } - - // get the header - hdr := req.Header() - - // get method - method := getMethod(hdr) - - // get endpoint - endpoint := getEndpoint(hdr) - - // set the endpoint - if len(endpoint) == 0 { - endpoint = p.Endpoint - } else { - // add endpoint to backend - u, err := url.Parse(p.Endpoint) - if err != nil { - return errors.InternalServerError(req.Service(), err.Error()) - } - u.Path = path.Join(u.Path, endpoint) - endpoint = u.String() - } - - // send to backend - hreq, err := http.NewRequest(method, endpoint, bytes.NewReader(body)) - if err != nil { - return errors.InternalServerError(req.Service(), err.Error()) - } - - // set the headers - for k, v := range hdr { - hreq.Header.Set(k, v) - } - - // make the call - hrsp, err := http.DefaultClient.Do(hreq) - if err != nil { - return errors.InternalServerError(req.Service(), err.Error()) - } - - // read body - b, err := ioutil.ReadAll(hrsp.Body) - hrsp.Body.Close() - if err != nil { - return errors.InternalServerError(req.Service(), err.Error()) - } - - // set response headers - hdr = map[string]string{} - for k := range hrsp.Header { - hdr[k] = hrsp.Header.Get(k) - } - // write the header - rsp.WriteHeader(hdr) - // write the body - err = rsp.Write(b) - if err == io.EOF { - return nil - } - if err != nil { - return errors.InternalServerError(req.Service(), err.Error()) - } - } -} - -func (p *Proxy) String() string { - return "http" -} - -// NewSingleHostProxy returns a router which sends requests to a single http backend -func NewSingleHostProxy(url string) proxy.Proxy { - return &Proxy{ - Endpoint: url, - } -} - -// NewProxy returns a new proxy which will route using a http client -func NewProxy(opts ...proxy.Option) proxy.Proxy { - var options proxy.Options - for _, o := range opts { - o(&options) - } - - p := new(Proxy) - p.Endpoint = options.Endpoint - p.options = options - - return p -} diff --git a/proxy/http/http_test.go b/proxy/http/http_test.go deleted file mode 100644 index fef5da68..00000000 --- a/proxy/http/http_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package http - -import ( - "context" - "fmt" - "net" - "net/http" - "testing" - - "github.com/unistack-org/micro/v3/client" - cmucp "github.com/unistack-org/micro/v3/client/mucp" - "github.com/unistack-org/micro/v3/registry/memory" - "github.com/unistack-org/micro/v3/router" - "github.com/unistack-org/micro/v3/router/registry" - "github.com/unistack-org/micro/v3/server" - "github.com/unistack-org/micro/v3/server/mucp" -) - -type testHandler struct{} - -func (t *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`{"hello": "world"}`)) -} - -func TestHTTPProxy(t *testing.T) { - c, err := net.Listen("tcp", "localhost:0") - if err != nil { - t.Fatal(err) - } - defer c.Close() - addr := c.Addr().String() - - url := fmt.Sprintf("http://%s", addr) - - testCases := []struct { - // http endpoint to call e.g /foo/bar - httpEp string - // rpc endpoint called e.g Foo.Bar - rpcEp string - // should be an error - err bool - }{ - {"/", "Foo.Bar", false}, - {"/", "Foo.Baz", false}, - {"/helloworld", "Hello.World", true}, - } - - // handler - http.Handle("/", new(testHandler)) - - // new proxy - p := NewSingleHostProxy(url) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - reg := memory.NewRegistry() - rtr := registry.NewRouter( - router.Registry(reg), - ) - - // new micro service - service := mucp.NewServer( - server.Context(ctx), - server.Name("foobar"), - server.Registry(reg), - server.WithRouter(p), - ) - - service.Start() - defer service.Stop() - - // run service - // server - go http.Serve(c, nil) - - cl := cmucp.NewClient( - client.Router(rtr), - ) - - for _, test := range testCases { - req := cl.NewRequest("foobar", test.rpcEp, map[string]string{"foo": "bar"}, client.WithContentType("application/json")) - var rsp map[string]string - err := cl.Call(ctx, req, &rsp) - if err != nil && test.err == false { - t.Fatal(err) - } - if v := rsp["hello"]; v != "world" { - t.Fatalf("Expected hello world got %s from %s", v, test.rpcEp) - } - } -} diff --git a/proxy/mucp/mucp.go b/proxy/mucp/mucp.go deleted file mode 100644 index 7da5264b..00000000 --- a/proxy/mucp/mucp.go +++ /dev/null @@ -1,676 +0,0 @@ -// Package mucp transparently forwards the incoming request using a go-micro client. -package mucp - -import ( - "context" - "fmt" - "io" - "sort" - "strings" - "sync" - "time" - - "github.com/unistack-org/micro/v3/client" - grpcc "github.com/unistack-org/micro/v3/client/grpc" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/metadata" - "github.com/unistack-org/micro/v3/proxy" - "github.com/unistack-org/micro/v3/router" - "github.com/unistack-org/micro/v3/router/registry" - "github.com/unistack-org/micro/v3/selector" - "github.com/unistack-org/micro/v3/selector/roundrobin" - "github.com/unistack-org/micro/v3/server" - "google.golang.org/grpc" -) - -// Proxy will transparently proxy requests to an endpoint. -// If no endpoint is specified it will call a service using the client. -type Proxy struct { - // embed options - options proxy.Options - - // Endpoint specifies the fixed service endpoint to call. - Endpoint string - - // The client to use for outbound requests in the local network - Client client.Client - - // Links are used for outbound requests not in the local network - Links map[string]client.Client - - // The router for routes - Router router.Router - - // A fib of routes service:address - sync.RWMutex - Routes map[string]map[uint64]router.Route - - // selector used for load balancing - Selector selector.Selector -} - -// read client request and write to server -func readLoop(r server.Request, s client.Stream) error { - // request to backend server - req := s.Request() - - for { - // get data from client - // no need to decode it - body, err := r.Read() - if err == io.EOF { - return nil - } - - if err != nil { - return err - } - - // get the header from client - hdr := r.Header() - msg := &codec.Message{ - Type: codec.Request, - Header: hdr, - Body: body, - } - - // write the raw request - err = req.Codec().Write(msg, nil) - if err == io.EOF { - return nil - } else if err != nil { - return err - } - } -} - -// toNodes returns a list of node addresses from given routes -func toNodes(routes []router.Route) []string { - nodes := make([]string, 0, len(routes)) - - for _, node := range routes { - address := node.Address - if len(node.Gateway) > 0 { - address = node.Gateway - } - nodes = append(nodes, address) - } - - return nodes -} - -func toSlice(r map[uint64]router.Route) []router.Route { - routes := make([]router.Route, 0, len(r)) - - for _, v := range r { - routes = append(routes, v) - } - - // sort the routes in order of metric - sort.Slice(routes, func(i, j int) bool { return routes[i].Metric < routes[j].Metric }) - - return routes -} - -func (p *Proxy) filterRoutes(ctx context.Context, routes []router.Route) []router.Route { - md, ok := metadata.FromContext(ctx) - if !ok { - return routes - } - - //nolint:prealloc - var filteredRoutes []router.Route - - // filter the routes based on our headers - for _, route := range routes { - // process only routes for this id - if id, ok := md.Get("Micro-Router"); ok && len(id) > 0 { - if route.Router != id { - // skip routes that don't match - continue - } - } - - // only process routes with this network - if net, ok := md.Get("Micro-Namespace"); ok && len(net) > 0 { - if route.Network != router.DefaultNetwork && route.Network != net { - // skip routes that don't match - continue - } - } - - // process only this gateway - if gw, ok := md.Get("Micro-Gateway"); ok && len(gw) > 0 { - // if the gateway matches our address - // special case, take the routes with no gateway - // TODO: should we strip the gateway from the context? - if gw == p.Router.Options().Address { - if len(route.Gateway) > 0 && route.Gateway != gw { - continue - } - // otherwise its a local route and we're keeping it - } else { - // gateway does not match our own - if route.Gateway != gw { - continue - } - } - } - - // TODO: address based filtering - // address := md["Micro-Address"] - - // TODO: label based filtering - // requires new field in routing table : route.Labels - - // passed the filter checks - filteredRoutes = append(filteredRoutes, route) - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy filtered routes %+v", filteredRoutes) - } - - return filteredRoutes -} - -func (p *Proxy) getLink(r router.Route) (client.Client, error) { - if r.Link == "local" || len(p.Links) == 0 { - return p.Client, nil - } - l, ok := p.Links[r.Link] - if !ok { - return nil, errors.InternalServerError("go.micro.proxy", "link not found") - } - return l, nil -} - -func (p *Proxy) getRoute(ctx context.Context, service string) ([]router.Route, error) { - // lookup the route cache first - p.RLock() - cached, ok := p.Routes[service] - p.RUnlock() - if ok { - return p.filterRoutes(ctx, toSlice(cached)), nil - } - - // cache routes for the service - routes, err := p.cacheRoutes(service) - if err != nil { - return nil, err - } - - return p.filterRoutes(ctx, routes), nil -} - -func (p *Proxy) cacheRoutes(service string) ([]router.Route, error) { - // lookup the routes in the router - results, err := p.Router.Lookup(router.QueryService(service), router.QueryNetwork("*")) - if err != nil { - // assumption that we're ok with stale routes - logger.Debugf("Failed to lookup route for %s: %v", service, err) - // otherwise return the error - return nil, err - } - - // update the proxy cache - p.Lock() - - // delete the existing reference to the service - delete(p.Routes, service) - - for _, route := range results { - // create if does not exist - if _, ok := p.Routes[service]; !ok { - p.Routes[service] = make(map[uint64]router.Route) - } - // cache the route based on its unique hash - p.Routes[service][route.Hash()] = route - } - - // make a copy of the service routes - routes := p.Routes[service] - - p.Unlock() - - // return routes to the caller - return toSlice(routes), nil -} - -// refreshMetrics will refresh any metrics for our local cached routes. -// we may not receive new watch events for these as they change. -func (p *Proxy) refreshMetrics() { - // get a list of services to update - p.RLock() - - services := make([]string, 0, len(p.Routes)) - - for service := range p.Routes { - services = append(services, service) - } - - p.RUnlock() - - // get and cache the routes for the service - for _, service := range services { - p.cacheRoutes(service) - } -} - -// manageRoutes applies action on a given route to Proxy route cache -func (p *Proxy) manageRoutes(route router.Route, action string) error { - // we only cache what we are actually concerned with - p.Lock() - defer p.Unlock() - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy taking route action %v %+v\n", action, route) - } - - switch action { - case "create", "update": - if _, ok := p.Routes[route.Service]; !ok { - return fmt.Errorf("not called %s", route.Service) - } - p.Routes[route.Service][route.Hash()] = route - case "delete": - // delete that specific route - delete(p.Routes[route.Service], route.Hash()) - // clean up the cache entirely - if len(p.Routes[route.Service]) == 0 { - delete(p.Routes, route.Service) - } - default: - return fmt.Errorf("unknown action: %s", action) - } - - return nil -} - -// watchRoutes watches service routes and updates proxy cache -func (p *Proxy) watchRoutes() { - // route watcher - w, err := p.Router.Watch() - if err != nil { - logger.Debugf("Error watching router: %v", err) - return - } - defer w.Stop() - - for { - event, err := w.Next() - if err != nil { - logger.Debugf("Error watching router: %v", err) - return - } - - if err := p.manageRoutes(event.Route, event.Type.String()); err != nil { - // TODO: should we bail here? - continue - } - } -} - -// ProcessMessage acts as a message exchange and forwards messages to ongoing topics -// TODO: should we look at p.Endpoint and only send to the local endpoint? probably -func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error { - // TODO: check that we're not broadcast storming by sending to the same topic - // that we're actually subscribed to - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy received message for %s", msg.Topic()) - } - - var errors []string - - // directly publish to the local client - if err := p.Client.Publish(ctx, msg); err != nil { - errors = append(errors, err.Error()) - } - - // publish to all links - for _, client := range p.Links { - if err := client.Publish(ctx, msg); err != nil { - errors = append(errors, err.Error()) - } - } - - if len(errors) == 0 { - return nil - } - - // there is no error...muahaha - return fmt.Errorf("Message processing error: %s", strings.Join(errors, "\n")) -} - -// ServeRequest honours the server.Router interface -func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { - // determine if its local routing - var local bool - // address to call - var addresses []string - // routes - var routes []router.Route - // service name to call - service := req.Service() - // endpoint to call - endpoint := req.Endpoint() - - if len(service) == 0 { - return errors.BadRequest("go.micro.proxy", "service name is blank") - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy received request for %s %s", service, endpoint) - } - - // are we network routing or local routing - if len(p.Links) == 0 { - local = true - } - - // if there is a proxy being used, e.g. micro network, we don't need to - // look up the routes since they'll be ignored by the client - if len(p.Client.Options().Proxy) > 0 { - return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp) - } - - // call a specific backend endpoint either by name or address - if len(p.Endpoint) > 0 { - // address:port - if parts := strings.Split(p.Endpoint, ":"); len(parts) > 1 { - addresses = []string{p.Endpoint} - } else { - // get route for endpoint from router - addr, err := p.getRoute(ctx, p.Endpoint) - if err != nil { - return err - } - // set the address - routes = addr - // set the name - service = p.Endpoint - } - } else { - // no endpoint was specified just lookup the route - // get route for endpoint from router - addr, err := p.getRoute(ctx, service) - if err != nil { - return err - } - routes = addr - } - - //nolint:prealloc - opts := []client.CallOption{ - // set strategy to round robin - client.WithSelector(p.Selector), - } - - // if the address is already set just serve it - // TODO: figure it out if we should know to pick a link - if len(addresses) > 0 { - opts = append(opts, - client.WithAddress(addresses...), - ) - - // serve the normal way - return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...) - } - - // there's no links e.g we're local routing then just serve it with addresses - if local { - var opts []client.CallOption - - // set address if available via routes or specific endpoint - if len(routes) > 0 { - addresses = toNodes(routes) - opts = append(opts, client.WithAddress(addresses...)) - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy calling %+v\n", addresses) - } - // serve the normal way - return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...) - } - - // we're assuming we need routes to operate on - if len(routes) == 0 { - return errors.InternalServerError("go.micro.proxy", "route not found") - } - - var gerr error - - // we're routing globally with multiple links - // so we need to pick a link per route - for _, route := range routes { - // pick the link or error out - link, err := p.getLink(route) - if err != nil { - // ok let's try again - gerr = err - continue - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Proxy using route %+v\n", route) - } - - // set the address to call - addresses := toNodes([]router.Route{route}) - // set the address in the options - // disable retries since its one route processing - opts = append(opts, - client.WithAddress(addresses...), - client.WithRetries(0), - ) - - // do the request with the link - gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, opts...) - // return on no error since we succeeded - if gerr == nil { - return nil - } - - // return where the context deadline was exceeded - if gerr == context.Canceled || gerr == context.DeadlineExceeded { - return err - } - - // otherwise attempt to do it all over again - } - - // if we got here something went really badly wrong - return gerr -} - -func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error { - // read initial request - body, err := req.Read() - if err != nil { - return err - } - - // create new request with raw bytes body - creq := link.NewRequest(service, endpoint, &bytes.Frame{Data: body}, client.WithContentType(req.ContentType())) - - // not a stream so make a client.Call request - if !req.Stream() { - crsp := new(bytes.Frame) - - // make a call to the backend - if err := link.Call(ctx, creq, crsp, opts...); err != nil { - return err - } - - // write the response - if err := rsp.Write(crsp.Data); err != nil { - return err - } - - return nil - } - - // new context with cancel - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // create new stream - stream, err := link.Stream(ctx, creq, opts...) - if err != nil { - return err - } - defer stream.Close() - - // if we receive a grpc stream we have to refire the initial request - c, ok := req.Codec().(codec.Codec) - if ok && c.String() == "grpc" && link.String() == "grpc" { - // get the header from client - hdr := req.Header() - msg := &codec.Message{ - Type: codec.Request, - Header: hdr, - Body: body, - } - - // write the raw request - err = stream.Request().Codec().Write(msg, nil) - if err == io.EOF { - return nil - } else if err != nil { - return err - } - } - - // create client request read loop if streaming - go func() { - err := readLoop(req, stream) - if err != nil && err != io.EOF { - // cancel the context - cancel() - } - }() - - // get raw response - resp := stream.Response() - - // create server response write loop - for { - // read backend response body - body, err := resp.Read() - if err != nil { - // when we're done if its a grpc stream we have to set the trailer - if cc, ok := stream.(grpc.ClientStream); ok { - if ss, ok := resp.Codec().(grpc.ServerStream); ok { - ss.SetTrailer(cc.Trailer()) - } - } - } - - if err == io.EOF { - return nil - } else if err != nil { - return err - } - - // read backend response header - hdr := resp.Header() - - // write raw response header to client - rsp.WriteHeader(hdr) - - // write raw response body to client - err = rsp.Write(body) - if err == io.EOF { - return nil - } else if err != nil { - return err - } - } -} - -func (p *Proxy) String() string { - return "mucp" -} - -// NewSingleHostProxy returns a proxy which sends requests to a single backend -func NewSingleHostProxy(endpoint string) *Proxy { - return &Proxy{ - Endpoint: endpoint, - } -} - -// NewProxy returns a new proxy which will route based on mucp headers -func NewProxy(opts ...proxy.Option) proxy.Proxy { - var options proxy.Options - for _, o := range opts { - o(&options) - } - - p := new(Proxy) - p.Links = map[string]client.Client{} - p.Routes = make(map[string]map[uint64]router.Route) - p.options = options - - // get endpoint - p.Endpoint = options.Endpoint - // set the client - p.Client = options.Client - // get router - p.Router = options.Router - - // set the default client - if p.Client == nil { - p.Client = grpcc.NewClient() - } - - // create default router and start it - if p.Router == nil { - p.Router = registry.NewRouter() - } - - if p.Selector == nil { - p.Selector = roundrobin.NewSelector() - } - - // set the links - if options.Links != nil { - // get client - p.Links = options.Links - } - - // TODO: remove this cruft - // skip watching routes if proxy is set - if len(p.Client.Options().Proxy) > 0 { - return p - } - - go func() { - // continuously attempt to watch routes - for { - // watch the routes - p.watchRoutes() - // in case of failure just wait a second - time.Sleep(time.Second) - } - }() - - go func() { - // TODO: speed up refreshing of metrics - // without this ticking effort e.g stream - t := time.NewTicker(time.Second * 10) - defer t.Stop() - - // we must refresh route metrics since they do not trigger new events - for range t.C { - // refresh route metrics - p.refreshMetrics() - } - }() - - return p -} diff --git a/registry/cache/README.md b/registry/cache/README.md deleted file mode 100644 index ae208523..00000000 --- a/registry/cache/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Registry Cache - -Cache is a library that provides a caching layer for the go-micro [registry](https://godoc.org/github.com/micro/go-micro/registry#Registry). - -If you're looking for caching in your microservices use the [selector](https://micro.mu/docs/fault-tolerance.html#caching-discovery). - -## Interface - -```go -// Cache is the registry cache interface -type Cache interface { - // embed the registry interface - registry.Registry - // stop the cache watcher - Stop() -} -``` - -## Usage - -```go -import "github.com/micro/go-micro/registry/cache" - -# create a new cache -c := cache.New(registry) - -# get a service from the cache -services, _ := c.GetService("helloworld") -``` diff --git a/registry/cache/cache.go b/registry/cache/cache.go deleted file mode 100644 index d88b10dd..00000000 --- a/registry/cache/cache.go +++ /dev/null @@ -1,518 +0,0 @@ -// Package cache provides a registry cache -package cache - -import ( - "math" - "math/rand" - "sync" - "time" - - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/registry" - util "github.com/unistack-org/micro/v3/util/registry" -) - -// Cache is the registry cache interface -type Cache interface { - // embed the registry interface - registry.Registry - // stop the cache watcher - Stop() -} - -type Options struct { - // TTL is the cache TTL - TTL time.Duration -} - -type Option func(o *Options) - -type cache struct { - registry.Registry - opts Options - - // registry cache. services,ttls,watched,running are grouped by doman - sync.RWMutex - services map[string]services - ttls map[string]ttls - watched map[string]watched - running map[string]bool - - // used to stop the caches - exit chan bool - - // indicate whether its running status of the registry used to hold onto the cache in failure state - status error -} - -type services map[string][]*registry.Service -type ttls map[string]time.Time -type watched map[string]bool - -var defaultTTL = time.Minute - -func backoff(attempts int) time.Duration { - if attempts == 0 { - return time.Duration(0) - } - return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond -} - -func (c *cache) getStatus() error { - c.RLock() - defer c.RUnlock() - return c.status -} - -func (c *cache) setStatus(err error) { - c.Lock() - c.status = err - c.Unlock() -} - -// isValid checks if the service is valid -func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool { - // no services exist - if len(services) == 0 { - return false - } - - // ttl is invalid - if ttl.IsZero() { - return false - } - - // time since ttl is longer than timeout - if time.Since(ttl) > 0 { - return false - } - - // ok - return true -} - -func (c *cache) quit() bool { - select { - case <-c.exit: - return true - default: - return false - } -} - -func (c *cache) del(domain, service string) { - // don't blow away cache in error state - if err := c.getStatus(); err != nil { - return - } - - c.Lock() - defer c.Unlock() - - if _, ok := c.services[domain]; ok { - delete(c.services[domain], service) - } - - if _, ok := c.ttls[domain]; ok { - delete(c.ttls[domain], service) - } -} - -func (c *cache) get(domain, service string) ([]*registry.Service, error) { - var services []*registry.Service - var ttl time.Time - - // lookup the values in the cache before calling the underlying registrry - c.RLock() - if srvs, ok := c.services[domain]; ok { - services = srvs[service] - } - if tt, ok := c.ttls[domain]; ok { - ttl = tt[service] - } - c.RUnlock() - - // got services && within ttl so return a copy of the services - if c.isValid(services, ttl) { - return util.Copy(services), nil - } - - // get does the actual request for a service and cache it - get := func(domain string, service string, cached []*registry.Service) ([]*registry.Service, error) { - // ask the registry - services, err := c.Registry.GetService(service, registry.GetDomain(domain)) - if err != nil { - // set the error status - c.setStatus(err) - - // check the cache - if len(cached) > 0 { - return cached, nil - } - - // otherwise return error - return nil, err - } - - // reset the status - if err := c.getStatus(); err != nil { - c.setStatus(nil) - } - - // cache results - c.set(domain, service, util.Copy(services)) - - return services, nil - } - - // watch service if not watched - c.RLock() - var ok bool - if _, d := c.watched[domain]; d { - if _, s := c.watched[domain][service]; s { - ok = true - } - } - c.RUnlock() - - // check if its being watched - if !ok { - c.Lock() - - // add domain if not registered - if _, ok := c.watched[domain]; !ok { - c.watched[domain] = make(map[string]bool) - } - - // set to watched - c.watched[domain][service] = true - - running := c.running[domain] - c.Unlock() - - // only kick it off if not running - if !running { - go c.run(domain) - } - } - - // get and return services - return get(domain, service, services) -} - -func (c *cache) set(domain string, service string, srvs []*registry.Service) { - c.Lock() - defer c.Unlock() - - if _, ok := c.services[domain]; !ok { - c.services[domain] = make(services) - } - if _, ok := c.ttls[domain]; !ok { - c.ttls[domain] = make(ttls) - } - - c.services[domain][service] = srvs - c.ttls[domain][service] = time.Now().Add(c.opts.TTL) -} - -func (c *cache) update(domain string, res *registry.Result) { - if res == nil || res.Service == nil { - return - } - - // only save watched services since the service using the cache may only depend on a handful - // of other services - c.RLock() - if _, ok := c.watched[res.Service.Name]; !ok { - c.RUnlock() - return - } - - // we're not going to cache anything unless there was already a lookup - services, ok := c.services[domain][res.Service.Name] - if !ok { - c.RUnlock() - return - } - - c.RUnlock() - - if len(res.Service.Nodes) == 0 { - switch res.Action { - case "delete": - c.del(domain, res.Service.Name) - } - return - } - - // existing service found - var service *registry.Service - var index int - for i, s := range services { - if s.Version == res.Service.Version { - service = s - index = i - } - } - - switch res.Action { - case "create", "update": - if service == nil { - c.set(domain, res.Service.Name, append(services, res.Service)) - return - } - - // append old nodes to new service - for _, cur := range service.Nodes { - var seen bool - for _, node := range res.Service.Nodes { - if cur.Id == node.Id { - seen = true - break - } - } - if !seen { - res.Service.Nodes = append(res.Service.Nodes, cur) - } - } - - services[index] = res.Service - c.set(domain, res.Service.Name, services) - case "delete": - if service == nil { - return - } - - var nodes []*registry.Node - - // filter cur nodes to remove the dead one - for _, cur := range service.Nodes { - var seen bool - for _, del := range res.Service.Nodes { - if del.Id == cur.Id { - seen = true - break - } - } - if !seen { - nodes = append(nodes, cur) - } - } - - // still got nodes, save and return - if len(nodes) > 0 { - service.Nodes = nodes - services[index] = service - c.set(domain, service.Name, services) - return - } - - // zero nodes left - - // only have one thing to delete - // nuke the thing - if len(services) == 1 { - c.del(domain, service.Name) - return - } - - // still have more than 1 service - // check the version and keep what we know - var srvs []*registry.Service - for _, s := range services { - if s.Version != service.Version { - srvs = append(srvs, s) - } - } - - // save - c.set(domain, service.Name, srvs) - } -} - -// run starts the cache watcher loop -// it creates a new watcher if there's a problem -func (c *cache) run(domain string) { - c.Lock() - c.running[domain] = true - c.Unlock() - - // reset watcher on exit - defer func() { - c.Lock() - c.watched[domain] = make(map[string]bool) - c.running[domain] = false - c.Unlock() - }() - - var a, b int - - for { - // exit early if already dead - if c.quit() { - return - } - - // jitter before starting - j := rand.Int63n(100) - time.Sleep(time.Duration(j) * time.Millisecond) - - // create new watcher - w, err := c.Registry.Watch(registry.WatchDomain(domain)) - if err != nil { - if c.quit() { - return - } - - d := backoff(a) - c.setStatus(err) - - if a > 3 { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debug("rcache: ", err, " backing off ", d) - } - a = 0 - } - - time.Sleep(d) - a++ - - continue - } - - // reset a - a = 0 - - // watch for events - if err := c.watch(domain, w); err != nil { - if c.quit() { - return - } - - d := backoff(b) - c.setStatus(err) - - if b > 3 { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debug("rcache: ", err, " backing off ", d) - } - b = 0 - } - - time.Sleep(d) - b++ - - continue - } - - // reset b - b = 0 - } -} - -// watch loops the next event and calls update -// it returns if there's an error -func (c *cache) watch(domain string, w registry.Watcher) error { - // used to stop the watch - stop := make(chan bool) - - // manage this loop - go func() { - defer w.Stop() - - select { - // wait for exit - case <-c.exit: - return - // we've been stopped - case <-stop: - return - } - }() - - for { - res, err := w.Next() - if err != nil { - close(stop) - return err - } - - // reset the error status since we succeeded - if err := c.getStatus(); err != nil { - // reset status - c.setStatus(nil) - } - - // for wildcard queries, the domain will be * and not the services domain, so we'll check to - // see if it was provided in the metadata. - dom := domain - if res.Service.Metadata != nil && len(res.Service.Metadata["domain"]) > 0 { - dom = res.Service.Metadata["domain"] - } - - c.update(dom, res) - } -} - -func (c *cache) GetService(service string, opts ...registry.GetOption) ([]*registry.Service, error) { - // parse the options, fallback to the default domain - var options registry.GetOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = registry.DefaultDomain - } - - // get the service - services, err := c.get(options.Domain, service) - if err != nil { - return nil, err - } - - // if there's nothing return err - if len(services) == 0 { - return nil, registry.ErrNotFound - } - - // return services - return services, nil -} - -func (c *cache) Stop() { - c.Lock() - defer c.Unlock() - - select { - case <-c.exit: - return - default: - close(c.exit) - } -} - -func (c *cache) String() string { - return "cache" -} - -// New returns a new cache -func New(r registry.Registry, opts ...Option) Cache { - rand.Seed(time.Now().UnixNano()) - options := Options{ - TTL: defaultTTL, - } - - for _, o := range opts { - o(&options) - } - - return &cache{ - Registry: r, - opts: options, - running: make(map[string]bool), - watched: make(map[string]watched), - services: make(map[string]services), - ttls: make(map[string]ttls), - exit: make(chan bool), - } -} diff --git a/registry/cache/options.go b/registry/cache/options.go deleted file mode 100644 index 284721a9..00000000 --- a/registry/cache/options.go +++ /dev/null @@ -1,12 +0,0 @@ -package cache - -import ( - "time" -) - -// WithTTL sets the cache TTL -func WithTTL(t time.Duration) Option { - return func(o *Options) { - o.TTL = t - } -} diff --git a/registry/etcd/etcd.go b/registry/etcd/etcd.go deleted file mode 100644 index d47f0f3c..00000000 --- a/registry/etcd/etcd.go +++ /dev/null @@ -1,605 +0,0 @@ -// Package etcd provides an etcd service registry -package etcd - -import ( - "context" - "crypto/tls" - "encoding/json" - "errors" - "net" - "path" - "sort" - "strings" - "sync" - "time" - - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" - "github.com/coreos/etcd/mvcc/mvccpb" - hash "github.com/mitchellh/hashstructure" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/registry" - "go.uber.org/zap" -) - -const ( - prefix = "/micro/registry/" - defaultDomain = "micro" -) - -type etcdRegistry struct { - client *clientv3.Client - options registry.Options - - // register and leases are grouped by domain - sync.RWMutex - register map[string]register - leases map[string]leases -} - -type register map[string]uint64 -type leases map[string]clientv3.LeaseID - -// NewRegistry returns an initialized etcd registry -func NewRegistry(opts ...registry.Option) registry.Registry { - e := &etcdRegistry{ - options: registry.Options{}, - register: make(map[string]register), - leases: make(map[string]leases), - } - configure(e, opts...) - return e -} - -func newClient(e *etcdRegistry) (*clientv3.Client, error) { - config := clientv3.Config{ - Endpoints: []string{"127.0.0.1:2379"}, - } - - if e.options.Timeout == 0 { - e.options.Timeout = 5 * time.Second - } - - if e.options.Secure || e.options.TLSConfig != nil { - tlsConfig := e.options.TLSConfig - if tlsConfig == nil { - tlsConfig = &tls.Config{ - InsecureSkipVerify: true, - } - } - - config.TLS = tlsConfig - } - - if e.options.Context != nil { - u, ok := e.options.Context.Value(authKey{}).(*authCreds) - if ok { - config.Username = u.Username - config.Password = u.Password - } - cfg, ok := e.options.Context.Value(logConfigKey{}).(*zap.Config) - if ok && cfg != nil { - config.LogConfig = cfg - } - } - - var cAddrs []string - - for _, address := range e.options.Addrs { - if len(address) == 0 { - continue - } - addr, port, err := net.SplitHostPort(address) - if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" { - port = "2379" - addr = address - cAddrs = append(cAddrs, net.JoinHostPort(addr, port)) - } else if err == nil { - cAddrs = append(cAddrs, net.JoinHostPort(addr, port)) - } - } - - // if we got addrs then we'll update - if len(cAddrs) > 0 { - config.Endpoints = cAddrs - } - - // check if the endpoints have https:// - if config.TLS != nil { - for i, ep := range config.Endpoints { - if !strings.HasPrefix(ep, "https://") { - config.Endpoints[i] = "https://" + ep - } - } - } - - cli, err := clientv3.New(config) - if err != nil { - return nil, err - } - - return cli, nil -} - -// configure will setup the registry with new options -func configure(e *etcdRegistry, opts ...registry.Option) error { - for _, o := range opts { - o(&e.options) - } - - // setup the client - cli, err := newClient(e) - if err != nil { - return err - } - - if e.client != nil { - e.client.Close() - } - - // setup new client - e.client = cli - - return nil -} - -// getName returns the domain and name -// it returns false if there's an issue -// the key is a path of /prefix/domain/name/id e.g /micro/registry/domain/service/uuid -func getName(key, prefix string) (string, string, bool) { - // strip the prefix from keys - key = strings.TrimPrefix(key, prefix) - - // split the key so we remove domain - parts := strings.Split(key, "/") - - if len(parts) == 0 { - return "", "", false - } - - if len(parts[0]) == 0 { - parts = parts[1:] - } - - // we expect a domain and then name domain/service - if len(parts) < 2 { - return "", "", false - } - - // return name, domain - return parts[0], parts[1], true -} - -func encode(s *registry.Service) string { - b, _ := json.Marshal(s) - return string(b) -} - -func decode(ds []byte) *registry.Service { - var s *registry.Service - json.Unmarshal(ds, &s) - return s -} - -func nodePath(domain, s, id string) string { - service := strings.Replace(s, "/", "-", -1) - node := strings.Replace(id, "/", "-", -1) - return path.Join(prefixWithDomain(domain), service, node) -} - -func servicePath(domain, s string) string { - return path.Join(prefixWithDomain(domain), serializeServiceName(s)) -} - -func serializeServiceName(s string) string { - return strings.ReplaceAll(s, "/", "-") -} - -func prefixWithDomain(domain string) string { - return path.Join(prefix, domain) -} - -func (e *etcdRegistry) Init(opts ...registry.Option) error { - return configure(e, opts...) -} - -func (e *etcdRegistry) Options() registry.Options { - return e.options -} - -func (e *etcdRegistry) registerNode(s *registry.Service, node *registry.Node, opts ...registry.RegisterOption) error { - if len(s.Nodes) == 0 { - return errors.New("Require at least one node") - } - - // parse the options - var options registry.RegisterOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = defaultDomain - } - - if s.Metadata == nil { - s.Metadata = map[string]string{} - } - if node.Metadata == nil { - node.Metadata = map[string]string{} - } - - // set the domain in metadata so it can be retrieved by wildcard queries - s.Metadata["domain"] = options.Domain - node.Metadata["domain"] = options.Domain - - e.Lock() - // ensure the leases and registers are setup for this domain - if _, ok := e.leases[options.Domain]; !ok { - e.leases[options.Domain] = make(leases) - } - if _, ok := e.register[options.Domain]; !ok { - e.register[options.Domain] = make(register) - } - - // check to see if we already have a lease cached - leaseID, ok := e.leases[options.Domain][s.Name+node.Id] - e.Unlock() - - if !ok { - // missing lease, check if the key exists - ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout) - defer cancel() - - // look for the existing key - key := nodePath(options.Domain, s.Name, node.Id) - rsp, err := e.client.Get(ctx, key, clientv3.WithSerializable()) - if err != nil { - return err - } - - // get the existing lease - for _, kv := range rsp.Kvs { - if kv.Lease > 0 { - leaseID = clientv3.LeaseID(kv.Lease) - - // decode the existing node - srv := decode(kv.Value) - if srv == nil || len(srv.Nodes) == 0 { - continue - } - - // create hash of service; uint64 - h, err := hash.Hash(srv.Nodes[0], nil) - if err != nil { - continue - } - - // save the info - e.Lock() - e.leases[options.Domain][s.Name+node.Id] = leaseID - e.register[options.Domain][s.Name+node.Id] = h - e.Unlock() - - break - } - } - } - - var leaseNotFound bool - - // renew the lease if it exists - if leaseID > 0 { - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Renewing existing lease for %s %d", s.Name, leaseID) - } - - if _, err := e.client.KeepAliveOnce(context.TODO(), leaseID); err != nil { - if err != rpctypes.ErrLeaseNotFound { - return err - } - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Lease not found for %s %d", s.Name, leaseID) - } - - // lease not found do register - leaseNotFound = true - } - } - - // create hash of service; uint64 - h, err := hash.Hash(node, nil) - if err != nil { - return err - } - - // get existing hash for the service node - e.RLock() - v, ok := e.register[options.Domain][s.Name+node.Id] - e.RUnlock() - - // the service is unchanged, skip registering - if ok && v == h && !leaseNotFound { - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Service %s node %s unchanged skipping registration", s.Name, node.Id) - } - return nil - } - - // add domain to the service metadata so it can be determined when doing wildcard queries - if s.Metadata == nil { - s.Metadata = map[string]string{"domain": options.Domain} - } else { - s.Metadata["domain"] = options.Domain - } - - service := ®istry.Service{ - Name: s.Name, - Version: s.Version, - Metadata: s.Metadata, - Endpoints: s.Endpoints, - Nodes: []*registry.Node{node}, - } - - ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout) - defer cancel() - - var lgr *clientv3.LeaseGrantResponse - if options.TTL.Seconds() > 0 { - // get a lease used to expire keys since we have a ttl - lgr, err = e.client.Grant(ctx, int64(options.TTL.Seconds())) - if err != nil { - return err - } - } - - // create an entry for the node - var putOpts []clientv3.OpOption - if lgr != nil { - putOpts = append(putOpts, clientv3.WithLease(lgr.ID)) - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Registering %s id %s with lease %v and leaseID %v and ttl %v", service.Name, node.Id, lgr, lgr.ID, options.TTL) - } - } else if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Registering %s id %s without lease", service.Name, node.Id) - } - - key := nodePath(options.Domain, s.Name, node.Id) - if _, err = e.client.Put(ctx, key, encode(service), putOpts...); err != nil { - return err - } - - e.Lock() - // save our hash of the service - e.register[options.Domain][s.Name+node.Id] = h - // save our leaseID of the service - if lgr != nil { - e.leases[options.Domain][s.Name+node.Id] = lgr.ID - } - e.Unlock() - - return nil -} - -func (e *etcdRegistry) Deregister(s *registry.Service, opts ...registry.DeregisterOption) error { - if len(s.Nodes) == 0 { - return errors.New("Require at least one node") - } - - // parse the options - var options registry.DeregisterOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = defaultDomain - } - - for _, node := range s.Nodes { - e.Lock() - // delete our hash of the service - nodes, ok := e.register[options.Domain] - if ok { - delete(nodes, s.Name+node.Id) - e.register[options.Domain] = nodes - } - - // delete our lease of the service - leases, ok := e.leases[options.Domain] - if ok { - delete(leases, s.Name+node.Id) - e.leases[options.Domain] = leases - } - e.Unlock() - - ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout) - defer cancel() - - if logger.V(logger.TraceLevel, logger.DefaultLogger) { - logger.Tracef("Deregistering %s id %s", s.Name, node.Id) - } - - if _, err := e.client.Delete(ctx, nodePath(options.Domain, s.Name, node.Id)); err != nil { - return err - } - } - - return nil -} - -func (e *etcdRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error { - if len(s.Nodes) == 0 { - return errors.New("Require at least one node") - } - - var gerr error - - // register each node individually - for _, node := range s.Nodes { - if err := e.registerNode(s, node, opts...); err != nil { - gerr = err - } - } - - return gerr -} - -func (e *etcdRegistry) GetService(name string, opts ...registry.GetOption) ([]*registry.Service, error) { - ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout) - defer cancel() - - // parse the options and fallback to the default domain - var options registry.GetOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = defaultDomain - } - - var results []*mvccpb.KeyValue - - // TODO: refactorout wildcard, this is an incredibly expensive operation - if options.Domain == registry.WildcardDomain { - rsp, err := e.client.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithSerializable()) - if err != nil { - return nil, err - } - - // filter the results for the key we care about - for _, kv := range rsp.Kvs { - // if the key does not contain the name then pass - _, service, ok := getName(string(kv.Key), prefix) - if !ok || service != name { - continue - } - - // save the result if its what we expect - results = append(results, kv) - } - } else { - prefix := servicePath(options.Domain, name) + "/" - rsp, err := e.client.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithSerializable()) - if err != nil { - return nil, err - } - results = rsp.Kvs - } - - if len(results) == 0 { - return nil, registry.ErrNotFound - } - - versions := make(map[string]*registry.Service) - - for _, n := range results { - // only process the things we care about - domain, service, ok := getName(string(n.Key), prefix) - if !ok || service != name { - continue - } - - if sn := decode(n.Value); sn != nil { - // compose a key of name/version/domain - key := sn.Name + sn.Version + domain - - s, ok := versions[key] - if !ok { - s = ®istry.Service{ - Name: sn.Name, - Version: sn.Version, - Metadata: sn.Metadata, - Endpoints: sn.Endpoints, - } - versions[key] = s - } - s.Nodes = append(s.Nodes, sn.Nodes...) - } - } - - services := make([]*registry.Service, 0, len(versions)) - - for _, service := range versions { - services = append(services, service) - } - - return services, nil -} - -func (e *etcdRegistry) ListServices(opts ...registry.ListOption) ([]*registry.Service, error) { - // parse the options - var options registry.ListOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = defaultDomain - } - - // determine the prefix - var p string - if options.Domain == registry.WildcardDomain { - p = prefix - } else { - p = prefixWithDomain(options.Domain) - } - - ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout) - defer cancel() - - rsp, err := e.client.Get(ctx, p, clientv3.WithPrefix(), clientv3.WithSerializable()) - if err != nil { - return nil, err - } - if len(rsp.Kvs) == 0 { - return []*registry.Service{}, nil - } - - versions := make(map[string]*registry.Service) - for _, n := range rsp.Kvs { - domain, service, ok := getName(string(n.Key), prefix) - if !ok { - continue - } - - sn := decode(n.Value) - if sn == nil || sn.Name != service { - continue - } - - // key based on name/version/domain - key := sn.Name + sn.Version + domain - - v, ok := versions[key] - if !ok { - versions[key] = sn - continue - } - - // append to service:version nodes - v.Nodes = append(v.Nodes, sn.Nodes...) - } - - services := make([]*registry.Service, 0, len(versions)) - for _, service := range versions { - services = append(services, service) - } - - // sort the services - sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name }) - - return services, nil -} - -func (e *etcdRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) { - cli, err := newClient(e) - if err != nil { - return nil, err - } - return newEtcdWatcher(cli, e.options.Timeout, opts...) -} - -func (e *etcdRegistry) String() string { - return "etcd" -} diff --git a/registry/etcd/etcd_test.go b/registry/etcd/etcd_test.go deleted file mode 100644 index 35c5668c..00000000 --- a/registry/etcd/etcd_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package etcd - -import ( - "testing" -) - -// test whether the name matches -func TestEtcdHasName(t *testing.T) { - testCases := []struct { - key string - prefix string - name string - domain string - expect bool - }{ - { - "/micro/registry/micro/registry", - "/micro/registry", - "registry", - "micro", - true, - }, - { - "/micro/registry/micro", - "/micro/registry", - "store", - "micro", - false, - }, - { - "/prefix/baz/*/registry", - "/prefix/baz", - "registry", - "*", - true, - }, - { - "/prefix/baz", - "/prefix/baz", - "store", - "micro", - false, - }, - { - "/prefix/baz/foobar/registry", - "/prefix/baz", - "registry", - "foobar", - true, - }, - } - - for _, c := range testCases { - domain, service, ok := getName(c.key, c.prefix) - if ok != c.expect { - t.Fatalf("Expected %t for %v got: %t", c.expect, c, ok) - } - if !ok { - continue - } - if service != c.name { - t.Fatalf("Expected service %s got %s", c.name, service) - } - if domain != c.domain { - t.Fatalf("Expected domain %s got %s", c.domain, domain) - } - } -} diff --git a/registry/etcd/options.go b/registry/etcd/options.go deleted file mode 100644 index c2833804..00000000 --- a/registry/etcd/options.go +++ /dev/null @@ -1,37 +0,0 @@ -package etcd - -import ( - "context" - - "github.com/unistack-org/micro/v3/registry" - "go.uber.org/zap" -) - -type authKey struct{} - -type logConfigKey struct{} - -type authCreds struct { - Username string - Password string -} - -// Auth allows you to specify username/password -func Auth(username, password string) registry.Option { - return func(o *registry.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, authKey{}, &authCreds{Username: username, Password: password}) - } -} - -// LogConfig allows you to set etcd log config -func LogConfig(config *zap.Config) registry.Option { - return func(o *registry.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, logConfigKey{}, config) - } -} diff --git a/registry/etcd/watcher.go b/registry/etcd/watcher.go deleted file mode 100644 index 72c466bb..00000000 --- a/registry/etcd/watcher.go +++ /dev/null @@ -1,105 +0,0 @@ -package etcd - -import ( - "context" - "errors" - "sync" - "time" - - "github.com/coreos/etcd/clientv3" - "github.com/unistack-org/micro/v3/registry" -) - -type etcdWatcher struct { - w clientv3.WatchChan - client *clientv3.Client - timeout time.Duration - - mtx sync.Mutex - stop chan bool - cancel func() -} - -func newEtcdWatcher(c *clientv3.Client, timeout time.Duration, opts ...registry.WatchOption) (registry.Watcher, error) { - var wo registry.WatchOptions - for _, o := range opts { - o(&wo) - } - if len(wo.Domain) == 0 { - wo.Domain = defaultDomain - } - - watchPath := prefix - if wo.Domain == registry.WildcardDomain { - if len(wo.Service) > 0 { - return nil, errors.New("Cannot watch a service across domains") - } - watchPath = prefix - } else if len(wo.Service) > 0 { - watchPath = servicePath(wo.Domain, wo.Service) + "/" - } - - ctx, cancel := context.WithCancel(context.Background()) - w := c.Watch(ctx, watchPath, clientv3.WithPrefix(), clientv3.WithPrevKV()) - stop := make(chan bool, 1) - - return &etcdWatcher{ - cancel: cancel, - stop: stop, - w: w, - client: c, - timeout: timeout, - }, nil -} - -func (ew *etcdWatcher) Next() (*registry.Result, error) { - for wresp := range ew.w { - if wresp.Err() != nil { - return nil, wresp.Err() - } - if wresp.Canceled { - return nil, errors.New("could not get next") - } - for _, ev := range wresp.Events { - service := decode(ev.Kv.Value) - var action string - - switch ev.Type { - case clientv3.EventTypePut: - if ev.IsCreate() { - action = "create" - } else if ev.IsModify() { - action = "update" - } - case clientv3.EventTypeDelete: - action = "delete" - - // get service from prevKv - service = decode(ev.PrevKv.Value) - } - - if service == nil { - continue - } - return ®istry.Result{ - Action: action, - Service: service, - }, nil - } - } - return nil, errors.New("could not get next") -} - -func (ew *etcdWatcher) Stop() { - ew.mtx.Lock() - defer ew.mtx.Unlock() - - select { - case <-ew.stop: - return - default: - close(ew.stop) - ew.cancel() - ew.client.Close() - } -} diff --git a/registry/mdns/mdns.go b/registry/mdns/mdns.go deleted file mode 100644 index 6d7b42af..00000000 --- a/registry/mdns/mdns.go +++ /dev/null @@ -1,770 +0,0 @@ -package mdns - -import ( - "bytes" - "compress/zlib" - "context" - "encoding/hex" - "encoding/json" - "fmt" - "io/ioutil" - "net" - "strconv" - "strings" - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/util/mdns" -) - -const ( - // every service is written to the global domain so * domain queries work, e.g. - // calling mdns.List(registry.ListDomain("*")) will list the services across all - // domains - globalDomain = "global" -) - -type mdnsTxt struct { - Service string - Version string - Endpoints []*registry.Endpoint - Metadata map[string]string -} - -type mdnsEntry struct { - id string - node *mdns.Server -} - -// services are a key/value map, with the service name as a key and the value being a -// slice of mdns entries, representing the nodes with a single _services entry to be -// used for listing -type services map[string][]*mdnsEntry - -// mdsRegistry is a multicast dns registry -type mdnsRegistry struct { - opts registry.Options - - // the top level domains, these can be overriden using options - defaultDomain string - globalDomain string - - sync.Mutex - domains map[string]services - - mtx sync.RWMutex - - // watchers - watchers map[string]*mdnsWatcher - - // listener - listener chan *mdns.ServiceEntry -} - -type mdnsWatcher struct { - id string - wo registry.WatchOptions - ch chan *mdns.ServiceEntry - exit chan struct{} - // the mdns domain - domain string - // the registry - registry *mdnsRegistry -} - -func encode(txt *mdnsTxt) ([]string, error) { - b, err := json.Marshal(txt) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - defer buf.Reset() - - w := zlib.NewWriter(&buf) - defer func() { - if closeErr := w.Close(); closeErr != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("[mdns] registry close encoding writer err: %v", closeErr) - } - } - }() - if _, err := w.Write(b); err != nil { - return nil, err - } - - if err = w.Close(); err != nil { - return nil, err - } - - encoded := hex.EncodeToString(buf.Bytes()) - // individual txt limit - if len(encoded) <= 255 { - return []string{encoded}, nil - } - - // split encoded string - var record []string - - for len(encoded) > 255 { - record = append(record, encoded[:255]) - encoded = encoded[255:] - } - - record = append(record, encoded) - - return record, nil -} - -func decode(record []string) (*mdnsTxt, error) { - encoded := strings.Join(record, "") - - hr, err := hex.DecodeString(encoded) - if err != nil { - return nil, err - } - - br := bytes.NewReader(hr) - zr, err := zlib.NewReader(br) - if err != nil { - return nil, err - } - defer zr.Close() - - rbuf, err := ioutil.ReadAll(zr) - if err != nil { - return nil, err - } - - var txt *mdnsTxt - - if err := json.Unmarshal(rbuf, &txt); err != nil { - return nil, err - } - - return txt, nil -} - -func newRegistry(opts ...registry.Option) registry.Registry { - options := registry.Options{ - Context: context.Background(), - Timeout: time.Millisecond * 100, - } - - for _, o := range opts { - o(&options) - } - - // set the domain - defaultDomain := registry.DefaultDomain - if d, ok := options.Context.Value("mdns.domain").(string); ok { - defaultDomain = d - } - - return &mdnsRegistry{ - defaultDomain: defaultDomain, - globalDomain: globalDomain, - opts: options, - domains: make(map[string]services), - watchers: make(map[string]*mdnsWatcher), - } -} - -func (m *mdnsRegistry) Init(opts ...registry.Option) error { - for _, o := range opts { - o(&m.opts) - } - return nil -} - -func (m *mdnsRegistry) Options() registry.Options { - return m.opts -} - -// createServiceMDNSEntry will create a new wildcard mdns entry for the service in the -// given domain. This wildcard mdns entry is used when listing services. -func createServiceMDNSEntry(name, domain string) (*mdnsEntry, error) { - ip := net.ParseIP("0.0.0.0") - - s, err := mdns.NewMDNSService(name, "_services", domain+".", "", 9999, []net.IP{ip}, nil) - if err != nil { - return nil, err - } - - srv, err := mdns.NewServer(&mdns.Config{Zone: &mdns.DNSSDService{MDNSService: s}, LocalhostChecking: true}) - if err != nil { - return nil, err - } - - return &mdnsEntry{id: "*", node: srv}, nil -} - -func (m *mdnsRegistry) createMDNSEntries(domain, serviceName string) ([]*mdnsEntry, error) { - // if it already exists don't reegister it again - entries, ok := m.domains[domain][serviceName] - if ok { - return entries, nil - } - - // create the wildcard entry used for list queries in this domain - entry, err := createServiceMDNSEntry(serviceName, domain) - if err != nil { - return nil, err - } - - return []*mdnsEntry{entry}, nil -} - -func registerService(service *registry.Service, entries []*mdnsEntry, options registry.RegisterOptions) ([]*mdnsEntry, error) { - var lastError error - for _, node := range service.Nodes { - var seen bool - - for _, entry := range entries { - if node.Id == entry.id { - seen = true - break - } - } - - // this node has already been registered, continue - if seen { - continue - } - - txt, err := encode(&mdnsTxt{ - Service: service.Name, - Version: service.Version, - Endpoints: service.Endpoints, - Metadata: node.Metadata, - }) - - if err != nil { - lastError = err - continue - } - - host, pt, err := net.SplitHostPort(node.Address) - if err != nil { - lastError = err - continue - } - port, _ := strconv.Atoi(pt) - - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("[mdns] registry create new service with ip: %s for: %s", net.ParseIP(host).String(), host) - } - // we got here, new node - s, err := mdns.NewMDNSService( - node.Id, - service.Name, - options.Domain+".", - "", - port, - []net.IP{net.ParseIP(host)}, - txt, - ) - if err != nil { - lastError = err - continue - } - - srv, err := mdns.NewServer(&mdns.Config{Zone: s, LocalhostChecking: true}) - if err != nil { - lastError = err - continue - } - - entries = append(entries, &mdnsEntry{id: node.Id, node: srv}) - } - - return entries, lastError -} - -func createGlobalDomainService(service *registry.Service, options registry.RegisterOptions) *registry.Service { - srv := *service - srv.Nodes = nil - - for _, n := range service.Nodes { - node := n - - // set the original domain in node metadata - if node.Metadata == nil { - node.Metadata = map[string]string{"domain": options.Domain} - } else { - node.Metadata["domain"] = options.Domain - } - - srv.Nodes = append(srv.Nodes, node) - } - - return &srv -} - -func (m *mdnsRegistry) Register(service *registry.Service, opts ...registry.RegisterOption) error { - m.Lock() - - // parse the options - var options registry.RegisterOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = m.defaultDomain - } - - // create the domain in the memory store if it doesn't yet exist - if _, ok := m.domains[options.Domain]; !ok { - m.domains[options.Domain] = make(services) - } - - entries, err := m.createMDNSEntries(options.Domain, service.Name) - if err != nil { - m.Unlock() - return err - } - - entries, gerr := registerService(service, entries, options) - - // save the mdns entry - m.domains[options.Domain][service.Name] = entries - m.Unlock() - - // register in the global Domain so it can be queried as one - if options.Domain != m.globalDomain { - srv := createGlobalDomainService(service, options) - if err := m.Register(srv, append(opts, registry.RegisterDomain(m.globalDomain))...); err != nil { - gerr = err - } - } - - return gerr -} - -func (m *mdnsRegistry) Deregister(service *registry.Service, opts ...registry.DeregisterOption) error { - // parse the options - var options registry.DeregisterOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = m.defaultDomain - } - - // register in the global Domain - var err error - if options.Domain != m.globalDomain { - defer func() { - err = m.Deregister(service, append(opts, registry.DeregisterDomain(m.globalDomain))...) - }() - } - - // we want to unlock before we call deregister on the global domain, so it's important this unlock - // is applied after the defer m.Deregister is called above - m.Lock() - defer m.Unlock() - - // the service wasn't registered, we can safely exist - if _, ok := m.domains[options.Domain]; !ok { - return err - } - - // loop existing entries, check if any match, shutdown those that do - var newEntries []*mdnsEntry - for _, entry := range m.domains[options.Domain][service.Name] { - var remove bool - - for _, node := range service.Nodes { - if node.Id == entry.id { - entry.node.Shutdown() - remove = true - break - } - } - - // keep it? - if !remove { - newEntries = append(newEntries, entry) - } - } - - // we have no new entries, we can exit - if len(newEntries) == 0 { - return nil - } - - // we have more than one entry remaining, we can exit - if len(newEntries) > 1 { - m.domains[options.Domain][service.Name] = newEntries - return err - } - - // our remaining entry is not a wildcard, we can exit - if len(newEntries) == 1 && newEntries[0].id != "*" { - m.domains[options.Domain][service.Name] = newEntries - return err - } - - // last entry is the wildcard for list queries. Remove it. - newEntries[0].node.Shutdown() - delete(m.domains[options.Domain], service.Name) - - // check to see if we can delete the domain entry - if len(m.domains[options.Domain]) == 0 { - delete(m.domains, options.Domain) - } - - return err -} - -func (m *mdnsRegistry) GetService(service string, opts ...registry.GetOption) ([]*registry.Service, error) { - // parse the options - var options registry.GetOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = m.defaultDomain - } - if options.Domain == registry.WildcardDomain { - options.Domain = m.globalDomain - } - - serviceMap := make(map[string]*registry.Service) - entries := make(chan *mdns.ServiceEntry, 10) - done := make(chan bool) - - p := mdns.DefaultParams(service) - // set context with timeout - var cancel context.CancelFunc - p.Context, cancel = context.WithTimeout(context.Background(), m.opts.Timeout) - defer cancel() - // set entries channel - p.Entries = entries - // set the domain - p.Domain = options.Domain - - go func() { - for { - select { - case e := <-entries: - // list record so skip - if e.Name == "_services" { - continue - } - if e.TTL == 0 { - continue - } - - txt, err := decode(e.InfoFields) - if err != nil { - continue - } - - if txt.Service != service { - continue - } - - s, ok := serviceMap[txt.Version] - if !ok { - s = ®istry.Service{ - Name: txt.Service, - Version: txt.Version, - Endpoints: txt.Endpoints, - } - } - addr := "" - // prefer ipv4 addrs - if len(e.AddrV4) > 0 { - addr = e.AddrV4.String() - // else use ipv6 - } else if len(e.AddrV6) > 0 { - addr = "[" + e.AddrV6.String() + "]" - } else { - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("[mdns]: invalid endpoint received: %v", e) - } - continue - } - s.Nodes = append(s.Nodes, ®istry.Node{ - Id: strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+"."), - Address: fmt.Sprintf("%s:%d", addr, e.Port), - Metadata: txt.Metadata, - }) - - serviceMap[txt.Version] = s - case <-p.Context.Done(): - close(done) - return - } - } - }() - - // execute the query - if err := mdns.Query(p); err != nil { - return nil, err - } - - // wait for completion - <-done - - // create list and return - services := make([]*registry.Service, 0, len(serviceMap)) - - for _, service := range serviceMap { - services = append(services, service) - } - - return services, nil -} - -func (m *mdnsRegistry) ListServices(opts ...registry.ListOption) ([]*registry.Service, error) { - // parse the options - var options registry.ListOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = m.defaultDomain - } - if options.Domain == registry.WildcardDomain { - options.Domain = m.globalDomain - } - - serviceMap := make(map[string]bool) - entries := make(chan *mdns.ServiceEntry, 10) - done := make(chan bool) - - p := mdns.DefaultParams("_services") - // set context with timeout - var cancel context.CancelFunc - p.Context, cancel = context.WithTimeout(context.Background(), m.opts.Timeout) - defer cancel() - // set entries channel - p.Entries = entries - // set domain - p.Domain = options.Domain - - var services []*registry.Service - - go func() { - for { - select { - case e := <-entries: - if e.TTL == 0 { - continue - } - if !strings.HasSuffix(e.Name, p.Domain+".") { - continue - } - name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".") - if !serviceMap[name] { - serviceMap[name] = true - services = append(services, ®istry.Service{Name: name}) - } - case <-p.Context.Done(): - close(done) - return - } - } - }() - - // execute query - if err := mdns.Query(p); err != nil { - return nil, err - } - - // wait till done - <-done - - return services, nil -} - -func (m *mdnsRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) { - var wo registry.WatchOptions - for _, o := range opts { - o(&wo) - } - if len(wo.Domain) == 0 { - wo.Domain = m.defaultDomain - } - if wo.Domain == registry.WildcardDomain { - wo.Domain = m.globalDomain - } - - md := &mdnsWatcher{ - id: uuid.New().String(), - wo: wo, - ch: make(chan *mdns.ServiceEntry, 32), - exit: make(chan struct{}), - domain: wo.Domain, - registry: m, - } - - m.mtx.Lock() - defer m.mtx.Unlock() - - // save the watcher - m.watchers[md.id] = md - - // check of the listener exists - if m.listener != nil { - return md, nil - } - - // start the listener - go func() { - // go to infinity - for { - m.mtx.Lock() - - // just return if there are no watchers - if len(m.watchers) == 0 { - m.listener = nil - m.mtx.Unlock() - return - } - - // check existing listener - if m.listener != nil { - m.mtx.Unlock() - return - } - - // reset the listener - exit := make(chan struct{}) - ch := make(chan *mdns.ServiceEntry, 32) - m.listener = ch - - m.mtx.Unlock() - - // send messages to the watchers - go func() { - send := func(w *mdnsWatcher, e *mdns.ServiceEntry) { - select { - case w.ch <- e: - default: - } - } - - for { - select { - case <-exit: - return - case e, ok := <-ch: - if !ok { - return - } - m.mtx.RLock() - // send service entry to all watchers - for _, w := range m.watchers { - send(w, e) - } - m.mtx.RUnlock() - } - } - - }() - - // start listening, blocking call - mdns.Listen(ch, exit) - - // mdns.Listen has unblocked - // kill the saved listener - m.mtx.Lock() - m.listener = nil - close(ch) - m.mtx.Unlock() - } - }() - - return md, nil -} - -func (m *mdnsRegistry) String() string { - return "mdns" -} - -func (m *mdnsWatcher) Next() (*registry.Result, error) { - for { - select { - case e := <-m.ch: - txt, err := decode(e.InfoFields) - if err != nil { - continue - } - - if len(txt.Service) == 0 || len(txt.Version) == 0 { - continue - } - - // Filter watch options - // wo.Service: Only keep services we care about - if len(m.wo.Service) > 0 && txt.Service != m.wo.Service { - continue - } - var action string - if e.TTL == 0 { - action = "delete" - } else { - action = "create" - } - - service := ®istry.Service{ - Name: txt.Service, - Version: txt.Version, - Endpoints: txt.Endpoints, - Metadata: txt.Metadata, - } - - // skip anything without the domain we care about - suffix := fmt.Sprintf(".%s.%s.", service.Name, m.domain) - if !strings.HasSuffix(e.Name, suffix) { - continue - } - - var addr string - if len(e.AddrV4) > 0 { - addr = e.AddrV4.String() - } else if len(e.AddrV6) > 0 { - addr = "[" + e.AddrV6.String() + "]" - } else { - addr = e.Addr.String() - } - - service.Nodes = append(service.Nodes, ®istry.Node{ - Id: strings.TrimSuffix(e.Name, suffix), - Address: fmt.Sprintf("%s:%d", addr, e.Port), - Metadata: txt.Metadata, - }) - - return ®istry.Result{ - Action: action, - Service: service, - }, nil - case <-m.exit: - return nil, registry.ErrWatcherStopped - } - } -} - -func (m *mdnsWatcher) Stop() { - select { - case <-m.exit: - return - default: - close(m.exit) - // remove self from the registry - m.registry.mtx.Lock() - delete(m.registry.watchers, m.id) - m.registry.mtx.Unlock() - } -} - -// NewRegistry returns a new default registry which is mdns -func NewRegistry(opts ...registry.Option) registry.Registry { - return newRegistry(opts...) -} diff --git a/registry/mdns/mdns_test.go b/registry/mdns/mdns_test.go deleted file mode 100644 index ec5a5c33..00000000 --- a/registry/mdns/mdns_test.go +++ /dev/null @@ -1,342 +0,0 @@ -package mdns - -import ( - "os" - "testing" - "time" - - "github.com/unistack-org/micro/v3/registry" -) - -func TestMDNS(t *testing.T) { - // skip test in travis because of sendto: operation not permitted error - if travis := os.Getenv("TRAVIS"); travis == "true" { - t.Skip() - } - - testData := []*registry.Service{ - { - Name: "test1", - Version: "1.0.1", - Nodes: []*registry.Node{ - { - Id: "test1-1", - Address: "10.0.0.1:10001", - Metadata: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - { - Name: "test2", - Version: "1.0.2", - Nodes: []*registry.Node{ - { - Id: "test2-1", - Address: "10.0.0.2:10002", - Metadata: map[string]string{ - "foo2": "bar2", - }, - }, - }, - }, - { - Name: "test3", - Version: "1.0.3", - Nodes: []*registry.Node{ - { - Id: "test3-1", - Address: "10.0.0.3:10003", - Metadata: map[string]string{ - "foo3": "bar3", - }, - }, - }, - }, - } - - travis := os.Getenv("TRAVIS") - - var opts []registry.Option - - if travis == "true" { - opts = append(opts, registry.Timeout(time.Millisecond*100)) - } - - // new registry - r := NewRegistry(opts...) - - for _, service := range testData { - // register service - if err := r.Register(service); err != nil { - t.Fatal(err) - } - - // get registered service - s, err := r.GetService(service.Name) - if err != nil { - t.Fatal(err) - } - - if len(s) != 1 { - t.Fatalf("Expected one result for %s got %d", service.Name, len(s)) - } - - if s[0].Name != service.Name { - t.Fatalf("Expected name %s got %s", service.Name, s[0].Name) - } - - if s[0].Version != service.Version { - t.Fatalf("Expected version %s got %s", service.Version, s[0].Version) - } - - if len(s[0].Nodes) != 1 { - t.Fatalf("Expected 1 node, got %d", len(s[0].Nodes)) - } - - node := s[0].Nodes[0] - - if node.Id != service.Nodes[0].Id { - t.Fatalf("Expected node id %s got %s", service.Nodes[0].Id, node.Id) - } - - if node.Address != service.Nodes[0].Address { - t.Fatalf("Expected node address %s got %s", service.Nodes[0].Address, node.Address) - } - } - - services, err := r.ListServices() - if err != nil { - t.Fatal(err) - } - - for _, service := range testData { - var seen bool - for _, s := range services { - if s.Name == service.Name { - seen = true - break - } - } - if !seen { - t.Fatalf("Expected service %s got nothing", service.Name) - } - - // deregister - if err := r.Deregister(service); err != nil { - t.Fatal(err) - } - - time.Sleep(time.Millisecond * 5) - - // check its gone - s, _ := r.GetService(service.Name) - if len(s) > 0 { - t.Fatalf("Expected nothing got %+v", s[0]) - } - } - -} - -func TestEncoding(t *testing.T) { - testData := []*mdnsTxt{ - { - Version: "1.0.0", - Metadata: map[string]string{ - "foo": "bar", - }, - Endpoints: []*registry.Endpoint{ - { - Name: "endpoint1", - Request: ®istry.Value{ - Name: "request", - Type: "request", - }, - Response: ®istry.Value{ - Name: "response", - Type: "response", - }, - Metadata: map[string]string{ - "foo1": "bar1", - }, - }, - }, - }, - } - - for _, d := range testData { - encoded, err := encode(d) - if err != nil { - t.Fatal(err) - } - - for _, txt := range encoded { - if len(txt) > 255 { - t.Fatalf("One of parts for txt is %d characters", len(txt)) - } - } - - decoded, err := decode(encoded) - if err != nil { - t.Fatal(err) - } - - if decoded.Version != d.Version { - t.Fatalf("Expected version %s got %s", d.Version, decoded.Version) - } - - if len(decoded.Endpoints) != len(d.Endpoints) { - t.Fatalf("Expected %d endpoints, got %d", len(d.Endpoints), len(decoded.Endpoints)) - } - - for k, v := range d.Metadata { - if val := decoded.Metadata[k]; val != v { - t.Fatalf("Expected %s=%s got %s=%s", k, v, k, val) - } - } - } - -} - -func TestWatcher(t *testing.T) { - if travis := os.Getenv("TRAVIS"); travis == "true" { - t.Skip() - } - - testData := []*registry.Service{ - { - Name: "test1", - Version: "1.0.1", - Nodes: []*registry.Node{ - { - Id: "test1-1", - Address: "10.0.0.1:10001", - Metadata: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - { - Name: "test2", - Version: "1.0.2", - Nodes: []*registry.Node{ - { - Id: "test2-1", - Address: "10.0.0.2:10002", - Metadata: map[string]string{ - "foo2": "bar2", - }, - }, - }, - }, - { - Name: "test3", - Version: "1.0.3", - Nodes: []*registry.Node{ - { - Id: "test3-1", - Address: "10.0.0.3:10003", - Metadata: map[string]string{ - "foo3": "bar3", - }, - }, - }, - }, - } - - testFn := func(service, s *registry.Service) { - if s == nil { - t.Fatalf("Expected one result for %s got nil", service.Name) - - } - - if s.Name != service.Name { - t.Fatalf("Expected name %s got %s", service.Name, s.Name) - } - - if s.Version != service.Version { - t.Fatalf("Expected version %s got %s", service.Version, s.Version) - } - - if len(s.Nodes) != 1 { - t.Fatalf("Expected 1 node, got %d", len(s.Nodes)) - } - - node := s.Nodes[0] - - if node.Id != service.Nodes[0].Id { - t.Fatalf("Expected node id %s got %s", service.Nodes[0].Id, node.Id) - } - - if node.Address != service.Nodes[0].Address { - t.Fatalf("Expected node address %s got %s", service.Nodes[0].Address, node.Address) - } - } - - travis := os.Getenv("TRAVIS") - - var opts []registry.Option - - if travis == "true" { - opts = append(opts, registry.Timeout(time.Millisecond*100)) - } - - // new registry - r := NewRegistry(opts...) - - w, err := r.Watch() - if err != nil { - t.Fatal(err) - } - defer w.Stop() - - for _, service := range testData { - // register service - if err := r.Register(service); err != nil { - t.Fatal(err) - } - - for { - res, err := w.Next() - if err != nil { - t.Fatal(err) - } - - if res.Service.Name != service.Name { - continue - } - - if res.Action != "create" { - t.Fatalf("Expected create event got %s for %s", res.Action, res.Service.Name) - } - - testFn(service, res.Service) - break - } - - // deregister - if err := r.Deregister(service); err != nil { - t.Fatal(err) - } - - for { - res, err := w.Next() - if err != nil { - t.Fatal(err) - } - - if res.Service.Name != service.Name { - continue - } - - if res.Action != "delete" { - continue - } - - testFn(service, res.Service) - break - } - } -} diff --git a/registry/mdns/options.go b/registry/mdns/options.go deleted file mode 100644 index bc8e6f90..00000000 --- a/registry/mdns/options.go +++ /dev/null @@ -1,18 +0,0 @@ -// Package mdns provides a multicast dns registry -package mdns - -import ( - "context" - - "github.com/unistack-org/micro/v3/registry" -) - -// Domain sets the mdnsDomain -func Domain(d string) registry.Option { - return func(o *registry.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, "mdns.domain", d) - } -} diff --git a/registry/memory/memory.go b/registry/memory/memory.go deleted file mode 100644 index 86ecd0a2..00000000 --- a/registry/memory/memory.go +++ /dev/null @@ -1,470 +0,0 @@ -// Package memory provides an in-memory registry -package memory - -import ( - "context" - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/registry" -) - -var ( - sendEventTime = 10 * time.Millisecond - ttlPruneTime = time.Second -) - -type node struct { - *registry.Node - TTL time.Duration - LastSeen time.Time -} - -type record struct { - Name string - Version string - Metadata map[string]string - Nodes map[string]*node - Endpoints []*registry.Endpoint -} - -type Registry struct { - options registry.Options - - sync.RWMutex - // records is a KV map with domain name as the key and a services map as the value - records map[string]services - watchers map[string]*Watcher -} - -// services is a KV map with service name as the key and a map of records as the value -type services map[string]map[string]*record - -// NewRegistry returns an initialized in-memory registry -func NewRegistry(opts ...registry.Option) registry.Registry { - options := registry.Options{ - Context: context.Background(), - } - for _, o := range opts { - o(&options) - } - - // records can be passed for testing purposes - records := getServiceRecords(options.Context) - if records == nil { - records = make(services) - } - - reg := &Registry{ - options: options, - records: map[string]services{registry.DefaultDomain: records}, - watchers: make(map[string]*Watcher), - } - - go reg.ttlPrune() - - return reg -} - -func (m *Registry) ttlPrune() { - prune := time.NewTicker(ttlPruneTime) - defer prune.Stop() - - for { - select { - case <-prune.C: - m.Lock() - for domain, services := range m.records { - for service, versions := range services { - for version, record := range versions { - for id, n := range record.Nodes { - if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Registry TTL expired for node %s of service %s", n.Id, service) - } - delete(m.records[domain][service][version].Nodes, id) - } - } - } - } - } - m.Unlock() - } - } -} - -func (m *Registry) sendEvent(r *registry.Result) { - m.RLock() - watchers := make([]*Watcher, 0, len(m.watchers)) - for _, w := range m.watchers { - watchers = append(watchers, w) - } - m.RUnlock() - - for _, w := range watchers { - select { - case <-w.exit: - m.Lock() - delete(m.watchers, w.id) - m.Unlock() - default: - select { - case w.res <- r: - case <-time.After(sendEventTime): - } - } - } -} - -func (m *Registry) Init(opts ...registry.Option) error { - for _, o := range opts { - o(&m.options) - } - - // add services - m.Lock() - defer m.Unlock() - - // get the existing services from the records - srvs, ok := m.records[registry.DefaultDomain] - if !ok { - srvs = make(services) - } - - // loop through the services and if it doesn't yet exist, add it to the slice. This is used for - // testing purposes. - for name, record := range getServiceRecords(m.options.Context) { - if _, ok := srvs[name]; !ok { - srvs[name] = record - continue - } - - for version, r := range record { - if _, ok := srvs[name][version]; !ok { - srvs[name][version] = r - continue - } - } - } - - // set the services in the registry - m.records[registry.DefaultDomain] = srvs - return nil -} - -func (m *Registry) Options() registry.Options { - return m.options -} - -func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error { - m.Lock() - defer m.Unlock() - - // parse the options, fallback to the default domain - var options registry.RegisterOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = registry.DefaultDomain - } - - // get the services for this domain from the registry - srvs, ok := m.records[options.Domain] - if !ok { - srvs = make(services) - } - - // domain is set in metadata so it can be passed to watchers - if s.Metadata == nil { - s.Metadata = map[string]string{"domain": options.Domain} - } else { - s.Metadata["domain"] = options.Domain - } - - // ensure the service name exists - r := serviceToRecord(s, options.TTL) - if _, ok := srvs[s.Name]; !ok { - srvs[s.Name] = make(map[string]*record) - } - - if _, ok := srvs[s.Name][s.Version]; !ok { - srvs[s.Name][s.Version] = r - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Registry added new service: %s, version: %s", s.Name, s.Version) - } - m.records[options.Domain] = srvs - go m.sendEvent(®istry.Result{Action: "create", Service: s}) - } - - var addedNodes bool - - for _, n := range s.Nodes { - // check if already exists - if _, ok := srvs[s.Name][s.Version].Nodes[n.Id]; ok { - continue - } - - metadata := make(map[string]string) - - // make copy of metadata - for k, v := range n.Metadata { - metadata[k] = v - } - - // set the domain - metadata["domain"] = options.Domain - - // add the node - srvs[s.Name][s.Version].Nodes[n.Id] = &node{ - Node: ®istry.Node{ - Id: n.Id, - Address: n.Address, - Metadata: metadata, - }, - TTL: options.TTL, - LastSeen: time.Now(), - } - - addedNodes = true - } - - if addedNodes { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Registry added new node to service: %s, version: %s", s.Name, s.Version) - } - go m.sendEvent(®istry.Result{Action: "update", Service: s}) - } else { - // refresh TTL and timestamp - for _, n := range s.Nodes { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Updated registration for service: %s, version: %s", s.Name, s.Version) - } - srvs[s.Name][s.Version].Nodes[n.Id].TTL = options.TTL - srvs[s.Name][s.Version].Nodes[n.Id].LastSeen = time.Now() - } - } - - m.records[options.Domain] = srvs - return nil -} - -func (m *Registry) Deregister(s *registry.Service, opts ...registry.DeregisterOption) error { - m.Lock() - defer m.Unlock() - - // parse the options, fallback to the default domain - var options registry.DeregisterOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = registry.DefaultDomain - } - - // domain is set in metadata so it can be passed to watchers - if s.Metadata == nil { - s.Metadata = map[string]string{"domain": options.Domain} - } else { - s.Metadata["domain"] = options.Domain - } - - // if the domain doesn't exist, there is nothing to deregister - services, ok := m.records[options.Domain] - if !ok { - return nil - } - - // if no services with this name and version exist, there is nothing to deregister - versions, ok := services[s.Name] - if !ok { - return nil - } - - version, ok := versions[s.Version] - if !ok { - return nil - } - - // deregister all of the service nodes from this version - for _, n := range s.Nodes { - if _, ok := version.Nodes[n.Id]; ok { - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Registry removed node from service: %s, version: %s", s.Name, s.Version) - } - delete(version.Nodes, n.Id) - } - } - - // if the nodes not empty, we replace the version in the store and exist, the rest of the logic - // is cleanup - if len(version.Nodes) > 0 { - m.records[options.Domain][s.Name][s.Version] = version - go m.sendEvent(®istry.Result{Action: "update", Service: s}) - return nil - } - - // if this version was the only version of the service, we can remove the whole service from the - // registry and exit - if len(versions) == 1 { - delete(m.records[options.Domain], s.Name) - go m.sendEvent(®istry.Result{Action: "delete", Service: s}) - - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Registry removed service: %s", s.Name) - } - return nil - } - - // there are other versions of the service running, so only remove this version of it - delete(m.records[options.Domain][s.Name], s.Version) - go m.sendEvent(®istry.Result{Action: "delete", Service: s}) - if logger.V(logger.DebugLevel, logger.DefaultLogger) { - logger.Debugf("Registry removed service: %s, version: %s", s.Name, s.Version) - } - - return nil -} - -func (m *Registry) GetService(name string, opts ...registry.GetOption) ([]*registry.Service, error) { - // parse the options, fallback to the default domain - var options registry.GetOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = registry.DefaultDomain - } - - // if it's a wildcard domain, return from all domains - if options.Domain == registry.WildcardDomain { - m.RLock() - recs := m.records - m.RUnlock() - - var services []*registry.Service - - for domain := range recs { - srvs, err := m.GetService(name, append(opts, registry.GetDomain(domain))...) - if err == registry.ErrNotFound { - continue - } else if err != nil { - return nil, err - } - services = append(services, srvs...) - } - - if len(services) == 0 { - return nil, registry.ErrNotFound - } - return services, nil - } - - m.RLock() - defer m.RUnlock() - - // check the domain exists - services, ok := m.records[options.Domain] - if !ok { - return nil, registry.ErrNotFound - } - - // check the service exists - versions, ok := services[name] - if !ok || len(versions) == 0 { - return nil, registry.ErrNotFound - } - - // serialize the response - result := make([]*registry.Service, len(versions)) - - var i int - - for _, r := range versions { - result[i] = recordToService(r, options.Domain) - i++ - } - - return result, nil -} - -func (m *Registry) ListServices(opts ...registry.ListOption) ([]*registry.Service, error) { - // parse the options, fallback to the default domain - var options registry.ListOptions - for _, o := range opts { - o(&options) - } - if len(options.Domain) == 0 { - options.Domain = registry.DefaultDomain - } - - // if it's a wildcard domain, list from all domains - if options.Domain == registry.WildcardDomain { - m.RLock() - recs := m.records - m.RUnlock() - - var services []*registry.Service - - for domain := range recs { - srvs, err := m.ListServices(append(opts, registry.ListDomain(domain))...) - if err != nil { - return nil, err - } - services = append(services, srvs...) - } - - return services, nil - } - - m.RLock() - defer m.RUnlock() - - // ensure the domain exists - services, ok := m.records[options.Domain] - if !ok { - return make([]*registry.Service, 0), nil - } - - // serialize the result, each version counts as an individual service - var result []*registry.Service - - for domain, service := range services { - for _, version := range service { - result = append(result, recordToService(version, domain)) - } - } - - return result, nil -} - -func (m *Registry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) { - // parse the options, fallback to the default domain - var wo registry.WatchOptions - for _, o := range opts { - o(&wo) - } - if len(wo.Domain) == 0 { - wo.Domain = registry.DefaultDomain - } - - // construct the watcher - w := &Watcher{ - exit: make(chan bool), - res: make(chan *registry.Result), - id: uuid.New().String(), - wo: wo, - } - - m.Lock() - m.watchers[w.id] = w - m.Unlock() - - return w, nil -} - -func (m *Registry) String() string { - return "memory" -} diff --git a/registry/memory/memory_test.go b/registry/memory/memory_test.go deleted file mode 100644 index 77f27e47..00000000 --- a/registry/memory/memory_test.go +++ /dev/null @@ -1,282 +0,0 @@ -package memory - -import ( - "fmt" - "os" - "testing" - "time" - - "github.com/unistack-org/micro/v3/registry" -) - -var ( - testData = map[string][]*registry.Service{ - "foo": { - { - Name: "foo", - Version: "1.0.0", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.0-123", - Address: "localhost:9999", - }, - { - Id: "foo-1.0.0-321", - Address: "localhost:9999", - }, - }, - }, - { - Name: "foo", - Version: "1.0.1", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.1-321", - Address: "localhost:6666", - }, - }, - }, - { - Name: "foo", - Version: "1.0.3", - Nodes: []*registry.Node{ - { - Id: "foo-1.0.3-345", - Address: "localhost:8888", - }, - }, - }, - }, - "bar": { - { - Name: "bar", - Version: "default", - Nodes: []*registry.Node{ - { - Id: "bar-1.0.0-123", - Address: "localhost:9999", - }, - { - Id: "bar-1.0.0-321", - Address: "localhost:9999", - }, - }, - }, - { - Name: "bar", - Version: "latest", - Nodes: []*registry.Node{ - { - Id: "bar-1.0.1-321", - Address: "localhost:6666", - }, - }, - }, - }, - } -) - -func TestMemoryRegistry(t *testing.T) { - m := NewRegistry() - - fn := func(k string, v []*registry.Service) { - services, err := m.GetService(k) - if err != nil { - t.Errorf("Unexpected error getting service %s: %v", k, err) - } - - if len(services) != len(v) { - t.Errorf("Expected %d services for %s, got %d", len(v), k, len(services)) - } - - for _, service := range v { - var seen bool - for _, s := range services { - if s.Version == service.Version { - seen = true - break - } - } - if !seen { - t.Errorf("expected to find version %s", service.Version) - } - } - } - - // register data - for _, v := range testData { - serviceCount := 0 - for _, service := range v { - if err := m.Register(service); err != nil { - t.Errorf("Unexpected register error: %v", err) - } - serviceCount++ - // after the service has been registered we should be able to query it - services, err := m.GetService(service.Name) - if err != nil { - t.Errorf("Unexpected error getting service %s: %v", service.Name, err) - } - if len(services) != serviceCount { - t.Errorf("Expected %d services for %s, got %d", serviceCount, service.Name, len(services)) - } - } - } - - // using test data - for k, v := range testData { - fn(k, v) - } - - services, err := m.ListServices() - if err != nil { - t.Errorf("Unexpected error when listing services: %v", err) - } - - totalServiceCount := 0 - for _, testSvc := range testData { - for range testSvc { - totalServiceCount++ - } - } - - if len(services) != totalServiceCount { - t.Errorf("Expected total service count: %d, got: %d", totalServiceCount, len(services)) - } - - // deregister - for _, v := range testData { - for _, service := range v { - if err := m.Deregister(service); err != nil { - t.Errorf("Unexpected deregister error: %v", err) - } - } - } - - // after all the service nodes have been deregistered we should not get any results - for _, v := range testData { - for _, service := range v { - services, err := m.GetService(service.Name) - if err != registry.ErrNotFound { - t.Errorf("Expected error: %v, got: %v", registry.ErrNotFound, err) - } - if len(services) != 0 { - t.Errorf("Expected %d services for %s, got %d", 0, service.Name, len(services)) - } - } - } -} - -func TestMemoryRegistryTTL(t *testing.T) { - m := NewRegistry() - - for _, v := range testData { - for _, service := range v { - if err := m.Register(service, registry.RegisterTTL(time.Millisecond)); err != nil { - t.Fatal(err) - } - } - } - - time.Sleep(ttlPruneTime * 2) - - for name := range testData { - svcs, err := m.GetService(name) - if err != nil { - t.Fatal(err) - } - - for _, svc := range svcs { - if len(svc.Nodes) > 0 { - t.Fatalf("Service %q still has nodes registered", name) - } - } - } -} - -func TestMemoryRegistryTTLConcurrent(t *testing.T) { - concurrency := 1000 - waitTime := ttlPruneTime * 2 - m := NewRegistry() - - for _, v := range testData { - for _, service := range v { - if err := m.Register(service, registry.RegisterTTL(waitTime/2)); err != nil { - t.Fatal(err) - } - } - } - - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("test will wait %v, then check TTL timeouts", waitTime) - } - - errChan := make(chan error, concurrency) - syncChan := make(chan struct{}) - - for i := 0; i < concurrency; i++ { - go func() { - <-syncChan - for name := range testData { - svcs, err := m.GetService(name) - if err != nil { - errChan <- err - return - } - - for _, svc := range svcs { - if len(svc.Nodes) > 0 { - errChan <- fmt.Errorf("Service %q still has nodes registered", name) - return - } - } - } - - errChan <- nil - }() - } - - time.Sleep(waitTime) - close(syncChan) - - for i := 0; i < concurrency; i++ { - if err := <-errChan; err != nil { - t.Fatal(err) - } - } -} - -func TestMemoryWildcard(t *testing.T) { - m := NewRegistry() - testSrv := ®istry.Service{Name: "foo", Version: "1.0.0"} - - if err := m.Register(testSrv, registry.RegisterDomain("one")); err != nil { - t.Fatalf("Register err: %v", err) - } - if err := m.Register(testSrv, registry.RegisterDomain("two")); err != nil { - t.Fatalf("Register err: %v", err) - } - - if recs, err := m.ListServices(registry.ListDomain("one")); err != nil { - t.Errorf("List err: %v", err) - } else if len(recs) != 1 { - t.Errorf("Expected 1 record, got %v", len(recs)) - } - - if recs, err := m.ListServices(registry.ListDomain("*")); err != nil { - t.Errorf("List err: %v", err) - } else if len(recs) != 2 { - t.Errorf("Expected 2 records, got %v", len(recs)) - } - - if recs, err := m.GetService(testSrv.Name, registry.GetDomain("one")); err != nil { - t.Errorf("Get err: %v", err) - } else if len(recs) != 1 { - t.Errorf("Expected 1 record, got %v", len(recs)) - } - - if recs, err := m.GetService(testSrv.Name, registry.GetDomain("*")); err != nil { - t.Errorf("Get err: %v", err) - } else if len(recs) != 2 { - t.Errorf("Expected 2 records, got %v", len(recs)) - } -} diff --git a/registry/memory/options.go b/registry/memory/options.go deleted file mode 100644 index defb1e74..00000000 --- a/registry/memory/options.go +++ /dev/null @@ -1,40 +0,0 @@ -package memory - -import ( - "context" - - "github.com/unistack-org/micro/v3/registry" -) - -type servicesKey struct{} - -func getServiceRecords(ctx context.Context) map[string]map[string]*record { - memServices, ok := ctx.Value(servicesKey{}).(map[string][]*registry.Service) - if !ok { - return nil - } - - services := make(map[string]map[string]*record) - - for name, svc := range memServices { - if _, ok := services[name]; !ok { - services[name] = make(map[string]*record) - } - // go through every version of the service - for _, s := range svc { - services[s.Name][s.Version] = serviceToRecord(s, 0) - } - } - - return services -} - -// Services is an option that preloads service data -func Services(s map[string][]*registry.Service) registry.Option { - return func(o *registry.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, servicesKey{}, s) - } -} diff --git a/registry/memory/util.go b/registry/memory/util.go deleted file mode 100644 index 0ee33bb0..00000000 --- a/registry/memory/util.go +++ /dev/null @@ -1,94 +0,0 @@ -package memory - -import ( - "time" - - "github.com/unistack-org/micro/v3/registry" -) - -func serviceToRecord(s *registry.Service, ttl time.Duration) *record { - metadata := make(map[string]string, len(s.Metadata)) - for k, v := range s.Metadata { - metadata[k] = v - } - - nodes := make(map[string]*node, len(s.Nodes)) - for _, n := range s.Nodes { - nodes[n.Id] = &node{ - Node: n, - TTL: ttl, - LastSeen: time.Now(), - } - } - - endpoints := make([]*registry.Endpoint, len(s.Endpoints)) - for i, e := range s.Endpoints { - endpoints[i] = e - } - - return &record{ - Name: s.Name, - Version: s.Version, - Metadata: metadata, - Nodes: nodes, - Endpoints: endpoints, - } -} - -func recordToService(r *record, domain string) *registry.Service { - metadata := make(map[string]string, len(r.Metadata)) - for k, v := range r.Metadata { - metadata[k] = v - } - - // set the domain in metadata so it can be determined when a wildcard query is performed - metadata["domain"] = domain - - endpoints := make([]*registry.Endpoint, len(r.Endpoints)) - for i, e := range r.Endpoints { - request := new(registry.Value) - if e.Request != nil { - *request = *e.Request - } - response := new(registry.Value) - if e.Response != nil { - *response = *e.Response - } - - metadata := make(map[string]string, len(e.Metadata)) - for k, v := range e.Metadata { - metadata[k] = v - } - - endpoints[i] = ®istry.Endpoint{ - Name: e.Name, - Request: request, - Response: response, - Metadata: metadata, - } - } - - nodes := make([]*registry.Node, len(r.Nodes)) - i := 0 - for _, n := range r.Nodes { - metadata := make(map[string]string, len(n.Metadata)) - for k, v := range n.Metadata { - metadata[k] = v - } - - nodes[i] = ®istry.Node{ - Id: n.Id, - Address: n.Address, - Metadata: metadata, - } - i++ - } - - return ®istry.Service{ - Name: r.Name, - Version: r.Version, - Metadata: metadata, - Endpoints: endpoints, - Nodes: nodes, - } -} diff --git a/registry/memory/watcher.go b/registry/memory/watcher.go deleted file mode 100644 index 28202ba2..00000000 --- a/registry/memory/watcher.go +++ /dev/null @@ -1,53 +0,0 @@ -package memory - -import ( - "errors" - - "github.com/unistack-org/micro/v3/registry" -) - -type Watcher struct { - id string - wo registry.WatchOptions - res chan *registry.Result - exit chan bool -} - -func (m *Watcher) Next() (*registry.Result, error) { - for { - select { - case r := <-m.res: - if r.Service == nil { - continue - } - - if len(m.wo.Service) > 0 && m.wo.Service != r.Service.Name { - continue - } - - // extract domain from service metadata - var domain string - if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 { - domain = r.Service.Metadata["domain"] - } else { - domain = registry.DefaultDomain - } - - // only send the event if watching the wildcard or this specific domain - if m.wo.Domain == registry.WildcardDomain || m.wo.Domain == domain { - return r, nil - } - case <-m.exit: - return nil, errors.New("watcher stopped") - } - } -} - -func (m *Watcher) Stop() { - select { - case <-m.exit: - return - default: - close(m.exit) - } -} diff --git a/registry/memory/watcher_test.go b/registry/memory/watcher_test.go deleted file mode 100644 index d5120ebe..00000000 --- a/registry/memory/watcher_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package memory - -import ( - "testing" - - "github.com/unistack-org/micro/v3/registry" -) - -func TestWatcher(t *testing.T) { - w := &Watcher{ - id: "test", - res: make(chan *registry.Result), - exit: make(chan bool), - wo: registry.WatchOptions{ - Domain: registry.WildcardDomain, - }, - } - - go func() { - w.res <- ®istry.Result{ - Service: ®istry.Service{Name: "foo"}, - } - }() - - _, err := w.Next() - if err != nil { - t.Fatal("unexpected err", err) - } - - w.Stop() - - if _, err := w.Next(); err == nil { - t.Fatal("expected error on Next()") - } -} diff --git a/registry/noop/noop.go b/registry/noop/noop.go deleted file mode 100644 index 41279483..00000000 --- a/registry/noop/noop.go +++ /dev/null @@ -1,46 +0,0 @@ -// Package noop is a registry which does nothing -package noop - -import ( - "errors" - - "github.com/unistack-org/micro/v3/registry" -) - -type noopRegistry struct{} - -func (n *noopRegistry) Init(o ...registry.Option) error { - return nil -} - -func (n *noopRegistry) Options() registry.Options { - return registry.Options{} -} - -func (n *noopRegistry) Register(*registry.Service, ...registry.RegisterOption) error { - return nil -} - -func (n *noopRegistry) Deregister(*registry.Service, ...registry.DeregisterOption) error { - return nil -} - -func (n *noopRegistry) GetService(s string, o ...registry.GetOption) ([]*registry.Service, error) { - return []*registry.Service{}, nil -} - -func (n *noopRegistry) ListServices(...registry.ListOption) ([]*registry.Service, error) { - return []*registry.Service{}, nil -} -func (n *noopRegistry) Watch(...registry.WatchOption) (registry.Watcher, error) { - return nil, errors.New("not implemented") -} - -func (n *noopRegistry) String() string { - return "noop" -} - -// NewRegistry returns a new noop registry -func NewRegistry(opts ...registry.Option) registry.Registry { - return new(noopRegistry) -} diff --git a/resolver/registry/registry.go b/resolver/registry/registry.go index cf6ee08a..ce57ee70 100644 --- a/resolver/registry/registry.go +++ b/resolver/registry/registry.go @@ -3,7 +3,6 @@ package registry import ( "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/mdns" "github.com/unistack-org/micro/v3/resolver" ) @@ -15,12 +14,7 @@ type Resolver struct { // Resolve assumes ID is a domain name e.g micro.mu func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { - reg := r.Registry - if reg == nil { - reg = mdns.NewRegistry() - } - - services, err := reg.GetService(name) + services, err := r.Registry.GetService(name) if err != nil { return nil, err } diff --git a/router/options.go b/router/options.go index 15377c42..3b76bd78 100644 --- a/router/options.go +++ b/router/options.go @@ -5,7 +5,6 @@ import ( "github.com/google/uuid" "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/mdns" ) // Options are router options @@ -71,9 +70,8 @@ func Precache() Option { // DefaultOptions returns router default options func DefaultOptions() Options { return Options{ - Id: uuid.New().String(), - Network: DefaultNetwork, - Registry: mdns.NewRegistry(), - Context: context.Background(), + Id: uuid.New().String(), + Network: DefaultNetwork, + Context: context.Background(), } } diff --git a/router/registry/registry.go b/router/registry/registry.go index 5098babd..7f5d7378 100644 --- a/router/registry/registry.go +++ b/router/registry/registry.go @@ -30,7 +30,7 @@ type rtr struct { } // NewRouter creates new router and returns it -func NewRouter(opts ...router.Option) router.Router { +func NewRouter(opts ...router.Option) (router.Router, error) { // get default options options := router.DefaultOptions() @@ -45,13 +45,17 @@ func NewRouter(opts ...router.Option) router.Router { initChan: make(chan bool), } + if options.Registry == nil { + return nil, fmt.Errorf("registry not set") + } + // create the new table, passing the fetchRoute method in as a fallback if // the table doesn't contain the result for a query. r.table = newTable(r.lookup) // start the router r.start() - return r + return r, nil } // Init initializes router with given options diff --git a/router/registry/registry_test.go b/router/registry/registry_test.go index b74c83a6..01a729ad 100644 --- a/router/registry/registry_test.go +++ b/router/registry/registry_test.go @@ -1,3 +1,5 @@ +// +build ignore + package registry import ( @@ -19,7 +21,7 @@ func TestRouterClose(t *testing.T) { if err := r.Close(); err != nil { t.Errorf("failed to stop router: %v", err) } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { + if len(os.Getenv("INTEGRATION_TESTS")) == 0 { t.Logf("TestRouterStartStop STOPPED") } } diff --git a/router/registry/table_test.go b/router/registry/table_test.go index 2ca3368c..cdecc27b 100644 --- a/router/registry/table_test.go +++ b/router/registry/table_test.go @@ -1,3 +1,5 @@ +// +build ignore + package registry import ( @@ -7,8 +9,13 @@ import ( "github.com/unistack-org/micro/v3/router" ) -func testSetup() (*table, router.Route) { - routr := NewRouter().(*rtr) +func testSetup(t *testing.T) (*table, router.Route) { + r, err := NewRouter() + if err != nil { + t.Fatal(err) + } + routr := r.(*rtr) + table := newTable(routr.lookup) route := router.Route{ @@ -25,7 +32,7 @@ func testSetup() (*table, router.Route) { } func TestCreate(t *testing.T) { - table, route := testSetup() + table, route := testSetup(t) if err := table.Create(route); err != nil { t.Fatalf("error adding route: %s", err) @@ -45,7 +52,7 @@ func TestCreate(t *testing.T) { } func TestDelete(t *testing.T) { - table, route := testSetup() + table, route := testSetup(t) if err := table.Create(route); err != nil { t.Fatalf("error adding route: %s", err) @@ -68,7 +75,7 @@ func TestDelete(t *testing.T) { } func TestUpdate(t *testing.T) { - table, route := testSetup() + table, route := testSetup(t) if err := table.Create(route); err != nil { t.Fatalf("error adding route: %s", err) @@ -90,7 +97,7 @@ func TestUpdate(t *testing.T) { } func TestList(t *testing.T) { - table, route := testSetup() + table, route := testSetup(t) svc := []string{"one.svc", "two.svc", "three.svc"} @@ -112,7 +119,7 @@ func TestList(t *testing.T) { } func TestQuery(t *testing.T) { - table, route := testSetup() + table, route := testSetup(t) svc := []string{"svc1", "svc2", "svc3", "svc1"} net := []string{"net1", "net2", "net1", "net3"} diff --git a/server/grpc/README.md b/server/grpc/README.md deleted file mode 100644 index d53fc58d..00000000 --- a/server/grpc/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# GRPC Server - -The grpc server is a [micro.Server](https://godoc.org/github.com/micro/go-micro/server#Server) compatible server. - -## Overview - -The server makes use of the [google.golang.org/grpc](google.golang.org/grpc) framework for the underlying server -but continues to use micro handler signatures and protoc-gen-micro generated code. - -## Usage - -Specify the server to your micro service - -```go -import ( - "github.com/micro/go-micro" - "github.com/micro/go-plugins/server/grpc" -) - -func main() { - service := micro.NewService( - // This needs to be first as it replaces the underlying server - // which causes any configuration set before it - // to be discarded - micro.Server(grpc.NewServer()), - micro.Name("greeter"), - ) -} -``` -**NOTE**: Setting the gRPC server and/or client causes the underlying the server/client to be replaced which causes any previous configuration set on that server/client to be discarded. It is therefore recommended to set gRPC server/client before any other configuration \ No newline at end of file diff --git a/server/grpc/codec.go b/server/grpc/codec.go deleted file mode 100644 index 0bf13802..00000000 --- a/server/grpc/codec.go +++ /dev/null @@ -1,190 +0,0 @@ -package grpc - -import ( - "encoding/json" - "strings" - - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/codec/bytes" - "google.golang.org/grpc" - "google.golang.org/grpc/encoding" - "google.golang.org/grpc/metadata" - jsonpb "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" -) - -type jsonCodec struct{} -type bytesCodec struct{} -type protoCodec struct{} -type wrapCodec struct{ encoding.Codec } - -var jsonpbMarshaler = jsonpb.MarshalOptions{ - UseEnumNumbers: false, - EmitUnpopulated: false, - UseProtoNames: true, - AllowPartial: false, -} - -var jsonpbUnmarshaler = jsonpb.UnmarshalOptions{ - DiscardUnknown: false, - AllowPartial: false, -} - -var ( - defaultGRPCCodecs = map[string]encoding.Codec{ - "application/json": jsonCodec{}, - "application/proto": protoCodec{}, - "application/protobuf": protoCodec{}, - "application/octet-stream": protoCodec{}, - "application/grpc": protoCodec{}, - "application/grpc+json": jsonCodec{}, - "application/grpc+proto": protoCodec{}, - "application/grpc+bytes": bytesCodec{}, - } -) - -func (w wrapCodec) String() string { - return w.Codec.Name() -} - -func (w wrapCodec) Marshal(v interface{}) ([]byte, error) { - b, ok := v.(*bytes.Frame) - if ok { - return b.Data, nil - } - return w.Codec.Marshal(v) -} - -func (w wrapCodec) Unmarshal(data []byte, v interface{}) error { - b, ok := v.(*bytes.Frame) - if ok { - b.Data = data - return nil - } - if v == nil { - return nil - } - return w.Codec.Unmarshal(data, v) -} - -func (protoCodec) Marshal(v interface{}) ([]byte, error) { - m, ok := v.(proto.Message) - if !ok { - return nil, codec.ErrInvalidMessage - } - return proto.Marshal(m) -} - -func (protoCodec) Unmarshal(data []byte, v interface{}) error { - m, ok := v.(proto.Message) - if !ok { - return codec.ErrInvalidMessage - } - return proto.Unmarshal(data, m) -} - -func (protoCodec) Name() string { - return "proto" -} - -func (jsonCodec) Marshal(v interface{}) ([]byte, error) { - if pb, ok := v.(proto.Message); ok { - s, err := jsonpbMarshaler.Marshal(pb) - return s, err - } - - return json.Marshal(v) -} - -func (jsonCodec) Unmarshal(data []byte, v interface{}) error { - if len(data) == 0 { - return nil - } - if pb, ok := v.(proto.Message); ok { - return jsonpbUnmarshaler.Unmarshal(data, pb) - } - return json.Unmarshal(data, v) -} - -func (jsonCodec) Name() string { - return "json" -} - -func (bytesCodec) Marshal(v interface{}) ([]byte, error) { - b, ok := v.(*[]byte) - if !ok { - return nil, codec.ErrInvalidMessage - } - return *b, nil -} - -func (bytesCodec) Unmarshal(data []byte, v interface{}) error { - b, ok := v.(*[]byte) - if !ok { - return codec.ErrInvalidMessage - } - *b = data - return nil -} - -func (bytesCodec) Name() string { - return "bytes" -} - -type grpcCodec struct { - grpc.ServerStream - // headers - id string - target string - method string - endpoint string - - c encoding.Codec -} - -func (g *grpcCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error { - md, _ := metadata.FromIncomingContext(g.ServerStream.Context()) - if m == nil { - m = new(codec.Message) - } - if m.Header == nil { - m.Header = make(map[string]string, len(md)) - } - for k, v := range md { - m.Header[k] = strings.Join(v, ",") - } - m.Id = g.id - m.Target = g.target - m.Method = g.method - m.Endpoint = g.endpoint - return nil -} - -func (g *grpcCodec) ReadBody(v interface{}) error { - // caller has requested a frame - if f, ok := v.(*bytes.Frame); ok { - return g.ServerStream.RecvMsg(f) - } - return g.ServerStream.RecvMsg(v) -} - -func (g *grpcCodec) Write(m *codec.Message, v interface{}) error { - // if we don't have a body - if v != nil { - b, err := g.c.Marshal(v) - if err != nil { - return err - } - m.Body = b - } - // write the body using the framing codec - return g.ServerStream.SendMsg(&bytes.Frame{Data: m.Body}) -} - -func (g *grpcCodec) Close() error { - return nil -} - -func (g *grpcCodec) String() string { - return "grpc" -} diff --git a/server/grpc/context.go b/server/grpc/context.go deleted file mode 100644 index 6113d6d7..00000000 --- a/server/grpc/context.go +++ /dev/null @@ -1,16 +0,0 @@ -package grpc - -import ( - "context" - - "github.com/unistack-org/micro/v3/server" -) - -func setServerOption(k, v interface{}) server.Option { - return func(o *server.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/server/grpc/error.go b/server/grpc/error.go deleted file mode 100644 index 375f3fd9..00000000 --- a/server/grpc/error.go +++ /dev/null @@ -1,33 +0,0 @@ -package grpc - -import ( - "net/http" - - "github.com/unistack-org/micro/v3/errors" - "google.golang.org/grpc/codes" -) - -var errMapping = map[int32]codes.Code{ - http.StatusOK: codes.OK, - http.StatusBadRequest: codes.InvalidArgument, - http.StatusRequestTimeout: codes.DeadlineExceeded, - http.StatusNotFound: codes.NotFound, - http.StatusConflict: codes.AlreadyExists, - http.StatusForbidden: codes.PermissionDenied, - http.StatusUnauthorized: codes.Unauthenticated, - http.StatusPreconditionFailed: codes.FailedPrecondition, - http.StatusNotImplemented: codes.Unimplemented, - http.StatusInternalServerError: codes.Internal, - http.StatusServiceUnavailable: codes.Unavailable, -} - -func microError(err *errors.Error) codes.Code { - if err == nil { - return codes.OK - } - - if code, ok := errMapping[err.Code]; ok { - return code - } - return codes.Unknown -} diff --git a/server/grpc/extractor.go b/server/grpc/extractor.go deleted file mode 100644 index 6f1f8883..00000000 --- a/server/grpc/extractor.go +++ /dev/null @@ -1,122 +0,0 @@ -package grpc - -import ( - "fmt" - "reflect" - "strings" - - "github.com/unistack-org/micro/v3/registry" -) - -func extractValue(v reflect.Type, d int) *registry.Value { - if d == 3 { - return nil - } - if v == nil { - return nil - } - - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - - arg := ®istry.Value{ - Name: v.Name(), - Type: v.Name(), - } - - switch v.Kind() { - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - val := extractValue(f.Type, d+1) - if val == nil { - continue - } - - // if we can find a json tag use it - if tags := f.Tag.Get("json"); len(tags) > 0 { - parts := strings.Split(tags, ",") - if parts[0] == "-" || parts[0] == "omitempty" { - continue - } - val.Name = parts[0] - } - - // if there's no name default it - if len(val.Name) == 0 { - val.Name = v.Field(i).Name - } - - arg.Values = append(arg.Values, val) - } - case reflect.Slice: - p := v.Elem() - if p.Kind() == reflect.Ptr { - p = p.Elem() - } - arg.Type = "[]" + p.Name() - } - - return arg -} - -func extractEndpoint(method reflect.Method) *registry.Endpoint { - if method.PkgPath != "" { - return nil - } - - var rspType, reqType reflect.Type - var stream bool - mt := method.Type - - switch mt.NumIn() { - case 3: - reqType = mt.In(1) - rspType = mt.In(2) - case 4: - reqType = mt.In(2) - rspType = mt.In(3) - default: - return nil - } - - // are we dealing with a stream? - switch rspType.Kind() { - case reflect.Func, reflect.Interface: - stream = true - } - - request := extractValue(reqType, 0) - response := extractValue(rspType, 0) - - ep := ®istry.Endpoint{ - Name: method.Name, - Request: request, - Response: response, - Metadata: make(map[string]string), - } - - if stream { - ep.Metadata = map[string]string{ - "stream": fmt.Sprintf("%v", stream), - } - } - - return ep -} - -func extractSubValue(typ reflect.Type) *registry.Value { - var reqType reflect.Type - switch typ.NumIn() { - case 1: - reqType = typ.In(0) - case 2: - reqType = typ.In(1) - case 3: - reqType = typ.In(2) - default: - return nil - } - return extractValue(reqType, 0) -} diff --git a/server/grpc/extractor_test.go b/server/grpc/extractor_test.go deleted file mode 100644 index a41aeea8..00000000 --- a/server/grpc/extractor_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package grpc - -import ( - "context" - "reflect" - "testing" - - "github.com/unistack-org/micro/v3/registry" -) - -type testHandler struct{} - -type testRequest struct{} - -type testResponse struct{} - -func (t *testHandler) Test(ctx context.Context, req *testRequest, rsp *testResponse) error { - return nil -} - -func TestExtractEndpoint(t *testing.T) { - handler := &testHandler{} - typ := reflect.TypeOf(handler) - - var endpoints []*registry.Endpoint - - for m := 0; m < typ.NumMethod(); m++ { - if e := extractEndpoint(typ.Method(m)); e != nil { - endpoints = append(endpoints, e) - } - } - - if i := len(endpoints); i != 1 { - t.Errorf("Expected 1 endpoint, have %d", i) - } - - if endpoints[0].Name != "Test" { - t.Errorf("Expected handler Test, got %s", endpoints[0].Name) - } - - if endpoints[0].Request == nil { - t.Error("Expected non nil request") - } - - if endpoints[0].Response == nil { - t.Error("Expected non nil request") - } - - if endpoints[0].Request.Name != "testRequest" { - t.Errorf("Expected testRequest got %s", endpoints[0].Request.Name) - } - - if endpoints[0].Response.Name != "testResponse" { - t.Errorf("Expected testResponse got %s", endpoints[0].Response.Name) - } - - if endpoints[0].Request.Type != "testRequest" { - t.Errorf("Expected testRequest type got %s", endpoints[0].Request.Type) - } - - if endpoints[0].Response.Type != "testResponse" { - t.Errorf("Expected testResponse type got %s", endpoints[0].Response.Type) - } - -} diff --git a/server/grpc/grpc.go b/server/grpc/grpc.go deleted file mode 100644 index 828c0973..00000000 --- a/server/grpc/grpc.go +++ /dev/null @@ -1,1047 +0,0 @@ -// Package grpc provides a grpc server -package grpc - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "reflect" - "runtime/debug" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/errors" - pberr "github.com/unistack-org/micro/v3/errors/proto" - "github.com/unistack-org/micro/v3/logger" - meta "github.com/unistack-org/micro/v3/metadata" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" - "github.com/unistack-org/micro/v3/util/addr" - "github.com/unistack-org/micro/v3/util/backoff" - mgrpc "github.com/unistack-org/micro/v3/util/grpc" - mnet "github.com/unistack-org/micro/v3/util/net" - "golang.org/x/net/netutil" - - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/encoding" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/status" -) - -var ( - // DefaultMaxMsgSize define maximum message size that server can send - // or receive. Default value is 4MB. - DefaultMaxMsgSize = 1024 * 1024 * 4 -) - -const ( - defaultContentType = "application/grpc" -) - -type grpcServer struct { - rpc *rServer - srv *grpc.Server - exit chan chan error - wg *sync.WaitGroup - - sync.RWMutex - opts server.Options - handlers map[string]server.Handler - subscribers map[*subscriber][]broker.Subscriber - // marks the serve as started - started bool - // used for first registration - registered bool - - // registry service instance - rsvc *registry.Service - - codecs map[string]encoding.Codec -} - -func init() { - encoding.RegisterCodec(wrapCodec{jsonCodec{}}) - encoding.RegisterCodec(wrapCodec{protoCodec{}}) - encoding.RegisterCodec(wrapCodec{bytesCodec{}}) -} - -func newGRPCServer(opts ...server.Option) server.Server { - // create a grpc server - srv := &grpcServer{ - opts: server.Options{}, - rpc: &rServer{ - serviceMap: make(map[string]*service), - }, - handlers: make(map[string]server.Handler), - subscribers: make(map[*subscriber][]broker.Subscriber), - exit: make(chan chan error), - } - - // configure the grpc server - srv.configure(opts...) - - return srv -} - -type grpcRouter struct { - h func(context.Context, server.Request, interface{}) error - m func(context.Context, server.Message) error -} - -func (r grpcRouter) ProcessMessage(ctx context.Context, msg server.Message) error { - return r.m(ctx, msg) -} - -func (r grpcRouter) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { - return r.h(ctx, req, rsp) -} - -func (g *grpcServer) configure(opts ...server.Option) { - g.Lock() - defer g.Unlock() - - // Don't reprocess if server created - if g.srv != nil { - return - } - - for _, o := range opts { - o(&g.opts) - } - - g.wg = wait(g.opts.Context) - - g.codecs = make(map[string]encoding.Codec, len(defaultGRPCCodecs)) - for k, v := range defaultGRPCCodecs { - g.codecs[k] = v - } - - var codecs map[string]encoding.Codec - if g.opts.Context != nil { - if v, ok := g.opts.Context.Value(codecsKey{}).(map[string]encoding.Codec); ok && v != nil { - codecs = v - } - } - - for k, v := range codecs { - g.codecs[k] = v - } - - maxMsgSize := g.getMaxMsgSize() - - gopts := []grpc.ServerOption{ - grpc.MaxRecvMsgSize(maxMsgSize), - grpc.MaxSendMsgSize(maxMsgSize), - grpc.UnknownServiceHandler(g.handler), - } - - if creds := g.getCredentials(); creds != nil { - gopts = append(gopts, grpc.Creds(creds)) - } - - if opts := g.getGrpcOptions(); opts != nil { - gopts = append(gopts, opts...) - } - - g.rsvc = nil - g.srv = grpc.NewServer(gopts...) -} - -func (g *grpcServer) getMaxMsgSize() int { - if g.opts.Context == nil { - return DefaultMaxMsgSize - } - s, ok := g.opts.Context.Value(maxMsgSizeKey{}).(int) - if !ok { - return DefaultMaxMsgSize - } - return s -} - -func (g *grpcServer) getCredentials() credentials.TransportCredentials { - if g.opts.Context != nil { - if v, ok := g.opts.Context.Value(tlsAuth{}).(*tls.Config); ok && v != nil { - return credentials.NewTLS(v) - } - } - return nil -} - -func (g *grpcServer) getGrpcOptions() []grpc.ServerOption { - if g.opts.Context == nil { - return nil - } - - opts, ok := g.opts.Context.Value(grpcOptions{}).([]grpc.ServerOption) - if !ok || opts == nil { - return nil - } - - return opts -} - -func (g *grpcServer) getListener() net.Listener { - if g.opts.Context == nil { - return nil - } - - if l, ok := g.opts.Context.Value(netListener{}).(net.Listener); ok && l != nil { - return l - } - - return nil -} - -func (g *grpcServer) handler(srv interface{}, stream grpc.ServerStream) (err error) { - defer func() { - if r := recover(); r != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error("panic recovered: ", r) - logger.Error(string(debug.Stack())) - } - err = errors.InternalServerError(g.opts.Name, "panic recovered: %v", r) - } - }() - - if g.wg != nil { - g.wg.Add(1) - defer g.wg.Done() - } - - fullMethod, ok := grpc.MethodFromServerStream(stream) - if !ok { - return status.Errorf(codes.Internal, "method does not exist in context") - } - - serviceName, methodName, err := mgrpc.ServiceMethod(fullMethod) - if err != nil { - return status.New(codes.InvalidArgument, err.Error()).Err() - } - - // get grpc metadata - gmd, ok := metadata.FromIncomingContext(stream.Context()) - if !ok { - gmd = metadata.MD{} - } - - // copy the metadata to go-micro.metadata - md := meta.Metadata{} - for k, v := range gmd { - md[k] = strings.Join(v, ", ") - } - - // timeout for server deadline - to := md["timeout"] - - // get content type - ct := defaultContentType - - if ctype, ok := md["x-content-type"]; ok { - ct = ctype - } - if ctype, ok := md["content-type"]; ok { - ct = ctype - } - - delete(md, "x-content-type") - delete(md, "timeout") - - // create new context - ctx := meta.NewContext(stream.Context(), md) - - // get peer from context - if p, ok := peer.FromContext(stream.Context()); ok { - md["Remote"] = p.Addr.String() - ctx = peer.NewContext(ctx, p) - } - - // set the timeout if we have it - if len(to) > 0 { - if n, err := strconv.ParseUint(to, 10, 64); err == nil { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(n)) - defer cancel() - } - } - - // process via router - if g.opts.Router != nil { - cc, err := g.newGRPCCodec(ct) - if err != nil { - return errors.InternalServerError(g.opts.Name, err.Error()) - } - codec := &grpcCodec{ - ServerStream: stream, - method: fmt.Sprintf("%s.%s", serviceName, methodName), - endpoint: fmt.Sprintf("%s.%s", serviceName, methodName), - target: g.opts.Name, - c: cc, - } - - // create a client.Request - request := &rpcRequest{ - service: mgrpc.ServiceFromMethod(fullMethod), - contentType: ct, - method: fmt.Sprintf("%s.%s", serviceName, methodName), - codec: codec, - stream: true, - } - - response := &rpcResponse{ - header: make(map[string]string), - codec: codec, - } - - // create a wrapped function - handler := func(ctx context.Context, req server.Request, rsp interface{}) error { - return g.opts.Router.ServeRequest(ctx, req, rsp.(server.Response)) - } - - // execute the wrapper for it - for i := len(g.opts.HdlrWrappers); i > 0; i-- { - handler = g.opts.HdlrWrappers[i-1](handler) - } - - r := grpcRouter{h: handler} - - // serve the actual request using the request router - if err := r.ServeRequest(ctx, request, response); err != nil { - if _, ok := status.FromError(err); ok { - return err - } - return status.Errorf(codes.Internal, err.Error()) - } - - return nil - } - - // process the standard request flow - g.rpc.mu.Lock() - service := g.rpc.serviceMap[serviceName] - g.rpc.mu.Unlock() - - if service == nil { - return status.New(codes.Unimplemented, fmt.Sprintf("unknown service %s", serviceName)).Err() - } - - mtype := service.method[methodName] - if mtype == nil { - return status.New(codes.Unimplemented, fmt.Sprintf("unknown service %s.%s", serviceName, methodName)).Err() - } - - // process unary - if !mtype.stream { - return g.processRequest(stream, service, mtype, ct, ctx) - } - - // process stream - return g.processStream(stream, service, mtype, ct, ctx) -} - -func (g *grpcServer) processRequest(stream grpc.ServerStream, service *service, mtype *methodType, ct string, ctx context.Context) error { - for { - var argv, replyv reflect.Value - - // Decode the argument value. - argIsValue := false // if true, need to indirect before calling. - if mtype.ArgType.Kind() == reflect.Ptr { - argv = reflect.New(mtype.ArgType.Elem()) - } else { - argv = reflect.New(mtype.ArgType) - argIsValue = true - } - - // Unmarshal request - if err := stream.RecvMsg(argv.Interface()); err != nil { - return err - } - - if argIsValue { - argv = argv.Elem() - } - - // reply value - replyv = reflect.New(mtype.ReplyType.Elem()) - - function := mtype.method.Func - var returnValues []reflect.Value - - cc, err := g.newGRPCCodec(ct) - if err != nil { - return errors.InternalServerError(g.opts.Name, err.Error()) - } - b, err := cc.Marshal(argv.Interface()) - if err != nil { - return err - } - - // create a client.Request - r := &rpcRequest{ - service: g.opts.Name, - contentType: ct, - method: fmt.Sprintf("%s.%s", service.name, mtype.method.Name), - body: b, - payload: argv.Interface(), - } - - // define the handler func - fn := func(ctx context.Context, req server.Request, rsp interface{}) (err error) { - returnValues = function.Call([]reflect.Value{service.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)}) - - // The return value for the method is an error. - if rerr := returnValues[0].Interface(); rerr != nil { - err = rerr.(error) - } - - return err - } - - // wrap the handler func - for i := len(g.opts.HdlrWrappers); i > 0; i-- { - fn = g.opts.HdlrWrappers[i-1](fn) - } - - statusCode := codes.OK - statusDesc := "" - - // execute the handler - if appErr := fn(ctx, r, replyv.Interface()); appErr != nil { - var errStatus *status.Status - switch verr := appErr.(type) { - case *errors.Error: - perr := &pberr.Error{ - Id: verr.Id, - Code: verr.Code, - Detail: verr.Detail, - Status: verr.Status, - } - - // micro.Error now proto based and we can attach it to grpc status - statusCode = microError(verr) - statusDesc = verr.Error() - - errStatus, err = status.New(statusCode, statusDesc).WithDetails(perr) - if err != nil { - return err - } - case proto.Message: - // user defined error that proto based we can attach it to grpc status - statusCode = convertCode(appErr) - statusDesc = appErr.Error() - errStatus, err = status.New(statusCode, statusDesc).WithDetails(verr) - if err != nil { - return err - } - default: - // default case user pass own error type that not proto based - statusCode = convertCode(verr) - statusDesc = verr.Error() - errStatus = status.New(statusCode, statusDesc) - } - - return errStatus.Err() - } - - if err := stream.SendMsg(replyv.Interface()); err != nil { - return err - } - - return status.New(statusCode, statusDesc).Err() - } -} - -func (g *grpcServer) processStream(stream grpc.ServerStream, service *service, mtype *methodType, ct string, ctx context.Context) error { - opts := g.opts - - r := &rpcRequest{ - service: opts.Name, - contentType: ct, - method: fmt.Sprintf("%s.%s", service.name, mtype.method.Name), - stream: true, - } - - ss := &rpcStream{ - ServerStream: stream, - request: r, - } - - function := mtype.method.Func - var returnValues []reflect.Value - - // Invoke the method, providing a new value for the reply. - fn := func(ctx context.Context, req server.Request, stream interface{}) error { - returnValues = function.Call([]reflect.Value{service.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(stream)}) - if err := returnValues[0].Interface(); err != nil { - return err.(error) - } - - return nil - } - - for i := len(opts.HdlrWrappers); i > 0; i-- { - fn = opts.HdlrWrappers[i-1](fn) - } - - statusCode := codes.OK - statusDesc := "" - - if appErr := fn(ctx, r, ss); appErr != nil { - var err error - var errStatus *status.Status - switch verr := appErr.(type) { - case *errors.Error: - perr := &pberr.Error{ - Id: verr.Id, - Code: verr.Code, - Detail: verr.Detail, - Status: verr.Status, - } - // micro.Error now proto based and we can attach it to grpc status - statusCode = microError(verr) - statusDesc = verr.Error() - errStatus, err = status.New(statusCode, statusDesc).WithDetails(perr) - if err != nil { - return err - } - case proto.Message: - // user defined error that proto based we can attach it to grpc status - statusCode = convertCode(appErr) - statusDesc = appErr.Error() - errStatus, err = status.New(statusCode, statusDesc).WithDetails(verr) - if err != nil { - return err - } - default: - // default case user pass own error type that not proto based - statusCode = convertCode(verr) - statusDesc = verr.Error() - errStatus = status.New(statusCode, statusDesc) - } - - return errStatus.Err() - } - - return status.New(statusCode, statusDesc).Err() -} - -func (g *grpcServer) newGRPCCodec(contentType string) (encoding.Codec, error) { - g.RLock() - defer g.RUnlock() - - if c, ok := g.codecs[contentType]; ok { - return c, nil - } - - return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType) -} - -func (g *grpcServer) Options() server.Options { - g.RLock() - opts := g.opts - g.RUnlock() - - return opts -} - -func (g *grpcServer) Init(opts ...server.Option) error { - g.configure(opts...) - return nil -} - -func (g *grpcServer) NewHandler(h interface{}, opts ...server.HandlerOption) server.Handler { - return newRpcHandler(h, opts...) -} - -func (g *grpcServer) Handle(h server.Handler) error { - if err := g.rpc.register(h.Handler()); err != nil { - return err - } - - g.handlers[h.Name()] = h - return nil -} - -func (g *grpcServer) NewSubscriber(topic string, sb interface{}, opts ...server.SubscriberOption) server.Subscriber { - return newSubscriber(topic, sb, opts...) -} - -func (g *grpcServer) Subscribe(sb server.Subscriber) error { - sub, ok := sb.(*subscriber) - if !ok { - return fmt.Errorf("invalid subscriber: expected *subscriber") - } - if len(sub.handlers) == 0 { - return fmt.Errorf("invalid subscriber: no handler functions") - } - - if err := validateSubscriber(sb); err != nil { - return err - } - - g.Lock() - if _, ok = g.subscribers[sub]; ok { - g.Unlock() - return fmt.Errorf("subscriber %v already exists", sub) - } - - g.subscribers[sub] = nil - g.Unlock() - return nil -} - -func (g *grpcServer) Register() error { - g.RLock() - rsvc := g.rsvc - config := g.opts - g.RUnlock() - - // only register if it exists or is not noop - if config.Registry == nil || config.Registry.String() == "noop" { - return nil - } - - regFunc := func(service *registry.Service) error { - var regErr error - - for i := 0; i < 3; i++ { - // set the ttl and namespace - rOpts := []registry.RegisterOption{ - registry.RegisterTTL(config.RegisterTTL), - registry.RegisterDomain(g.opts.Namespace), - } - - // attempt to register - if err := config.Registry.Register(service, rOpts...); err != nil { - // set the error - regErr = err - // backoff then retry - time.Sleep(backoff.Do(i + 1)) - continue - } - // success so nil error - regErr = nil - break - } - - return regErr - } - - // if service already filled, reuse it and return early - if rsvc != nil { - if err := regFunc(rsvc); err != nil { - return err - } - return nil - } - - var err error - var advt, host, port string - var cacheService bool - - // check the advertise address first - // if it exists then use it, otherwise - // use the address - if len(config.Advertise) > 0 { - advt = config.Advertise - } else { - advt = config.Address - } - - if cnt := strings.Count(advt, ":"); cnt >= 1 { - // ipv6 address in format [host]:port or ipv4 host:port - host, port, err = net.SplitHostPort(advt) - if err != nil { - return err - } - } else { - host = advt - } - - if ip := net.ParseIP(host); ip != nil { - cacheService = true - } - - addr, err := addr.Extract(host) - if err != nil { - return err - } - - // make copy of metadata - md := meta.Copy(config.Metadata) - - // register service - node := ®istry.Node{ - Id: config.Name + "-" + config.Id, - Address: mnet.HostPort(addr, port), - Metadata: md, - } - - node.Metadata["broker"] = config.Broker.String() - node.Metadata["registry"] = config.Registry.String() - node.Metadata["server"] = g.String() - node.Metadata["transport"] = g.String() - node.Metadata["protocol"] = "grpc" - - g.RLock() - // Maps are ordered randomly, sort the keys for consistency - var handlerList []string - for n, e := range g.handlers { - // Only advertise non internal handlers - if !e.Options().Internal { - handlerList = append(handlerList, n) - } - } - sort.Strings(handlerList) - - var subscriberList []*subscriber - for e := range g.subscribers { - // Only advertise non internal subscribers - if !e.Options().Internal { - subscriberList = append(subscriberList, e) - } - } - sort.Slice(subscriberList, func(i, j int) bool { - return subscriberList[i].topic > subscriberList[j].topic - }) - - endpoints := make([]*registry.Endpoint, 0, len(handlerList)+len(subscriberList)) - for _, n := range handlerList { - endpoints = append(endpoints, g.handlers[n].Endpoints()...) - } - for _, e := range subscriberList { - endpoints = append(endpoints, e.Endpoints()...) - } - g.RUnlock() - - service := ®istry.Service{ - Name: config.Name, - Version: config.Version, - Nodes: []*registry.Node{node}, - Endpoints: endpoints, - } - - g.RLock() - registered := g.registered - g.RUnlock() - - if !registered { - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Registry [%s] Registering node: %s", config.Registry.String(), node.Id) - } - } - - // register the service - if err := regFunc(service); err != nil { - return err - } - - // already registered? don't need to register subscribers - if registered { - return nil - } - - g.Lock() - defer g.Unlock() - - for sb := range g.subscribers { - handler := g.createSubHandler(sb, g.opts) - var opts []broker.SubscribeOption - if queue := sb.Options().Queue; len(queue) > 0 { - opts = append(opts, broker.Queue(queue)) - } - - if cx := sb.Options().Context; cx != nil { - opts = append(opts, broker.SubscribeContext(cx)) - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Subscribing to topic: %s", sb.Topic()) - } - sub, err := config.Broker.Subscribe(sb.Topic(), handler, opts...) - if err != nil { - return err - } - g.subscribers[sb] = []broker.Subscriber{sub} - } - - g.registered = true - if cacheService { - g.rsvc = service - } - - return nil -} - -func (g *grpcServer) Deregister() error { - var err error - var advt, host, port string - - g.RLock() - config := g.opts - g.RUnlock() - - // only register if it exists or is not noop - if config.Registry == nil || config.Registry.String() == "noop" { - return nil - } - - // check the advertise address first - // if it exists then use it, otherwise - // use the address - if len(config.Advertise) > 0 { - advt = config.Advertise - } else { - advt = config.Address - } - - if cnt := strings.Count(advt, ":"); cnt >= 1 { - // ipv6 address in format [host]:port or ipv4 host:port - host, port, err = net.SplitHostPort(advt) - if err != nil { - return err - } - } else { - host = advt - } - - addr, err := addr.Extract(host) - if err != nil { - return err - } - - node := ®istry.Node{ - Id: config.Name + "-" + config.Id, - Address: mnet.HostPort(addr, port), - } - - service := ®istry.Service{ - Name: config.Name, - Version: config.Version, - Nodes: []*registry.Node{node}, - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Deregistering node: %s", node.Id) - } - - opt := registry.DeregisterDomain(g.opts.Namespace) - if err := config.Registry.Deregister(service, opt); err != nil { - return err - } - - g.Lock() - g.rsvc = nil - - if !g.registered { - g.Unlock() - return nil - } - - g.registered = false - - wg := sync.WaitGroup{} - for sb, subs := range g.subscribers { - for _, sub := range subs { - wg.Add(1) - go func(s broker.Subscriber) { - defer wg.Done() - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Unsubscribing from topic: %s", s.Topic()) - } - s.Unsubscribe() - }(sub) - } - g.subscribers[sb] = nil - } - wg.Wait() - - g.Unlock() - return nil -} - -func (g *grpcServer) Start() error { - g.RLock() - if g.started { - g.RUnlock() - return nil - } - g.RUnlock() - - config := g.Options() - - // micro: config.Transport.Listen(config.Address) - var ts net.Listener - - if l := g.getListener(); l != nil { - ts = l - } else { - var err error - - // check the tls config for secure connect - if tc := config.TLSConfig; tc != nil { - ts, err = tls.Listen("tcp", config.Address, tc) - // otherwise just plain tcp listener - } else { - ts, err = net.Listen("tcp", config.Address) - } - if err != nil { - return err - } - } - - if g.opts.Context != nil { - if c, ok := g.opts.Context.Value(maxConnKey{}).(int); ok && c > 0 { - ts = netutil.LimitListener(ts, c) - } - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Server [grpc] Listening on %s", ts.Addr().String()) - } - g.Lock() - g.opts.Address = ts.Addr().String() - g.Unlock() - - // only connect if we're subscribed - if len(g.subscribers) > 0 { - // connect to the broker - if err := config.Broker.Connect(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("Broker [%s] connect error: %v", config.Broker.String(), err) - } - return err - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Broker [%s] Connected to %s", config.Broker.String(), config.Broker.Address()) - } - } - - // announce self to the world - if err := g.Register(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("Server register error: %v", err) - } - } - - // micro: go ts.Accept(s.accept) - go func() { - if err := g.srv.Serve(ts); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("gRPC Server start error: %v", err) - } - } - }() - - go func() { - t := new(time.Ticker) - - // only process if it exists - if g.opts.RegisterInterval > time.Duration(0) { - // new ticker - t = time.NewTicker(g.opts.RegisterInterval) - } - - // return error chan - var ch chan error - - Loop: - for { - select { - // register self on interval - case <-t.C: - if err := g.Register(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error("Server register error: ", err) - } - } - // wait for exit - case ch = <-g.exit: - break Loop - } - } - - // deregister self - if err := g.Deregister(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error("Server deregister error: ", err) - } - } - - // wait for waitgroup - if g.wg != nil { - g.wg.Wait() - } - - // stop the grpc server - exit := make(chan bool) - - go func() { - g.srv.GracefulStop() - close(exit) - }() - - select { - case <-exit: - case <-time.After(time.Second): - g.srv.Stop() - } - - // close transport - ch <- nil - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - logger.Infof("Broker [%s] Disconnected from %s", config.Broker.String(), config.Broker.Address()) - } - // disconnect broker - if err := config.Broker.Disconnect(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("Broker [%s] disconnect error: %v", config.Broker.String(), err) - } - } - }() - - // mark the server as started - g.Lock() - g.started = true - g.Unlock() - - return nil -} - -func (g *grpcServer) Stop() error { - g.RLock() - if !g.started { - g.RUnlock() - return nil - } - g.RUnlock() - - ch := make(chan error) - g.exit <- ch - - err := <-ch - g.Lock() - g.rsvc = nil - g.started = false - g.Unlock() - - return err -} - -func (g *grpcServer) String() string { - return "grpc" -} - -func NewServer(opts ...server.Option) server.Server { - return newGRPCServer(opts...) -} diff --git a/server/grpc/grpc_test.go b/server/grpc/grpc_test.go deleted file mode 100644 index c4738073..00000000 --- a/server/grpc/grpc_test.go +++ /dev/null @@ -1,303 +0,0 @@ -package grpc_test - -import ( - "context" - "fmt" - "testing" - - bmemory "github.com/unistack-org/micro/v3/broker/memory" - "github.com/unistack-org/micro/v3/client" - gcli "github.com/unistack-org/micro/v3/client/grpc" - "github.com/unistack-org/micro/v3/errors" - pberr "github.com/unistack-org/micro/v3/errors/proto" - rmemory "github.com/unistack-org/micro/v3/registry/memory" - "github.com/unistack-org/micro/v3/router" - rtreg "github.com/unistack-org/micro/v3/router/registry" - "github.com/unistack-org/micro/v3/server" - gsrv "github.com/unistack-org/micro/v3/server/grpc" - pb "github.com/unistack-org/micro/v3/server/grpc/proto" - tgrpc "github.com/unistack-org/micro/v3/transport/grpc" - "google.golang.org/grpc" - "google.golang.org/grpc/status" -) - -// server is used to implement helloworld.GreeterServer. -type testServer struct { - msgCount int -} - -func (s *testServer) Handle(ctx context.Context, msg *pb.Request) error { - s.msgCount++ - return nil -} -func (s *testServer) HandleError(ctx context.Context, msg *pb.Request) error { - return fmt.Errorf("fake") -} - -// TestHello implements helloworld.GreeterServer -func (s *testServer) CallPcre(ctx context.Context, req *pb.Request, rsp *pb.Response) error { - if req.Name == "Error" { - return &errors.Error{Id: "1", Code: 99, Detail: "detail"} - } - - rsp.Msg = "Hello " + req.Name - return nil -} - -// TestHello implements helloworld.GreeterServer -func (s *testServer) CallPcreInvalid(ctx context.Context, req *pb.Request, rsp *pb.Response) error { - if req.Name == "Error" { - return &errors.Error{Id: "1", Code: 99, Detail: "detail"} - } - - rsp.Msg = "Hello " + req.Name - return nil -} - -// TestHello implements helloworld.GreeterServer -func (s *testServer) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error { - if req.Name == "Error" { - return &errors.Error{Id: "1", Code: 99, Detail: "detail"} - } - - if req.Name == "Panic" { - // make it panic - panic("handler panic") - } - - rsp.Msg = "Hello " + req.Name - return nil -} - -/* -func BenchmarkServer(b *testing.B) { - r := rmemory.NewRegistry() - br := bmemory.NewBroker() - tr := tgrpc.NewTransport() - s := gsrv.NewServer( - server.Broker(br), - server.Name("foo"), - server.Registry(r), - server.Transport(tr), - ) - c := gcli.NewClient( - client.Registry(r), - client.Broker(br), - client.Transport(tr), - ) - ctx := context.TODO() - - h := &testServer{} - pb.RegisterTestHandler(s, h) - if err := s.Start(); err != nil { - b.Fatalf("failed to start: %v", err) - } - - // check registration - services, err := r.GetService("foo") - if err != nil || len(services) == 0 { - b.Fatalf("failed to get service: %v # %d", err, len(services)) - } - - defer func() { - if err := s.Stop(); err != nil { - b.Fatalf("failed to stop: %v", err) - } - }() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - c.Call() - } - -} -*/ -func TestGRPCServer(t *testing.T) { - r := rmemory.NewRegistry() - b := bmemory.NewBroker() - tr := tgrpc.NewTransport() - rtr := rtreg.NewRouter(router.Registry(r)) - - s := gsrv.NewServer( - server.Broker(b), - server.Name("foo"), - server.Registry(r), - server.Transport(tr), - ) - - c := gcli.NewClient( - client.Router(rtr), - client.Broker(b), - client.Transport(tr), - ) - ctx := context.TODO() - - h := &testServer{} - pb.RegisterTestHandler(s, h) - - if err := s.Subscribe(s.NewSubscriber("test_topic", h.Handle)); err != nil { - t.Fatal(err) - } - - if err := s.Start(); err != nil { - t.Fatalf("failed to start: %v", err) - } - - // check registration - services, err := r.GetService("foo") - if err != nil || len(services) == 0 { - t.Fatalf("failed to get service: %v # %d", err, len(services)) - } - - defer func() { - if err := s.Stop(); err != nil { - t.Fatalf("failed to stop: %v", err) - } - }() - - cnt := 4 - for i := 0; i < cnt; i++ { - msg := c.NewMessage("test_topic", &pb.Request{Name: fmt.Sprintf("msg %d", i)}) - if err = c.Publish(ctx, msg); err != nil { - t.Fatal(err) - } - } - - if h.msgCount != cnt { - t.Fatalf("pub/sub not work, or invalid message count %d", h.msgCount) - } - - cc, err := grpc.Dial(s.Options().Address, grpc.WithInsecure()) - if err != nil { - t.Fatalf("failed to dial server: %v", err) - } - - testMethods := []string{"/test.Test/Call", "/go.micro.test.Test/Call"} - - for _, method := range testMethods { - rsp := pb.Response{} - - if err := cc.Invoke(context.Background(), method, &pb.Request{Name: "John"}, &rsp); err != nil { - t.Fatalf("error calling server: %v", err) - } - - if rsp.Msg != "Hello John" { - t.Fatalf("Got unexpected response %v", rsp.Msg) - } - } - - // Test grpc error - rsp := pb.Response{} - - if err := cc.Invoke(context.Background(), "/test.Test/Call", &pb.Request{Name: "Error"}, &rsp); err != nil { - st, ok := status.FromError(err) - if !ok { - t.Fatalf("invalid error received %#+v\n", err) - } - verr, ok := st.Details()[0].(*pberr.Error) - if !ok { - t.Fatalf("invalid error received %#+v\n", st.Details()[0]) - } - if verr.Code != 99 && verr.Id != "1" && verr.Detail != "detail" { - t.Fatalf("invalid error received %#+v\n", verr) - } - } -} - -// TestGRPCServerWithPanicWrapper test grpc server with panic wrapper -// gRPC server should not crash when wrapper crashed -func TestGRPCServerWithPanicWrapper(t *testing.T) { - r := rmemory.NewRegistry() - b := bmemory.NewBroker() - tr := tgrpc.NewTransport() - s := gsrv.NewServer( - server.Broker(b), - server.Name("foo"), - server.Registry(r), - server.Transport(tr), - server.WrapHandler(func(hf server.HandlerFunc) server.HandlerFunc { - return func(ctx context.Context, req server.Request, rsp interface{}) error { - // make it panic - panic("wrapper panic") - } - }), - ) - - h := &testServer{} - pb.RegisterTestHandler(s, h) - - if err := s.Start(); err != nil { - t.Fatalf("failed to start: %v", err) - } - - // check registration - services, err := r.GetService("foo") - if err != nil || len(services) == 0 { - t.Fatalf("failed to get service: %v # %d", err, len(services)) - } - - defer func() { - if err := s.Stop(); err != nil { - t.Fatalf("failed to stop: %v", err) - } - }() - - cc, err := grpc.Dial(s.Options().Address, grpc.WithInsecure()) - if err != nil { - t.Fatalf("failed to dial server: %v", err) - } - - rsp := pb.Response{} - if err := cc.Invoke(context.Background(), "/test.Test/Call", &pb.Request{Name: "John"}, &rsp); err == nil { - t.Fatal("this must return error, as wrapper should be panic") - } - - // both wrapper and handler should panic - rsp = pb.Response{} - if err := cc.Invoke(context.Background(), "/test.Test/Call", &pb.Request{Name: "Panic"}, &rsp); err == nil { - t.Fatal("this must return error, as wrapper and handler should be panic") - } -} - -// TestGRPCServerWithPanicWrapper test grpc server with panic handler -// gRPC server should not crash when handler crashed -func TestGRPCServerWithPanicHandler(t *testing.T) { - r := rmemory.NewRegistry() - b := bmemory.NewBroker() - tr := tgrpc.NewTransport() - s := gsrv.NewServer( - server.Broker(b), - server.Name("foo"), - server.Registry(r), - server.Transport(tr), - ) - - h := &testServer{} - pb.RegisterTestHandler(s, h) - - if err := s.Start(); err != nil { - t.Fatalf("failed to start: %v", err) - } - - // check registration - services, err := r.GetService("foo") - if err != nil || len(services) == 0 { - t.Fatalf("failed to get service: %v # %d", err, len(services)) - } - - defer func() { - if err := s.Stop(); err != nil { - t.Fatalf("failed to stop: %v", err) - } - }() - - cc, err := grpc.Dial(s.Options().Address, grpc.WithInsecure()) - if err != nil { - t.Fatalf("failed to dial server: %v", err) - } - - rsp := pb.Response{} - if err := cc.Invoke(context.Background(), "/test.Test/Call", &pb.Request{Name: "Panic"}, &rsp); err == nil { - t.Fatal("this must return error, as handler should be panic") - } -} diff --git a/server/grpc/handler.go b/server/grpc/handler.go deleted file mode 100644 index 59a5947f..00000000 --- a/server/grpc/handler.go +++ /dev/null @@ -1,66 +0,0 @@ -package grpc - -import ( - "reflect" - - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" -) - -type rpcHandler struct { - name string - handler interface{} - endpoints []*registry.Endpoint - opts server.HandlerOptions -} - -func newRpcHandler(handler interface{}, opts ...server.HandlerOption) server.Handler { - options := server.HandlerOptions{ - Metadata: make(map[string]map[string]string), - } - - for _, o := range opts { - o(&options) - } - - typ := reflect.TypeOf(handler) - hdlr := reflect.ValueOf(handler) - name := reflect.Indirect(hdlr).Type().Name() - - var endpoints []*registry.Endpoint - - for m := 0; m < typ.NumMethod(); m++ { - if e := extractEndpoint(typ.Method(m)); e != nil { - e.Name = name + "." + e.Name - - for k, v := range options.Metadata[e.Name] { - e.Metadata[k] = v - } - - endpoints = append(endpoints, e) - } - } - - return &rpcHandler{ - name: name, - handler: handler, - endpoints: endpoints, - opts: options, - } -} - -func (r *rpcHandler) Name() string { - return r.name -} - -func (r *rpcHandler) Handler() interface{} { - return r.handler -} - -func (r *rpcHandler) Endpoints() []*registry.Endpoint { - return r.endpoints -} - -func (r *rpcHandler) Options() server.HandlerOptions { - return r.opts -} diff --git a/server/grpc/options.go b/server/grpc/options.go deleted file mode 100644 index 7381ca59..00000000 --- a/server/grpc/options.go +++ /dev/null @@ -1,85 +0,0 @@ -package grpc - -import ( - "context" - "crypto/tls" - "net" - - "github.com/unistack-org/micro/v3/broker/http" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/registry/mdns" - "github.com/unistack-org/micro/v3/server" - "google.golang.org/grpc" - "google.golang.org/grpc/encoding" -) - -type codecsKey struct{} -type grpcOptions struct{} -type netListener struct{} -type maxMsgSizeKey struct{} -type maxConnKey struct{} -type tlsAuth struct{} - -// gRPC Codec to be used to encode/decode requests for a given content type -func Codec(contentType string, c encoding.Codec) server.Option { - return func(o *server.Options) { - codecs := make(map[string]encoding.Codec) - if o.Context == nil { - o.Context = context.Background() - } - if v, ok := o.Context.Value(codecsKey{}).(map[string]encoding.Codec); ok && v != nil { - codecs = v - } - codecs[contentType] = c - o.Context = context.WithValue(o.Context, codecsKey{}, codecs) - } -} - -// AuthTLS should be used to setup a secure authentication using TLS -func AuthTLS(t *tls.Config) server.Option { - return setServerOption(tlsAuth{}, t) -} - -// MaxConn specifies maximum number of max simultaneous connections to server -func MaxConn(n int) server.Option { - return setServerOption(maxConnKey{}, n) -} - -// Listener specifies the net.Listener to use instead of the default -func Listener(l net.Listener) server.Option { - return setServerOption(netListener{}, l) -} - -// Options to be used to configure gRPC options -func Options(opts ...grpc.ServerOption) server.Option { - return setServerOption(grpcOptions{}, opts) -} - -// -// MaxMsgSize set the maximum message in bytes the server can receive and -// send. Default maximum message size is 4 MB. -// -func MaxMsgSize(s int) server.Option { - return setServerOption(maxMsgSizeKey{}, s) -} - -func newOptions(opt ...server.Option) server.Options { - opts := server.Options{ - Codecs: make(map[string]codec.NewCodec), - Metadata: map[string]string{}, - Broker: http.NewBroker(), - Registry: mdns.NewRegistry(), - Address: server.DefaultAddress, - Name: server.DefaultName, - Id: server.DefaultId, - Version: server.DefaultVersion, - RegisterInterval: server.DefaultRegisterInterval, - RegisterTTL: server.DefaultRegisterTTL, - } - - for _, o := range opt { - o(&opts) - } - - return opts -} diff --git a/server/grpc/proto/test.pb.go b/server/grpc/proto/test.pb.go deleted file mode 100644 index c031174c..00000000 --- a/server/grpc/proto/test.pb.go +++ /dev/null @@ -1,240 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: server/grpc/proto/test.proto - -package test - -import ( - proto "github.com/golang/protobuf/proto" - _ "google.golang.org/genproto/googleapis/api/annotations" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Request struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *Request) Reset() { - *x = Request{} - if protoimpl.UnsafeEnabled { - mi := &file_server_grpc_proto_test_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Request) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Request) ProtoMessage() {} - -func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_server_grpc_proto_test_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 Request.ProtoReflect.Descriptor instead. -func (*Request) Descriptor() ([]byte, []int) { - return file_server_grpc_proto_test_proto_rawDescGZIP(), []int{0} -} - -func (x *Request) GetUuid() string { - if x != nil { - return x.Uuid - } - return "" -} - -func (x *Request) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type Response struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` -} - -func (x *Response) Reset() { - *x = Response{} - if protoimpl.UnsafeEnabled { - mi := &file_server_grpc_proto_test_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Response) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Response) ProtoMessage() {} - -func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_server_grpc_proto_test_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 Response.ProtoReflect.Descriptor instead. -func (*Response) Descriptor() ([]byte, []int) { - return file_server_grpc_proto_test_proto_rawDescGZIP(), []int{1} -} - -func (x *Response) GetMsg() string { - if x != nil { - return x.Msg - } - return "" -} - -var File_server_grpc_proto_test_proto protoreflect.FileDescriptor - -var file_server_grpc_proto_test_proto_rawDesc = []byte{ - 0x0a, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x31, 0x0a, 0x07, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x1c, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x32, 0xe6, 0x01, - 0x0a, 0x04, 0x54, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x08, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x63, 0x61, 0x6c, 0x6c, 0x2f, 0x7b, - 0x75, 0x75, 0x69, 0x64, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x46, 0x0a, 0x08, 0x43, 0x61, 0x6c, 0x6c, - 0x50, 0x63, 0x72, 0x65, 0x12, 0x08, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1f, 0x22, 0x1a, 0x5e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x74, 0x65, 0x73, 0x74, - 0x2f, 0x63, 0x61, 0x6c, 0x6c, 0x2f, 0x70, 0x63, 0x72, 0x65, 0x2f, 0x3f, 0x24, 0x3a, 0x01, 0x2a, - 0x12, 0x54, 0x0a, 0x0f, 0x43, 0x61, 0x6c, 0x6c, 0x50, 0x63, 0x72, 0x65, 0x49, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x12, 0x08, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, - 0x22, 0x21, 0x5e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, - 0x63, 0x61, 0x6c, 0x6c, 0x2f, 0x70, 0x63, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x2f, 0x3f, 0x3a, 0x01, 0x2a, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_server_grpc_proto_test_proto_rawDescOnce sync.Once - file_server_grpc_proto_test_proto_rawDescData = file_server_grpc_proto_test_proto_rawDesc -) - -func file_server_grpc_proto_test_proto_rawDescGZIP() []byte { - file_server_grpc_proto_test_proto_rawDescOnce.Do(func() { - file_server_grpc_proto_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_server_grpc_proto_test_proto_rawDescData) - }) - return file_server_grpc_proto_test_proto_rawDescData -} - -var file_server_grpc_proto_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_server_grpc_proto_test_proto_goTypes = []interface{}{ - (*Request)(nil), // 0: Request - (*Response)(nil), // 1: Response -} -var file_server_grpc_proto_test_proto_depIdxs = []int32{ - 0, // 0: Test.Call:input_type -> Request - 0, // 1: Test.CallPcre:input_type -> Request - 0, // 2: Test.CallPcreInvalid:input_type -> Request - 1, // 3: Test.Call:output_type -> Response - 1, // 4: Test.CallPcre:output_type -> Response - 1, // 5: Test.CallPcreInvalid:output_type -> Response - 3, // [3:6] is the sub-list for method output_type - 0, // [0:3] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_server_grpc_proto_test_proto_init() } -func file_server_grpc_proto_test_proto_init() { - if File_server_grpc_proto_test_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_server_grpc_proto_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_server_grpc_proto_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); 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_server_grpc_proto_test_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_server_grpc_proto_test_proto_goTypes, - DependencyIndexes: file_server_grpc_proto_test_proto_depIdxs, - MessageInfos: file_server_grpc_proto_test_proto_msgTypes, - }.Build() - File_server_grpc_proto_test_proto = out.File - file_server_grpc_proto_test_proto_rawDesc = nil - file_server_grpc_proto_test_proto_goTypes = nil - file_server_grpc_proto_test_proto_depIdxs = nil -} diff --git a/server/grpc/proto/test.pb.micro.go b/server/grpc/proto/test.pb.micro.go deleted file mode 100644 index 3e209227..00000000 --- a/server/grpc/proto/test.pb.micro.go +++ /dev/null @@ -1,171 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: server/grpc/proto/test.proto - -package test - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - _ "google.golang.org/genproto/googleapis/api/annotations" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Test service - -func NewTestEndpoints() []*api.Endpoint { - return []*api.Endpoint{ - &api.Endpoint{ - Name: "Test.Call", - Path: []string{"/api/v0/test/call/{uuid}"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - }, - &api.Endpoint{ - Name: "Test.CallPcre", - Path: []string{"^/api/v0/test/call/pcre/?$"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - }, - &api.Endpoint{ - Name: "Test.CallPcreInvalid", - Path: []string{"^/api/v0/test/call/pcre/invalid/?"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - }, - } -} - -// Client API for Test service - -type TestService interface { - Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) - CallPcre(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) - CallPcreInvalid(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) -} - -type testService struct { - c client.Client - name string -} - -func NewTestService(name string, c client.Client) TestService { - return &testService{ - c: c, - name: name, - } -} - -func (c *testService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { - req := c.c.NewRequest(c.name, "Test.Call", in) - out := new(Response) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *testService) CallPcre(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { - req := c.c.NewRequest(c.name, "Test.CallPcre", in) - out := new(Response) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *testService) CallPcreInvalid(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { - req := c.c.NewRequest(c.name, "Test.CallPcreInvalid", in) - out := new(Response) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Test service - -type TestHandler interface { - Call(context.Context, *Request, *Response) error - CallPcre(context.Context, *Request, *Response) error - CallPcreInvalid(context.Context, *Request, *Response) error -} - -func RegisterTestHandler(s server.Server, hdlr TestHandler, opts ...server.HandlerOption) error { - type test interface { - Call(ctx context.Context, in *Request, out *Response) error - CallPcre(ctx context.Context, in *Request, out *Response) error - CallPcreInvalid(ctx context.Context, in *Request, out *Response) error - } - type Test struct { - test - } - h := &testHandler{hdlr} - opts = append(opts, api.WithEndpoint(&api.Endpoint{ - Name: "Test.Call", - Path: []string{"/api/v0/test/call/{uuid}"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - })) - opts = append(opts, api.WithEndpoint(&api.Endpoint{ - Name: "Test.CallPcre", - Path: []string{"^/api/v0/test/call/pcre/?$"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - })) - opts = append(opts, api.WithEndpoint(&api.Endpoint{ - Name: "Test.CallPcreInvalid", - Path: []string{"^/api/v0/test/call/pcre/invalid/?"}, - Method: []string{"POST"}, - Body: "*", - Handler: "rpc", - })) - return s.Handle(s.NewHandler(&Test{h}, opts...)) -} - -type testHandler struct { - TestHandler -} - -func (h *testHandler) Call(ctx context.Context, in *Request, out *Response) error { - return h.TestHandler.Call(ctx, in, out) -} - -func (h *testHandler) CallPcre(ctx context.Context, in *Request, out *Response) error { - return h.TestHandler.CallPcre(ctx, in, out) -} - -func (h *testHandler) CallPcreInvalid(ctx context.Context, in *Request, out *Response) error { - return h.TestHandler.CallPcreInvalid(ctx, in, out) -} diff --git a/server/grpc/proto/test.proto b/server/grpc/proto/test.proto deleted file mode 100644 index 4d745ecc..00000000 --- a/server/grpc/proto/test.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; - -import "google/api/annotations.proto"; - -service Test { - rpc Call(Request) returns (Response) { - option (google.api.http) = { post: "/api/v0/test/call/{uuid}"; body:"*"; }; - }; - rpc CallPcre(Request) returns (Response) { - option (google.api.http) = { post: "^/api/v0/test/call/pcre/?$"; body:"*"; }; - }; - rpc CallPcreInvalid(Request) returns (Response) { - option (google.api.http) = { post: "^/api/v0/test/call/pcre/invalid/?"; body:"*"; }; - }; -} - -message Request { - string uuid = 1; - string name = 2; -} - -message Response { - string msg = 1; -} diff --git a/server/grpc/proto/test_grpc.pb.go b/server/grpc/proto/test_grpc.pb.go deleted file mode 100644 index daa1ce7e..00000000 --- a/server/grpc/proto/test_grpc.pb.go +++ /dev/null @@ -1,162 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package test - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// TestClient is the client API for Test service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type TestClient interface { - Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) - CallPcre(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) - CallPcreInvalid(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) -} - -type testClient struct { - cc grpc.ClientConnInterface -} - -func NewTestClient(cc grpc.ClientConnInterface) TestClient { - return &testClient{cc} -} - -func (c *testClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { - out := new(Response) - err := c.cc.Invoke(ctx, "/Test/Call", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *testClient) CallPcre(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { - out := new(Response) - err := c.cc.Invoke(ctx, "/Test/CallPcre", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *testClient) CallPcreInvalid(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { - out := new(Response) - err := c.cc.Invoke(ctx, "/Test/CallPcreInvalid", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// TestServer is the server API for Test service. -// All implementations must embed UnimplementedTestServer -// for forward compatibility -type TestServer interface { - Call(context.Context, *Request) (*Response, error) - CallPcre(context.Context, *Request) (*Response, error) - CallPcreInvalid(context.Context, *Request) (*Response, error) - mustEmbedUnimplementedTestServer() -} - -// UnimplementedTestServer must be embedded to have forward compatible implementations. -type UnimplementedTestServer struct { -} - -func (*UnimplementedTestServer) Call(context.Context, *Request) (*Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method Call not implemented") -} -func (*UnimplementedTestServer) CallPcre(context.Context, *Request) (*Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method CallPcre not implemented") -} -func (*UnimplementedTestServer) CallPcreInvalid(context.Context, *Request) (*Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method CallPcreInvalid not implemented") -} -func (*UnimplementedTestServer) mustEmbedUnimplementedTestServer() {} - -func RegisterTestServer(s *grpc.Server, srv TestServer) { - s.RegisterService(&_Test_serviceDesc, srv) -} - -func _Test_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TestServer).Call(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Test/Call", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TestServer).Call(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Test_CallPcre_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TestServer).CallPcre(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Test/CallPcre", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TestServer).CallPcre(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Test_CallPcreInvalid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TestServer).CallPcreInvalid(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Test/CallPcreInvalid", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TestServer).CallPcreInvalid(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -var _Test_serviceDesc = grpc.ServiceDesc{ - ServiceName: "Test", - HandlerType: (*TestServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Call", - Handler: _Test_Call_Handler, - }, - { - MethodName: "CallPcre", - Handler: _Test_CallPcre_Handler, - }, - { - MethodName: "CallPcreInvalid", - Handler: _Test_CallPcreInvalid_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "server/grpc/proto/test.proto", -} diff --git a/server/grpc/request.go b/server/grpc/request.go deleted file mode 100644 index 2e1506ec..00000000 --- a/server/grpc/request.go +++ /dev/null @@ -1,90 +0,0 @@ -package grpc - -import ( - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/codec/bytes" -) - -type rpcRequest struct { - service string - method string - contentType string - codec codec.Codec - header map[string]string - body []byte - stream bool - payload interface{} -} - -type rpcMessage struct { - topic string - contentType string - payload interface{} - header map[string]string - body []byte - codec codec.Codec -} - -func (r *rpcRequest) ContentType() string { - return r.contentType -} - -func (r *rpcRequest) Service() string { - return r.service -} - -func (r *rpcRequest) Method() string { - return r.method -} - -func (r *rpcRequest) Endpoint() string { - return r.method -} - -func (r *rpcRequest) Codec() codec.Reader { - return r.codec -} - -func (r *rpcRequest) Header() map[string]string { - return r.header -} - -func (r *rpcRequest) Read() ([]byte, error) { - f := &bytes.Frame{} - if err := r.codec.ReadBody(f); err != nil { - return nil, err - } - return f.Data, nil -} - -func (r *rpcRequest) Stream() bool { - return r.stream -} - -func (r *rpcRequest) Body() interface{} { - return r.payload -} - -func (r *rpcMessage) ContentType() string { - return r.contentType -} - -func (r *rpcMessage) Topic() string { - return r.topic -} - -func (r *rpcMessage) Payload() interface{} { - return r.payload -} - -func (r *rpcMessage) Header() map[string]string { - return r.header -} - -func (r *rpcMessage) Body() []byte { - return r.body -} - -func (r *rpcMessage) Codec() codec.Reader { - return r.codec -} diff --git a/server/grpc/response.go b/server/grpc/response.go deleted file mode 100644 index fff96a94..00000000 --- a/server/grpc/response.go +++ /dev/null @@ -1,27 +0,0 @@ -package grpc - -import ( - "github.com/unistack-org/micro/v3/codec" -) - -type rpcResponse struct { - header map[string]string - codec codec.Codec -} - -func (r *rpcResponse) Codec() codec.Writer { - return r.codec -} - -func (r *rpcResponse) WriteHeader(hdr map[string]string) { - for k, v := range hdr { - r.header[k] = v - } -} - -func (r *rpcResponse) Write(b []byte) error { - return r.codec.Write(&codec.Message{ - Header: r.header, - Body: b, - }, nil) -} diff --git a/server/grpc/server.go b/server/grpc/server.go deleted file mode 100644 index ccee0286..00000000 --- a/server/grpc/server.go +++ /dev/null @@ -1,198 +0,0 @@ -package grpc - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Meh, we need to get rid of this shit - -import ( - "context" - "errors" - "reflect" - "sync" - "unicode" - "unicode/utf8" - - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/server" -) - -var ( - // Precompute the reflect type for error. Can't use error directly - // because Typeof takes an empty interface value. This is annoying. - typeOfError = reflect.TypeOf((*error)(nil)).Elem() -) - -type methodType struct { - method reflect.Method - ArgType reflect.Type - ReplyType reflect.Type - ContextType reflect.Type - stream bool -} - -type service struct { - name string // name of service - rcvr reflect.Value // receiver of methods for the service - typ reflect.Type // type of the receiver - method map[string]*methodType // registered methods -} - -// server represents an RPC Server. -type rServer struct { - mu sync.Mutex // protects the serviceMap - serviceMap map[string]*service -} - -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// Is this type exported or a builtin? -func isExportedOrBuiltinType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - // PkgPath will be non-empty even for an exported type, - // so we need to check the type name as well. - return isExported(t.Name()) || t.PkgPath() == "" -} - -// prepareEndpoint() returns a methodType for the provided method or nil -// in case if the method was unsuitable. -func prepareEndpoint(method reflect.Method) *methodType { - mtype := method.Type - mname := method.Name - var replyType, argType, contextType reflect.Type - var stream bool - - // Endpoint() must be exported. - if method.PkgPath != "" { - return nil - } - - switch mtype.NumIn() { - case 3: - // assuming streaming - argType = mtype.In(2) - contextType = mtype.In(1) - stream = true - case 4: - // method that takes a context - argType = mtype.In(2) - replyType = mtype.In(3) - contextType = mtype.In(1) - default: - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn()) - } - return nil - } - - if stream { - // check stream type - streamType := reflect.TypeOf((*server.Stream)(nil)).Elem() - if !argType.Implements(streamType) { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("%v argument does not implement Streamer interface: %v", mname, argType) - } - return nil - } - } else { - // if not stream check the replyType - - // First arg need not be a pointer. - if !isExportedOrBuiltinType(argType) { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("%v argument type not exported: %v", mname, argType) - } - return nil - } - - if replyType.Kind() != reflect.Ptr { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("method %v reply type not a pointer: %v", mname, replyType) - } - return nil - } - - // Reply type must be exported. - if !isExportedOrBuiltinType(replyType) { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("method %v reply type not exported: %v", mname, replyType) - } - return nil - } - } - - // Endpoint() needs one out. - if mtype.NumOut() != 1 { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("method %v has wrong number of outs: %v", mname, mtype.NumOut()) - } - return nil - } - // The return type of the method must be error. - if returnType := mtype.Out(0); returnType != typeOfError { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Errorf("method %v returns %v not error", mname, returnType.String()) - } - return nil - } - return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream} -} - -func (server *rServer) register(rcvr interface{}) error { - server.mu.Lock() - defer server.mu.Unlock() - if server.serviceMap == nil { - server.serviceMap = make(map[string]*service) - } - s := new(service) - s.typ = reflect.TypeOf(rcvr) - s.rcvr = reflect.ValueOf(rcvr) - sname := reflect.Indirect(s.rcvr).Type().Name() - if sname == "" { - logger.Fatalf("rpc: no service name for type %v", s.typ.String()) - } - if !isExported(sname) { - s := "rpc Register: type " + sname + " is not exported" - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error(s) - } - return errors.New(s) - } - if _, present := server.serviceMap[sname]; present { - return errors.New("rpc: service already defined: " + sname) - } - s.name = sname - s.method = make(map[string]*methodType) - - // Install the methods - for m := 0; m < s.typ.NumMethod(); m++ { - method := s.typ.Method(m) - if mt := prepareEndpoint(method); mt != nil { - s.method[method.Name] = mt - } - } - - if len(s.method) == 0 { - s := "rpc Register: type " + sname + " has no exported methods of suitable type" - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error(s) - } - return errors.New(s) - } - server.serviceMap[s.name] = s - return nil -} - -func (m *methodType) prepareContext(ctx context.Context) reflect.Value { - if contextv := reflect.ValueOf(ctx); contextv.IsValid() { - return contextv - } - return reflect.Zero(m.ContextType) -} diff --git a/server/grpc/stream.go b/server/grpc/stream.go deleted file mode 100644 index ccebbd0a..00000000 --- a/server/grpc/stream.go +++ /dev/null @@ -1,40 +0,0 @@ -package grpc - -import ( - "context" - - "github.com/unistack-org/micro/v3/server" - "google.golang.org/grpc" -) - -// rpcStream implements a server side Stream. -type rpcStream struct { - // embed the grpc stream so we can access it - grpc.ServerStream - - request server.Request -} - -func (r *rpcStream) Close() error { - return nil -} - -func (r *rpcStream) Error() error { - return nil -} - -func (r *rpcStream) Request() server.Request { - return r.request -} - -func (r *rpcStream) Context() context.Context { - return r.ServerStream.Context() -} - -func (r *rpcStream) Send(m interface{}) error { - return r.ServerStream.SendMsg(m) -} - -func (r *rpcStream) Recv(m interface{}) error { - return r.ServerStream.RecvMsg(m) -} diff --git a/server/grpc/subscriber.go b/server/grpc/subscriber.go deleted file mode 100644 index db8b6930..00000000 --- a/server/grpc/subscriber.go +++ /dev/null @@ -1,293 +0,0 @@ -package grpc - -import ( - "context" - "fmt" - "reflect" - "runtime/debug" - "strings" - - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/metadata" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" -) - -const ( - subSig = "func(context.Context, interface{}) error" -) - -type handler struct { - method reflect.Value - reqType reflect.Type - ctxType reflect.Type -} - -type subscriber struct { - topic string - rcvr reflect.Value - typ reflect.Type - subscriber interface{} - handlers []*handler - endpoints []*registry.Endpoint - opts server.SubscriberOptions -} - -func newSubscriber(topic string, sub interface{}, opts ...server.SubscriberOption) server.Subscriber { - options := server.SubscriberOptions{ - AutoAck: true, - } - - for _, o := range opts { - o(&options) - } - - var endpoints []*registry.Endpoint - var handlers []*handler - - if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func { - h := &handler{ - method: reflect.ValueOf(sub), - } - - switch typ.NumIn() { - case 1: - h.reqType = typ.In(0) - case 2: - h.ctxType = typ.In(0) - h.reqType = typ.In(1) - } - - handlers = append(handlers, h) - - endpoints = append(endpoints, ®istry.Endpoint{ - Name: "Func", - Request: extractSubValue(typ), - Metadata: map[string]string{ - "topic": topic, - "subscriber": "true", - }, - }) - } else { - hdlr := reflect.ValueOf(sub) - name := reflect.Indirect(hdlr).Type().Name() - - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - h := &handler{ - method: method.Func, - } - - switch method.Type.NumIn() { - case 2: - h.reqType = method.Type.In(1) - case 3: - h.ctxType = method.Type.In(1) - h.reqType = method.Type.In(2) - } - - handlers = append(handlers, h) - - endpoints = append(endpoints, ®istry.Endpoint{ - Name: name + "." + method.Name, - Request: extractSubValue(method.Type), - Metadata: map[string]string{ - "topic": topic, - "subscriber": "true", - }, - }) - } - } - - return &subscriber{ - rcvr: reflect.ValueOf(sub), - typ: reflect.TypeOf(sub), - topic: topic, - subscriber: sub, - handlers: handlers, - endpoints: endpoints, - opts: options, - } -} - -func validateSubscriber(sub server.Subscriber) error { - typ := reflect.TypeOf(sub.Subscriber()) - var argType reflect.Type - - if typ.Kind() == reflect.Func { - name := "Func" - switch typ.NumIn() { - case 2: - argType = typ.In(1) - default: - return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig) - } - if !isExportedOrBuiltinType(argType) { - return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType) - } - if typ.NumOut() != 1 { - return fmt.Errorf("subscriber %v has wrong number of outs: %v require signature %s", - name, typ.NumOut(), subSig) - } - if returnType := typ.Out(0); returnType != typeOfError { - return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String()) - } - } else { - hdlr := reflect.ValueOf(sub.Subscriber()) - name := reflect.Indirect(hdlr).Type().Name() - - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - - switch method.Type.NumIn() { - case 3: - argType = method.Type.In(2) - default: - return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s", - name, method.Name, method.Type.NumIn(), subSig) - } - - if !isExportedOrBuiltinType(argType) { - return fmt.Errorf("%v argument type not exported: %v", name, argType) - } - if method.Type.NumOut() != 1 { - return fmt.Errorf( - "subscriber %v.%v has wrong number of outs: %v require signature %s", - name, method.Name, method.Type.NumOut(), subSig) - } - if returnType := method.Type.Out(0); returnType != typeOfError { - return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String()) - } - } - } - - return nil -} - -func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broker.Handler { - return func(msg *broker.Message) (err error) { - - defer func() { - if r := recover(); r != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error("panic recovered: ", r) - logger.Error(string(debug.Stack())) - } - err = errors.InternalServerError(g.opts.Name+".subscriber", "panic recovered: %v", r) - } - }() - - // if we don't have headers, create empty map - if msg.Header == nil { - msg.Header = make(map[string]string) - } - - ct := msg.Header["Content-Type"] - if len(ct) == 0 { - msg.Header["Content-Type"] = defaultContentType - ct = defaultContentType - } - cf, err := g.newGRPCCodec(ct) - if err != nil { - return err - } - - hdr := make(map[string]string, len(msg.Header)) - for k, v := range msg.Header { - hdr[k] = v - } - delete(hdr, "Content-Type") - ctx := metadata.NewContext(context.Background(), hdr) - - results := make(chan error, len(sb.handlers)) - - for i := 0; i < len(sb.handlers); i++ { - handler := sb.handlers[i] - - var isVal bool - var req reflect.Value - - if handler.reqType.Kind() == reflect.Ptr { - req = reflect.New(handler.reqType.Elem()) - } else { - req = reflect.New(handler.reqType) - isVal = true - } - if isVal { - req = req.Elem() - } - - if err = cf.Unmarshal(msg.Body, req.Interface()); err != nil { - return err - } - - fn := func(ctx context.Context, msg server.Message) error { - var vals []reflect.Value - if sb.typ.Kind() != reflect.Func { - vals = append(vals, sb.rcvr) - } - if handler.ctxType != nil { - vals = append(vals, reflect.ValueOf(ctx)) - } - - vals = append(vals, reflect.ValueOf(msg.Payload())) - - returnValues := handler.method.Call(vals) - if rerr := returnValues[0].Interface(); rerr != nil { - return rerr.(error) - } - return nil - } - - for i := len(opts.SubWrappers); i > 0; i-- { - fn = opts.SubWrappers[i-1](fn) - } - - if g.wg != nil { - g.wg.Add(1) - } - go func() { - if g.wg != nil { - defer g.wg.Done() - } - err := fn(ctx, &rpcMessage{ - topic: sb.topic, - contentType: ct, - payload: req.Interface(), - header: msg.Header, - body: msg.Body, - }) - results <- err - }() - } - var errors []string - for i := 0; i < len(sb.handlers); i++ { - if rerr := <-results; rerr != nil { - errors = append(errors, rerr.Error()) - } - } - if len(errors) > 0 { - err = fmt.Errorf("subscriber error: %s", strings.Join(errors, "\n")) - } - - return err - } -} - -func (s *subscriber) Topic() string { - return s.topic -} - -func (s *subscriber) Subscriber() interface{} { - return s.subscriber -} - -func (s *subscriber) Endpoints() []*registry.Endpoint { - return s.endpoints -} - -func (s *subscriber) Options() server.SubscriberOptions { - return s.opts -} diff --git a/server/grpc/util.go b/server/grpc/util.go deleted file mode 100644 index dfb467ab..00000000 --- a/server/grpc/util.go +++ /dev/null @@ -1,49 +0,0 @@ -package grpc - -import ( - "context" - "io" - "os" - "sync" - - "google.golang.org/grpc/codes" -) - -// convertCode converts a standard Go error into its canonical code. Note that -// this is only used to translate the error returned by the server applications. -func convertCode(err error) codes.Code { - switch err { - case nil: - return codes.OK - case io.EOF: - return codes.OutOfRange - case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF: - return codes.FailedPrecondition - case os.ErrInvalid: - return codes.InvalidArgument - case context.Canceled: - return codes.Canceled - case context.DeadlineExceeded: - return codes.DeadlineExceeded - } - switch { - case os.IsExist(err): - return codes.AlreadyExists - case os.IsNotExist(err): - return codes.NotFound - case os.IsPermission(err): - return codes.PermissionDenied - } - return codes.Unknown -} - -func wait(ctx context.Context) *sync.WaitGroup { - if ctx == nil { - return nil - } - wg, ok := ctx.Value("wait").(*sync.WaitGroup) - if !ok { - return nil - } - return wg -} diff --git a/server/mock/mock.go b/server/mock/mock.go deleted file mode 100644 index e2305d92..00000000 --- a/server/mock/mock.go +++ /dev/null @@ -1,139 +0,0 @@ -package mock - -import ( - "errors" - "sync" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/server" -) - -type MockServer struct { - sync.Mutex - Running bool - Opts server.Options - Handlers map[string]server.Handler - Subscribers map[string][]server.Subscriber -} - -var ( - _ server.Server = NewServer() -) - -func newMockServer(opts ...server.Option) *MockServer { - var options server.Options - - for _, o := range opts { - o(&options) - } - - return &MockServer{ - Opts: options, - Handlers: make(map[string]server.Handler), - Subscribers: make(map[string][]server.Subscriber), - } -} - -func (m *MockServer) Options() server.Options { - m.Lock() - defer m.Unlock() - - return m.Opts -} - -func (m *MockServer) Init(opts ...server.Option) error { - m.Lock() - defer m.Unlock() - - for _, o := range opts { - o(&m.Opts) - } - return nil -} - -func (m *MockServer) Handle(h server.Handler) error { - m.Lock() - defer m.Unlock() - - if _, ok := m.Handlers[h.Name()]; ok { - return errors.New("Handler " + h.Name() + " already exists") - } - m.Handlers[h.Name()] = h - return nil -} - -func (m *MockServer) NewHandler(h interface{}, opts ...server.HandlerOption) server.Handler { - var options server.HandlerOptions - for _, o := range opts { - o(&options) - } - - return &MockHandler{ - Id: uuid.New().String(), - Hdlr: h, - Opts: options, - } -} - -func (m *MockServer) NewSubscriber(topic string, fn interface{}, opts ...server.SubscriberOption) server.Subscriber { - var options server.SubscriberOptions - for _, o := range opts { - o(&options) - } - - return &MockSubscriber{ - Id: topic, - Sub: fn, - Opts: options, - } -} - -func (m *MockServer) Subscribe(sub server.Subscriber) error { - m.Lock() - defer m.Unlock() - - subs := m.Subscribers[sub.Topic()] - subs = append(subs, sub) - m.Subscribers[sub.Topic()] = subs - return nil -} - -func (m *MockServer) Register() error { - return nil -} - -func (m *MockServer) Deregister() error { - return nil -} - -func (m *MockServer) Start() error { - m.Lock() - defer m.Unlock() - - if m.Running { - return errors.New("already running") - } - - m.Running = true - return nil -} - -func (m *MockServer) Stop() error { - m.Lock() - defer m.Unlock() - - if !m.Running { - return errors.New("not running") - } - - m.Running = false - return nil -} - -func (m *MockServer) String() string { - return "mock" -} - -func NewServer(opts ...server.Option) *MockServer { - return newMockServer(opts...) -} diff --git a/server/mock/mock_handler.go b/server/mock/mock_handler.go deleted file mode 100644 index 4f1fda03..00000000 --- a/server/mock/mock_handler.go +++ /dev/null @@ -1,28 +0,0 @@ -package mock - -import ( - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" -) - -type MockHandler struct { - Id string - Opts server.HandlerOptions - Hdlr interface{} -} - -func (m *MockHandler) Name() string { - return m.Id -} - -func (m *MockHandler) Handler() interface{} { - return m.Hdlr -} - -func (m *MockHandler) Endpoints() []*registry.Endpoint { - return []*registry.Endpoint{} -} - -func (m *MockHandler) Options() server.HandlerOptions { - return m.Opts -} diff --git a/server/mock/mock_subscriber.go b/server/mock/mock_subscriber.go deleted file mode 100644 index 2db41b5a..00000000 --- a/server/mock/mock_subscriber.go +++ /dev/null @@ -1,28 +0,0 @@ -package mock - -import ( - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" -) - -type MockSubscriber struct { - Id string - Opts server.SubscriberOptions - Sub interface{} -} - -func (m *MockSubscriber) Topic() string { - return m.Id -} - -func (m *MockSubscriber) Subscriber() interface{} { - return m.Sub -} - -func (m *MockSubscriber) Endpoints() []*registry.Endpoint { - return []*registry.Endpoint{} -} - -func (m *MockSubscriber) Options() server.SubscriberOptions { - return m.Opts -} diff --git a/server/mock/mock_test.go b/server/mock/mock_test.go deleted file mode 100644 index 32d6d54a..00000000 --- a/server/mock/mock_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package mock - -import ( - "testing" - - "github.com/unistack-org/micro/v3/server" -) - -func TestMockServer(t *testing.T) { - srv := NewServer( - server.Name("mock"), - server.Version("latest"), - ) - - if srv.Options().Name != "mock" { - t.Fatalf("Expected name mock, got %s", srv.Options().Name) - } - - if srv.Options().Version != "latest" { - t.Fatalf("Expected version latest, got %s", srv.Options().Version) - } - - srv.Init(server.Version("test")) - if srv.Options().Version != "test" { - t.Fatalf("Expected version test, got %s", srv.Options().Version) - } - - h := srv.NewHandler(func() string { return "foo" }) - if err := srv.Handle(h); err != nil { - t.Fatal(err) - } - - sub := srv.NewSubscriber("test", func() string { return "foo" }) - if err := srv.Subscribe(sub); err != nil { - t.Fatal(err) - } - - if sub.Topic() != "test" { - t.Fatalf("Expected topic test got %s", sub.Topic()) - } - - if err := srv.Start(); err != nil { - t.Fatal(err) - } - - if err := srv.Register(); err != nil { - t.Fatal(err) - } - - if err := srv.Deregister(); err != nil { - t.Fatal(err) - } - - if err := srv.Stop(); err != nil { - t.Fatal(err) - } -} diff --git a/server/mucp/extractor.go b/server/mucp/extractor.go deleted file mode 100644 index 1630397d..00000000 --- a/server/mucp/extractor.go +++ /dev/null @@ -1,128 +0,0 @@ -package mucp - -import ( - "fmt" - "reflect" - "strings" - - "github.com/unistack-org/micro/v3/registry" -) - -func extractValue(v reflect.Type, d int) *registry.Value { - if d == 3 { - return nil - } - if v == nil { - return nil - } - - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - - arg := ®istry.Value{ - Name: v.Name(), - Type: v.Name(), - } - - switch v.Kind() { - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - val := extractValue(f.Type, d+1) - if val == nil { - continue - } - - // if we can find a json tag use it - if tags := f.Tag.Get("json"); len(tags) > 0 { - parts := strings.Split(tags, ",") - if parts[0] == "-" || parts[0] == "omitempty" { - continue - } - val.Name = parts[0] - } - - // if there's no name default it - if len(val.Name) == 0 { - val.Name = v.Field(i).Name - } - - // still no name then continue - if len(val.Name) == 0 { - continue - } - - arg.Values = append(arg.Values, val) - } - case reflect.Slice: - p := v.Elem() - if p.Kind() == reflect.Ptr { - p = p.Elem() - } - arg.Type = "[]" + p.Name() - } - - return arg -} - -func extractEndpoint(method reflect.Method) *registry.Endpoint { - if method.PkgPath != "" { - return nil - } - - var rspType, reqType reflect.Type - var stream bool - mt := method.Type - - switch mt.NumIn() { - case 3: - reqType = mt.In(1) - rspType = mt.In(2) - case 4: - reqType = mt.In(2) - rspType = mt.In(3) - default: - return nil - } - - // are we dealing with a stream? - switch rspType.Kind() { - case reflect.Func, reflect.Interface: - stream = true - } - - request := extractValue(reqType, 0) - response := extractValue(rspType, 0) - - ep := ®istry.Endpoint{ - Name: method.Name, - Request: request, - Response: response, - Metadata: make(map[string]string), - } - - // set endpoint metadata for stream - if stream { - ep.Metadata = map[string]string{ - "stream": fmt.Sprintf("%v", stream), - } - } - - return ep -} - -func extractSubValue(typ reflect.Type) *registry.Value { - var reqType reflect.Type - switch typ.NumIn() { - case 1: - reqType = typ.In(0) - case 2: - reqType = typ.In(1) - case 3: - reqType = typ.In(2) - default: - return nil - } - return extractValue(reqType, 0) -} diff --git a/server/mucp/extractor_test.go b/server/mucp/extractor_test.go deleted file mode 100644 index 478899e2..00000000 --- a/server/mucp/extractor_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package mucp - -import ( - "context" - "reflect" - "testing" - - "github.com/unistack-org/micro/v3/registry" -) - -type testHandler struct{} - -type testRequest struct{} - -type testResponse struct{} - -func (t *testHandler) Test(ctx context.Context, req *testRequest, rsp *testResponse) error { - return nil -} - -func TestExtractEndpoint(t *testing.T) { - handler := &testHandler{} - typ := reflect.TypeOf(handler) - - var endpoints []*registry.Endpoint - - for m := 0; m < typ.NumMethod(); m++ { - if e := extractEndpoint(typ.Method(m)); e != nil { - endpoints = append(endpoints, e) - } - } - - if i := len(endpoints); i != 1 { - t.Errorf("Expected 1 endpoint, have %d", i) - } - - if endpoints[0].Name != "Test" { - t.Errorf("Expected handler Test, got %s", endpoints[0].Name) - } - - if endpoints[0].Request == nil { - t.Error("Expected non nil request") - } - - if endpoints[0].Response == nil { - t.Error("Expected non nil request") - } - - if endpoints[0].Request.Name != "testRequest" { - t.Errorf("Expected testRequest got %s", endpoints[0].Request.Name) - } - - if endpoints[0].Response.Name != "testResponse" { - t.Errorf("Expected testResponse got %s", endpoints[0].Response.Name) - } - - if endpoints[0].Request.Type != "testRequest" { - t.Errorf("Expected testRequest type got %s", endpoints[0].Request.Type) - } - - if endpoints[0].Response.Type != "testResponse" { - t.Errorf("Expected testResponse type got %s", endpoints[0].Response.Type) - } - -} diff --git a/server/mucp/mucp.go b/server/mucp/mucp.go deleted file mode 100644 index d0dc5512..00000000 --- a/server/mucp/mucp.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package mucp provides an mucp server -package mucp - -import ( - "github.com/unistack-org/micro/v3/server" -) - -var ( - DefaultRouter = newRpcRouter() -) - -// NewServer returns a micro server interface -func NewServer(opts ...server.Option) server.Server { - return newServer(opts...) -} diff --git a/server/mucp/options.go b/server/mucp/options.go deleted file mode 100644 index 63a69778..00000000 --- a/server/mucp/options.go +++ /dev/null @@ -1,56 +0,0 @@ -package mucp - -import ( - "github.com/unistack-org/micro/v3/broker/http" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/registry/mdns" - "github.com/unistack-org/micro/v3/server" - thttp "github.com/unistack-org/micro/v3/transport/http" -) - -func newOptions(opt ...server.Option) server.Options { - opts := server.Options{ - Codecs: make(map[string]codec.NewCodec), - Metadata: map[string]string{}, - RegisterInterval: server.DefaultRegisterInterval, - RegisterTTL: server.DefaultRegisterTTL, - } - - for _, o := range opt { - o(&opts) - } - - if opts.Broker == nil { - opts.Broker = http.NewBroker() - } - - if opts.Registry == nil { - opts.Registry = mdns.NewRegistry() - } - - if opts.Transport == nil { - opts.Transport = thttp.NewTransport() - } - - if opts.RegisterCheck == nil { - opts.RegisterCheck = server.DefaultRegisterCheck - } - - if len(opts.Address) == 0 { - opts.Address = server.DefaultAddress - } - - if len(opts.Name) == 0 { - opts.Name = server.DefaultName - } - - if len(opts.Id) == 0 { - opts.Id = server.DefaultId - } - - if len(opts.Version) == 0 { - opts.Version = server.DefaultVersion - } - - return opts -} diff --git a/server/mucp/rpc_codec.go b/server/mucp/rpc_codec.go deleted file mode 100644 index fd8ae265..00000000 --- a/server/mucp/rpc_codec.go +++ /dev/null @@ -1,354 +0,0 @@ -package mucp - -import ( - "bytes" - "sync" - - "github.com/oxtoacart/bpool" - "github.com/pkg/errors" - "github.com/unistack-org/micro/v3/codec" - raw "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/codec/grpc" - "github.com/unistack-org/micro/v3/codec/json" - "github.com/unistack-org/micro/v3/codec/jsonrpc" - "github.com/unistack-org/micro/v3/codec/proto" - "github.com/unistack-org/micro/v3/codec/protorpc" - "github.com/unistack-org/micro/v3/transport" -) - -type rpcCodec struct { - socket transport.Socket - codec codec.Codec - protocol string - - req *transport.Message - buf *readWriteCloser - - // check if we're the first - sync.RWMutex - first chan bool -} - -type readWriteCloser struct { - sync.RWMutex - wbuf *bytes.Buffer - rbuf *bytes.Buffer -} - -var ( - DefaultContentType = "application/protobuf" - - DefaultCodecs = map[string]codec.NewCodec{ - "application/grpc": grpc.NewCodec, - "application/grpc+json": grpc.NewCodec, - "application/grpc+proto": grpc.NewCodec, - "application/json": json.NewCodec, - "application/json-rpc": jsonrpc.NewCodec, - "application/protobuf": proto.NewCodec, - "application/proto-rpc": protorpc.NewCodec, - "application/octet-stream": raw.NewCodec, - } - - // TODO: remove legacy codec list - defaultCodecs = map[string]codec.NewCodec{ - "application/json": jsonrpc.NewCodec, - "application/json-rpc": jsonrpc.NewCodec, - "application/protobuf": protorpc.NewCodec, - "application/proto-rpc": protorpc.NewCodec, - "application/octet-stream": protorpc.NewCodec, - } - - // the local buffer pool - bufferPool = bpool.NewSizedBufferPool(32, 1) -) - -func (rwc *readWriteCloser) Read(p []byte) (n int, err error) { - rwc.RLock() - defer rwc.RUnlock() - return rwc.rbuf.Read(p) -} - -func (rwc *readWriteCloser) Write(p []byte) (n int, err error) { - rwc.Lock() - defer rwc.Unlock() - return rwc.wbuf.Write(p) -} - -func (rwc *readWriteCloser) Close() error { - return nil -} - -func getHeader(hdr string, md map[string]string) string { - if hd := md[hdr]; len(hd) > 0 { - return hd - } - return md["X-"+hdr] -} - -func getHeaders(m *codec.Message) { - set := func(v, hdr string) string { - if len(v) > 0 { - return v - } - return m.Header[hdr] - } - - m.Id = set(m.Id, "Micro-Id") - m.Error = set(m.Error, "Micro-Error") - m.Endpoint = set(m.Endpoint, "Micro-Endpoint") - m.Method = set(m.Method, "Micro-Method") - m.Target = set(m.Target, "Micro-Service") - - // TODO: remove this cruft - if len(m.Endpoint) == 0 { - m.Endpoint = m.Method - } -} - -func setHeaders(m, r *codec.Message) { - set := func(hdr, v string) { - if len(v) == 0 { - return - } - m.Header[hdr] = v - m.Header["X-"+hdr] = v - } - - // set headers - set("Micro-Id", r.Id) - set("Micro-Service", r.Target) - set("Micro-Method", r.Method) - set("Micro-Endpoint", r.Endpoint) - set("Micro-Error", r.Error) -} - -// setupProtocol sets up the old protocol -func setupProtocol(msg *transport.Message) codec.NewCodec { - service := getHeader("Micro-Service", msg.Header) - method := getHeader("Micro-Method", msg.Header) - endpoint := getHeader("Micro-Endpoint", msg.Header) - protocol := getHeader("Micro-Protocol", msg.Header) - target := getHeader("Micro-Target", msg.Header) - topic := getHeader("Micro-Topic", msg.Header) - - // if the protocol exists (mucp) do nothing - if len(protocol) > 0 { - return nil - } - - // newer method of processing messages over transport - if len(topic) > 0 { - return nil - } - - // if no service/method/endpoint then it's the old protocol - if len(service) == 0 && len(method) == 0 && len(endpoint) == 0 { - return defaultCodecs[msg.Header["Content-Type"]] - } - - // old target method specified - if len(target) > 0 { - return defaultCodecs[msg.Header["Content-Type"]] - } - - // no method then set to endpoint - if len(method) == 0 { - msg.Header["Micro-Method"] = endpoint - } - - // no endpoint then set to method - if len(endpoint) == 0 { - msg.Header["Micro-Endpoint"] = method - } - - return nil -} - -func newRpcCodec(req *transport.Message, socket transport.Socket, c codec.NewCodec) codec.Codec { - rwc := &readWriteCloser{ - rbuf: bufferPool.Get(), - wbuf: bufferPool.Get(), - } - - r := &rpcCodec{ - buf: rwc, - codec: c(rwc), - req: req, - socket: socket, - protocol: "mucp", - first: make(chan bool), - } - - // if grpc pre-load the buffer - // TODO: remove this terrible hack - switch r.codec.String() { - case "grpc": - // write the body - rwc.rbuf.Write(req.Body) - // set the protocol - r.protocol = "grpc" - default: - // first is not preloaded - close(r.first) - } - - return r -} - -func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error { - // the initial message - m := codec.Message{ - Header: c.req.Header, - Body: c.req.Body, - } - - // first message could be pre-loaded - select { - case <-c.first: - // not the first - var tm transport.Message - - // read off the socket - if err := c.socket.Recv(&tm); err != nil { - return err - } - // reset the read buffer - c.buf.rbuf.Reset() - - // write the body to the buffer - if _, err := c.buf.rbuf.Write(tm.Body); err != nil { - return err - } - - // set the message header - m.Header = tm.Header - // set the message body - m.Body = tm.Body - - // set req - c.req = &tm - default: - // we need to lock here to prevent race conditions - // and we make use of a channel otherwise because - // this does not result in a context switch - // locking to check c.first on every call to ReadHeader - // would otherwise drastically slow the code execution - c.Lock() - // recheck before closing because the select statement - // above is not thread safe, so thread safety here is - // mandatory - select { - case <-c.first: - default: - // disable first - close(c.first) - } - // now unlock and we never need this again - c.Unlock() - } - - // set some internal things - getHeaders(&m) - - // read header via codec - if err := c.codec.ReadHeader(&m, codec.Request); err != nil { - return err - } - - // fallback for 0.14 and older - if len(m.Endpoint) == 0 { - m.Endpoint = m.Method - } - - // set message - *r = m - - return nil -} - -func (c *rpcCodec) ReadBody(b interface{}) error { - // don't read empty body - if len(c.req.Body) == 0 { - return nil - } - // read raw data - if v, ok := b.(*raw.Frame); ok { - v.Data = c.req.Body - return nil - } - // decode the usual way - return c.codec.ReadBody(b) -} - -func (c *rpcCodec) Write(r *codec.Message, b interface{}) error { - c.buf.wbuf.Reset() - - // create a new message - m := &codec.Message{ - Target: r.Target, - Method: r.Method, - Endpoint: r.Endpoint, - Id: r.Id, - Error: r.Error, - Type: r.Type, - Header: r.Header, - } - - if m.Header == nil { - m.Header = map[string]string{} - } - - setHeaders(m, r) - - // the body being sent - var body []byte - - // is it a raw frame? - if v, ok := b.(*raw.Frame); ok { - body = v.Data - // if we have encoded data just send it - } else if len(r.Body) > 0 { - body = r.Body - // write the body to codec - } else if err := c.codec.Write(m, b); err != nil { - c.buf.wbuf.Reset() - - // write an error if it failed - m.Error = errors.Wrapf(err, "Unable to encode body").Error() - m.Header["Micro-Error"] = m.Error - // no body to write - if err := c.codec.Write(m, nil); err != nil { - return err - } - } else { - // set the body - body = c.buf.wbuf.Bytes() - } - - // Set content type if theres content - if len(body) > 0 { - m.Header["Content-Type"] = c.req.Header["Content-Type"] - } - - // send on the socket - return c.socket.Send(&transport.Message{ - Header: m.Header, - Body: body, - }) -} - -func (c *rpcCodec) Close() error { - // close the codec - c.codec.Close() - // close the socket - err := c.socket.Close() - // put back the buffers - bufferPool.Put(c.buf.rbuf) - bufferPool.Put(c.buf.wbuf) - // return the error - return err -} - -func (c *rpcCodec) String() string { - return c.protocol -} diff --git a/server/mucp/rpc_codec_test.go b/server/mucp/rpc_codec_test.go deleted file mode 100644 index 1b5d19ef..00000000 --- a/server/mucp/rpc_codec_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package mucp - -import ( - "bytes" - "errors" - "testing" - - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/transport" -) - -// testCodec is a dummy codec that only knows how to encode nil bodies -type testCodec struct { - buf *bytes.Buffer -} - -type testSocket struct { - local string - remote string -} - -// TestCodecWriteError simulates what happens when a codec is unable -// to encode a message (e.g. a missing branch of an "oneof" message in -// protobufs) -// -// We expect an error to be sent to the socket. Previously the socket -// would remain open with no bytes sent, leading to client-side -// timeouts. -func TestCodecWriteError(t *testing.T) { - socket := testSocket{} - message := transport.Message{ - Header: map[string]string{}, - Body: []byte{}, - } - - rwc := readWriteCloser{ - rbuf: new(bytes.Buffer), - wbuf: new(bytes.Buffer), - } - - c := rpcCodec{ - buf: &rwc, - codec: &testCodec{ - buf: rwc.wbuf, - }, - req: &message, - socket: socket, - } - - err := c.Write(&codec.Message{ - Endpoint: "Service.Endpoint", - Id: "0", - Error: "", - }, "body") - - if err != nil { - t.Fatalf(`Expected Write to fail; got "%+v" instead`, err) - } - - const expectedError = "Unable to encode body: simulating a codec write failure" - actualError := rwc.wbuf.String() - if actualError != expectedError { - t.Fatalf(`Expected error "%+v" in the write buffer, got "%+v" instead`, expectedError, actualError) - } -} - -func (c *testCodec) ReadHeader(message *codec.Message, typ codec.MessageType) error { - return nil -} - -func (c *testCodec) ReadBody(dest interface{}) error { - return nil -} - -func (c *testCodec) Write(message *codec.Message, dest interface{}) error { - if dest != nil { - return errors.New("simulating a codec write failure") - } - c.buf.Write([]byte(message.Error)) - return nil -} - -func (c *testCodec) Close() error { - return nil -} - -func (c *testCodec) String() string { - return "string" -} - -func (s testSocket) Local() string { - return s.local -} - -func (s testSocket) Remote() string { - return s.remote -} - -func (s testSocket) Recv(message *transport.Message) error { - return nil -} - -func (s testSocket) Send(message *transport.Message) error { - return nil -} - -func (s testSocket) Close() error { - return nil -} diff --git a/server/mucp/rpc_handler.go b/server/mucp/rpc_handler.go deleted file mode 100644 index 57942d7e..00000000 --- a/server/mucp/rpc_handler.go +++ /dev/null @@ -1,66 +0,0 @@ -package mucp - -import ( - "reflect" - - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" -) - -type rpcHandler struct { - name string - handler interface{} - endpoints []*registry.Endpoint - opts server.HandlerOptions -} - -func newRpcHandler(handler interface{}, opts ...server.HandlerOption) server.Handler { - options := server.HandlerOptions{ - Metadata: make(map[string]map[string]string), - } - - for _, o := range opts { - o(&options) - } - - typ := reflect.TypeOf(handler) - hdlr := reflect.ValueOf(handler) - name := reflect.Indirect(hdlr).Type().Name() - - var endpoints []*registry.Endpoint - - for m := 0; m < typ.NumMethod(); m++ { - if e := extractEndpoint(typ.Method(m)); e != nil { - e.Name = name + "." + e.Name - - for k, v := range options.Metadata[e.Name] { - e.Metadata[k] = v - } - - endpoints = append(endpoints, e) - } - } - - return &rpcHandler{ - name: name, - handler: handler, - endpoints: endpoints, - opts: options, - } -} - -func (r *rpcHandler) Name() string { - return r.name -} - -func (r *rpcHandler) Handler() interface{} { - return r.handler -} - -func (r *rpcHandler) Endpoints() []*registry.Endpoint { - return r.endpoints -} - -func (r *rpcHandler) Options() server.HandlerOptions { - return r.opts -} diff --git a/server/mucp/rpc_request.go b/server/mucp/rpc_request.go deleted file mode 100644 index 0171ded2..00000000 --- a/server/mucp/rpc_request.go +++ /dev/null @@ -1,107 +0,0 @@ -package mucp - -import ( - "bytes" - - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/util/buf" -) - -type rpcRequest struct { - service string - method string - endpoint string - contentType string - socket transport.Socket - codec codec.Codec - header map[string]string - body []byte - rawBody interface{} - stream bool - first bool -} - -type rpcMessage struct { - topic string - contentType string - payload interface{} - header map[string]string - body []byte - codec codec.NewCodec -} - -func (r *rpcRequest) Codec() codec.Reader { - return r.codec -} - -func (r *rpcRequest) ContentType() string { - return r.contentType -} - -func (r *rpcRequest) Service() string { - return r.service -} - -func (r *rpcRequest) Method() string { - return r.method -} - -func (r *rpcRequest) Endpoint() string { - return r.endpoint -} - -func (r *rpcRequest) Header() map[string]string { - return r.header -} - -func (r *rpcRequest) Body() interface{} { - return r.rawBody -} - -func (r *rpcRequest) Read() ([]byte, error) { - // got a body - if r.first { - b := r.body - r.first = false - return b, nil - } - - var msg transport.Message - err := r.socket.Recv(&msg) - if err != nil { - return nil, err - } - r.header = msg.Header - - return msg.Body, nil -} - -func (r *rpcRequest) Stream() bool { - return r.stream -} - -func (r *rpcMessage) ContentType() string { - return r.contentType -} - -func (r *rpcMessage) Topic() string { - return r.topic -} - -func (r *rpcMessage) Payload() interface{} { - return r.payload -} - -func (r *rpcMessage) Header() map[string]string { - return r.header -} - -func (r *rpcMessage) Body() []byte { - return r.body -} - -func (r *rpcMessage) Codec() codec.Reader { - b := buf.New(bytes.NewBuffer(r.body)) - return r.codec(b) -} diff --git a/server/mucp/rpc_response.go b/server/mucp/rpc_response.go deleted file mode 100644 index 502498c2..00000000 --- a/server/mucp/rpc_response.go +++ /dev/null @@ -1,35 +0,0 @@ -package mucp - -import ( - "net/http" - - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/transport" -) - -type rpcResponse struct { - header map[string]string - socket transport.Socket - codec codec.Codec -} - -func (r *rpcResponse) Codec() codec.Writer { - return r.codec -} - -func (r *rpcResponse) WriteHeader(hdr map[string]string) { - for k, v := range hdr { - r.header[k] = v - } -} - -func (r *rpcResponse) Write(b []byte) error { - if _, ok := r.header["Content-Type"]; !ok { - r.header["Content-Type"] = http.DetectContentType(b) - } - - return r.socket.Send(&transport.Message{ - Header: r.header, - Body: b, - }) -} diff --git a/server/mucp/rpc_router.go b/server/mucp/rpc_router.go deleted file mode 100644 index 28bf120d..00000000 --- a/server/mucp/rpc_router.go +++ /dev/null @@ -1,618 +0,0 @@ -package mucp - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Meh, we need to get rid of this shit - -import ( - "context" - "errors" - "fmt" - "io" - "reflect" - "runtime/debug" - "strings" - "sync" - "unicode" - "unicode/utf8" - - "github.com/unistack-org/micro/v3/codec" - merrors "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/server" -) - -var ( - lastStreamResponseError = errors.New("EOS") - - // Precompute the reflect type for error. Can't use error directly - // because Typeof takes an empty interface value. This is annoying. - typeOfError = reflect.TypeOf((*error)(nil)).Elem() -) - -type methodType struct { - sync.Mutex // protects counters - method reflect.Method - ArgType reflect.Type - ReplyType reflect.Type - ContextType reflect.Type - stream bool -} - -type service struct { - name string // name of service - rcvr reflect.Value // receiver of methods for the service - typ reflect.Type // type of the receiver - method map[string]*methodType // registered methods -} - -type request struct { - msg *codec.Message - next *request // for free list in Server -} - -type response struct { - msg *codec.Message - next *response // for free list in Server -} - -// router represents an RPC router. -type router struct { - mu sync.Mutex // protects the serviceMap - serviceMap map[string]*service - - reqLock sync.Mutex // protects freeReq - freeReq *request - - respLock sync.Mutex // protects freeResp - freeResp *response - - // handler wrappers - hdlrWrappers []server.HandlerWrapper - // subscriber wrappers - subWrappers []server.SubscriberWrapper - - su sync.RWMutex - subscribers map[string][]*subscriber -} - -// rpcRouter encapsulates functions that become a server.Router -type rpcRouter struct { - h func(context.Context, server.Request, interface{}) error - m func(context.Context, server.Message) error -} - -func (r rpcRouter) ProcessMessage(ctx context.Context, msg server.Message) error { - return r.m(ctx, msg) -} - -func (r rpcRouter) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { - return r.h(ctx, req, rsp) -} - -func newRpcRouter() *router { - return &router{ - serviceMap: make(map[string]*service), - subscribers: make(map[string][]*subscriber), - } -} - -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// Is this type exported or a builtin? -func isExportedOrBuiltinType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - // PkgPath will be non-empty even for an exported type, - // so we need to check the type name as well. - return isExported(t.Name()) || t.PkgPath() == "" -} - -// prepareMethod returns a methodType for the provided method or nil -// in case if the method was unsuitable. -func prepareMethod(method reflect.Method) *methodType { - mtype := method.Type - mname := method.Name - var replyType, argType, contextType reflect.Type - var stream bool - - // Method must be exported. - if method.PkgPath != "" { - return nil - } - - switch mtype.NumIn() { - case 3: - // assuming streaming - argType = mtype.In(2) - contextType = mtype.In(1) - stream = true - case 4: - // method that takes a context - argType = mtype.In(2) - replyType = mtype.In(3) - contextType = mtype.In(1) - default: - log.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn()) - return nil - } - - if stream { - // check stream type - streamType := reflect.TypeOf((*server.Stream)(nil)).Elem() - if !argType.Implements(streamType) { - log.Errorf("%v argument does not implement Stream interface: %v", mname, argType) - return nil - } - } else { - // if not stream check the replyType - - // First arg need not be a pointer. - if !isExportedOrBuiltinType(argType) { - log.Errorf("%v argument type not exported: %v", mname, argType) - return nil - } - - if replyType.Kind() != reflect.Ptr { - log.Errorf("method %v reply type not a pointer: %v", mname, replyType) - return nil - } - - // Reply type must be exported. - if !isExportedOrBuiltinType(replyType) { - log.Errorf("method %v reply type not exported: %v", mname, replyType) - return nil - } - } - - // Method needs one out. - if mtype.NumOut() != 1 { - log.Errorf("method %v has wrong number of outs: %v", mname, mtype.NumOut()) - return nil - } - // The return type of the method must be error. - if returnType := mtype.Out(0); returnType != typeOfError { - log.Errorf("method %v returns %v not error", mname, returnType.String()) - return nil - } - return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream} -} - -func (router *router) sendResponse(sending sync.Locker, req *request, reply interface{}, cc codec.Writer, last bool) error { - msg := new(codec.Message) - msg.Type = codec.Response - resp := router.getResponse() - resp.msg = msg - - resp.msg.Id = req.msg.Id - sending.Lock() - err := cc.Write(resp.msg, reply) - sending.Unlock() - router.freeResponse(resp) - return err -} - -func (s *service) call(ctx context.Context, router *router, sending *sync.Mutex, mtype *methodType, req *request, argv, replyv reflect.Value, cc codec.Writer) error { - defer router.freeRequest(req) - - function := mtype.method.Func - var returnValues []reflect.Value - - r := &rpcRequest{ - service: req.msg.Target, - contentType: req.msg.Header["Content-Type"], - method: req.msg.Method, - endpoint: req.msg.Endpoint, - body: req.msg.Body, - header: req.msg.Header, - } - - // only set if not nil - if argv.IsValid() { - r.rawBody = argv.Interface() - } - - if !mtype.stream { - fn := func(ctx context.Context, req server.Request, rsp interface{}) error { - returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)}) - - // The return value for the method is an error. - if err := returnValues[0].Interface(); err != nil { - return err.(error) - } - - return nil - } - - // wrap the handler - for i := len(router.hdlrWrappers); i > 0; i-- { - fn = router.hdlrWrappers[i-1](fn) - } - - // execute handler - if err := fn(ctx, r, replyv.Interface()); err != nil { - return err - } - - // send response - return router.sendResponse(sending, req, replyv.Interface(), cc, true) - } - - // declare a local error to see if we errored out already - // keep track of the type, to make sure we return - // the same one consistently - rawStream := &rpcStream{ - context: ctx, - codec: cc.(codec.Codec), - request: r, - id: req.msg.Id, - } - - // Invoke the method, providing a new value for the reply. - fn := func(ctx context.Context, req server.Request, stream interface{}) error { - returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(stream)}) - if err := returnValues[0].Interface(); err != nil { - // the function returned an error, we use that - return err.(error) - } else if serr := rawStream.Error(); serr == io.EOF || serr == io.ErrUnexpectedEOF { - return nil - } else { - // no error, we send the special EOS error - return lastStreamResponseError - } - } - - // wrap the handler - for i := len(router.hdlrWrappers); i > 0; i-- { - fn = router.hdlrWrappers[i-1](fn) - } - - // client.Stream request - r.stream = true - - // execute handler - return fn(ctx, r, rawStream) -} - -func (m *methodType) prepareContext(ctx context.Context) reflect.Value { - if contextv := reflect.ValueOf(ctx); contextv.IsValid() { - return contextv - } - return reflect.Zero(m.ContextType) -} - -func (router *router) getRequest() *request { - router.reqLock.Lock() - req := router.freeReq - if req == nil { - req = new(request) - } else { - router.freeReq = req.next - *req = request{} - } - router.reqLock.Unlock() - return req -} - -func (router *router) freeRequest(req *request) { - router.reqLock.Lock() - req.next = router.freeReq - router.freeReq = req - router.reqLock.Unlock() -} - -func (router *router) getResponse() *response { - router.respLock.Lock() - resp := router.freeResp - if resp == nil { - resp = new(response) - } else { - router.freeResp = resp.next - *resp = response{} - } - router.respLock.Unlock() - return resp -} - -func (router *router) freeResponse(resp *response) { - router.respLock.Lock() - resp.next = router.freeResp - router.freeResp = resp - router.respLock.Unlock() -} - -func (router *router) readRequest(r server.Request) (service *service, mtype *methodType, req *request, argv, replyv reflect.Value, keepReading bool, err error) { - cc := r.Codec() - - service, mtype, req, keepReading, err = router.readHeader(cc) - if err != nil { - if !keepReading { - return - } - // discard body - cc.ReadBody(nil) - return - } - // is it a streaming request? then we don't read the body - if mtype.stream { - if cc.(codec.Codec).String() != "grpc" { - cc.ReadBody(nil) - } - return - } - - // Decode the argument value. - argIsValue := false // if true, need to indirect before calling. - if mtype.ArgType.Kind() == reflect.Ptr { - argv = reflect.New(mtype.ArgType.Elem()) - } else { - argv = reflect.New(mtype.ArgType) - argIsValue = true - } - // argv guaranteed to be a pointer now. - if err = cc.ReadBody(argv.Interface()); err != nil { - return - } - if argIsValue { - argv = argv.Elem() - } - - if !mtype.stream { - replyv = reflect.New(mtype.ReplyType.Elem()) - } - return -} - -func (router *router) readHeader(cc codec.Reader) (*service, *methodType, *request, bool, error) { - var err error - var service *service - var mtype *methodType - var req *request - - // Grab the request header. - msg := new(codec.Message) - msg.Type = codec.Request - req = router.getRequest() - req.msg = msg - - err = cc.ReadHeader(msg, msg.Type) - if err != nil { - req = nil - if err == io.EOF || err == io.ErrUnexpectedEOF { - return nil, nil, nil, false, err - } - return nil, nil, nil, false, fmt.Errorf("rpc: router cannot decode request: %v", err) - } - - // We read the header successfully. If we see an error now, - // we can still recover and move on to the next request. - keepReading := true - - serviceMethod := strings.Split(req.msg.Endpoint, ".") - if len(serviceMethod) != 2 { - return nil, nil, nil, keepReading, fmt.Errorf("rpc: service/endpoint request ill-formed: %v", req.msg.Endpoint) - } - // Look up the request. - router.mu.Lock() - service = router.serviceMap[serviceMethod[0]] - router.mu.Unlock() - if service == nil { - return nil, nil, nil, keepReading, fmt.Errorf("rpc: can't find service %v", serviceMethod[0]) - } - mtype = service.method[serviceMethod[1]] - if mtype == nil { - return nil, nil, nil, keepReading, fmt.Errorf("rpc: can't find method %v", serviceMethod[1]) - } - - return service, mtype, req, keepReading, nil -} - -func (router *router) NewHandler(h interface{}, opts ...server.HandlerOption) server.Handler { - return newRpcHandler(h, opts...) -} - -func (router *router) Handle(h server.Handler) error { - router.mu.Lock() - defer router.mu.Unlock() - if router.serviceMap == nil { - router.serviceMap = make(map[string]*service) - } - - if len(h.Name()) == 0 { - return errors.New("rpc.Handle: handler has no name") - } - if !isExported(h.Name()) { - return errors.New("rpc.Handle: type " + h.Name() + " is not exported") - } - - rcvr := h.Handler() - s := new(service) - s.typ = reflect.TypeOf(rcvr) - s.rcvr = reflect.ValueOf(rcvr) - - // check name - if _, present := router.serviceMap[h.Name()]; present { - return errors.New("rpc.Handle: service already defined: " + h.Name()) - } - - s.name = h.Name() - s.method = make(map[string]*methodType) - - // Install the methods - for m := 0; m < s.typ.NumMethod(); m++ { - method := s.typ.Method(m) - if mt := prepareMethod(method); mt != nil { - s.method[method.Name] = mt - } - } - - // Check there are methods - if len(s.method) == 0 { - return errors.New("rpc Register: type " + s.name + " has no exported methods of suitable type") - } - - // save handler - router.serviceMap[s.name] = s - return nil -} - -func (router *router) ServeRequest(ctx context.Context, r server.Request, rsp server.Response) error { - sending := new(sync.Mutex) - service, mtype, req, argv, replyv, keepReading, err := router.readRequest(r) - if err != nil { - if !keepReading { - return err - } - // send a response if we actually managed to read a header. - if req != nil { - router.freeRequest(req) - } - return err - } - return service.call(ctx, router, sending, mtype, req, argv, replyv, rsp.Codec()) -} - -func (router *router) NewSubscriber(topic string, handler interface{}, opts ...server.SubscriberOption) server.Subscriber { - return newSubscriber(topic, handler, opts...) -} - -func (router *router) Subscribe(s server.Subscriber) error { - sub, ok := s.(*subscriber) - if !ok { - return fmt.Errorf("invalid subscriber: expected *subscriber") - } - if len(sub.handlers) == 0 { - return fmt.Errorf("invalid subscriber: no handler functions") - } - - if err := validateSubscriber(sub); err != nil { - return err - } - - router.su.Lock() - defer router.su.Unlock() - - // append to subscribers - subs := router.subscribers[sub.Topic()] - subs = append(subs, sub) - router.subscribers[sub.Topic()] = subs - - return nil -} - -func (router *router) String() string { - return "mucp" -} - -func (router *router) ProcessMessage(ctx context.Context, msg server.Message) (err error) { - defer func() { - // recover any panics - if r := recover(); r != nil { - log.Errorf("panic recovered: %v", r) - log.Error(string(debug.Stack())) - err = merrors.InternalServerError("go.micro.server", "panic recovered: %v", r) - } - }() - - router.su.RLock() - // get the subscribers by topic - subs, ok := router.subscribers[msg.Topic()] - // unlock since we only need to get the subs - router.su.RUnlock() - if !ok { - return nil - } - - var errResults []string - - // we may have multiple subscribers for the topic - for _, sub := range subs { - // we may have multiple handlers per subscriber - for i := 0; i < len(sub.handlers); i++ { - // get the handler - handler := sub.handlers[i] - - var isVal bool - var req reflect.Value - - // check whether the handler is a pointer - if handler.reqType.Kind() == reflect.Ptr { - req = reflect.New(handler.reqType.Elem()) - } else { - req = reflect.New(handler.reqType) - isVal = true - } - - // if its a value get the element - if isVal { - req = req.Elem() - } - - cc := msg.Codec() - - // read the header. mostly a noop - if err = cc.ReadHeader(&codec.Message{}, codec.Event); err != nil { - return err - } - - // read the body into the handler request value - if err = cc.ReadBody(req.Interface()); err != nil { - return err - } - - // create the handler which will honour the SubscriberFunc type - fn := func(ctx context.Context, msg server.Message) error { - var vals []reflect.Value - if sub.typ.Kind() != reflect.Func { - vals = append(vals, sub.rcvr) - } - if handler.ctxType != nil { - vals = append(vals, reflect.ValueOf(ctx)) - } - - // values to pass the handler - vals = append(vals, reflect.ValueOf(msg.Payload())) - - // execute the actuall call of the handler - returnValues := handler.method.Call(vals) - if rerr := returnValues[0].Interface(); rerr != nil { - err = rerr.(error) - } - return err - } - - // wrap with subscriber wrappers - for i := len(router.subWrappers); i > 0; i-- { - fn = router.subWrappers[i-1](fn) - } - - // create new rpc message - rpcMsg := &rpcMessage{ - topic: msg.Topic(), - contentType: msg.ContentType(), - payload: req.Interface(), - codec: msg.(*rpcMessage).codec, - header: msg.Header(), - body: msg.Body(), - } - - // execute the message handler - if err = fn(ctx, rpcMsg); err != nil { - errResults = append(errResults, err.Error()) - } - } - } - - // if no errors just return - if len(errResults) > 0 { - err = merrors.InternalServerError("go.micro.server", "subscriber error: %v", strings.Join(errResults, "\n")) - } - - return err -} diff --git a/server/mucp/rpc_server.go b/server/mucp/rpc_server.go deleted file mode 100644 index d5460623..00000000 --- a/server/mucp/rpc_server.go +++ /dev/null @@ -1,1014 +0,0 @@ -package mucp - -import ( - "context" - "fmt" - "io" - "net" - "runtime/debug" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/codec" - raw "github.com/unistack-org/micro/v3/codec/bytes" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/metadata" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/util/addr" - "github.com/unistack-org/micro/v3/util/backoff" - mnet "github.com/unistack-org/micro/v3/util/net" - "github.com/unistack-org/micro/v3/util/socket" -) - -type rpcServer struct { - router *router - exit chan chan error - - sync.RWMutex - opts server.Options - handlers map[string]server.Handler - subscribers map[server.Subscriber][]broker.Subscriber - // marks the serve as started - started bool - // used for first registration - registered bool - // subscribe to service name - subscriber broker.Subscriber - // graceful exit - wg *sync.WaitGroup - - rsvc *registry.Service -} - -var ( - log = logger.NewHelper(logger.DefaultLogger).WithFields(map[string]interface{}{"service": "server"}) -) - -func wait(ctx context.Context) *sync.WaitGroup { - if ctx == nil { - return nil - } - wg, ok := ctx.Value("wait").(*sync.WaitGroup) - if !ok { - return nil - } - return wg -} - -func newServer(opts ...server.Option) server.Server { - options := newOptions(opts...) - router := newRpcRouter() - router.hdlrWrappers = options.HdlrWrappers - router.subWrappers = options.SubWrappers - - return &rpcServer{ - opts: options, - router: router, - handlers: make(map[string]server.Handler), - subscribers: make(map[server.Subscriber][]broker.Subscriber), - exit: make(chan chan error), - wg: wait(options.Context), - } -} - -// HandleEvent handles inbound messages to the service directly -// TODO: handle requests from an event. We won't send a response. -func (s *rpcServer) HandleEvent(msg *broker.Message) error { - if msg.Header == nil { - // create empty map in case of headers empty to avoid panic later - msg.Header = make(map[string]string) - } - - // get codec - ct := msg.Header["Content-Type"] - - // default content type - if len(ct) == 0 { - msg.Header["Content-Type"] = DefaultContentType - ct = DefaultContentType - } - - // get codec - cf, err := s.newCodec(ct) - if err != nil { - return err - } - - // copy headers - hdr := make(map[string]string, len(msg.Header)) - for k, v := range msg.Header { - hdr[k] = v - } - - // create context - ctx := metadata.NewContext(context.Background(), hdr) - - // TODO: inspect message header - // Micro-Service means a request - // Micro-Topic means a message - - rpcMsg := &rpcMessage{ - topic: msg.Header["Micro-Topic"], - contentType: ct, - payload: &raw.Frame{Data: msg.Body}, - codec: cf, - header: msg.Header, - body: msg.Body, - } - - // existing router - r := server.Router(s.router) - - // if the router is present then execute it - if s.opts.Router != nil { - // create a wrapped function - handler := s.opts.Router.ProcessMessage - - // execute the wrapper for it - for i := len(s.opts.SubWrappers); i > 0; i-- { - handler = s.opts.SubWrappers[i-1](handler) - } - - // set the router - r = rpcRouter{m: handler} - } - - return r.ProcessMessage(ctx, rpcMsg) -} - -// ServeConn serves a single connection -func (s *rpcServer) ServeConn(sock transport.Socket) { - // streams are multiplexed on Micro-Stream or Micro-Id header - pool := socket.NewPool() - - // get global waitgroup - s.Lock() - gg := s.wg - s.Unlock() - - // waitgroup to wait for processing to finish - wg := &waitGroup{ - gg: gg, - } - - defer func() { - // wait till done - wg.Wait() - - // close all the sockets for this connection - pool.Close() - - // close underlying socket - sock.Close() - - // recover any panics - if r := recover(); r != nil { - if logger.V(logger.ErrorLevel, log) { - log.Error("panic recovered: ", r) - log.Error(string(debug.Stack())) - } - } - }() - - for { - var msg transport.Message - // process inbound messages one at a time - if err := sock.Recv(&msg); err != nil { - return - } - - // check the message header for - // Micro-Service is a request - // Micro-Topic is a message - if t := msg.Header["Micro-Topic"]; len(t) > 0 { - // TODO: handle the error event - if err := s.HandleEvent(newMessage(msg)); err != nil { - msg.Header["Micro-Error"] = err.Error() - } - // write back some 200 - if err := sock.Send(&transport.Message{ - Header: msg.Header, - }); err != nil { - break - } - // we're done - continue - } - - // business as usual - - // use Micro-Stream as the stream identifier - // in the event its blank we'll always process - // on the same socket - id := msg.Header["Micro-Stream"] - - // if there's no stream id then its a standard request - // use the Micro-Id - if len(id) == 0 { - id = msg.Header["Micro-Id"] - } - - // check stream id - var stream bool - - if v := getHeader("Micro-Stream", msg.Header); len(v) > 0 { - stream = true - } - - // check if we have an existing socket - psock, ok := pool.Get(id) - - // if we don't have a socket and its a stream - if !ok && stream { - // check if its a last stream EOS error - err := msg.Header["Micro-Error"] - if err == lastStreamResponseError.Error() { - pool.Release(psock) - continue - } - } - - // got an existing socket already - if ok { - // we're starting processing - wg.Add(1) - - // pass the message to that existing socket - if err := psock.Accept(&msg); err != nil { - // release the socket if there's an error - pool.Release(psock) - } - - // done waiting - wg.Done() - - // continue to the next message - continue - } - - // no socket was found so its new - // set the local and remote values - psock.SetLocal(sock.Local()) - psock.SetRemote(sock.Remote()) - - // load the socket with the current message - psock.Accept(&msg) - - // now walk the usual path - - // we use this Timeout header to set a server deadline - to := msg.Header["Timeout"] - // we use this Content-Type header to identify the codec needed - ct := msg.Header["Content-Type"] - - // copy the message headers - hdr := make(map[string]string, len(msg.Header)) - for k, v := range msg.Header { - hdr[k] = v - } - - // set local/remote ips - hdr["Local"] = sock.Local() - hdr["Remote"] = sock.Remote() - - // create new context with the metadata - ctx := metadata.NewContext(context.Background(), hdr) - - // set the timeout from the header if we have it - if len(to) > 0 { - if n, err := strconv.ParseUint(to, 10, 64); err == nil { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(n)) - defer cancel() - } - } - - // if there's no content type default it - if len(ct) == 0 { - msg.Header["Content-Type"] = DefaultContentType - ct = DefaultContentType - } - - // setup old protocol - cf := setupProtocol(&msg) - - // no legacy codec needed - if cf == nil { - var err error - // try get a new codec - if cf, err = s.newCodec(ct); err != nil { - // no codec found so send back an error - sock.Send(&transport.Message{ - Header: map[string]string{ - "Content-Type": "text/plain", - }, - Body: []byte(err.Error()), - }) - - // release the socket we just created - pool.Release(psock) - // now continue - continue - } - } - - // create a new rpc codec based on the pseudo socket and codec - rcodec := newRpcCodec(&msg, psock, cf) - // check the protocol as well - protocol := rcodec.String() - - // internal request - request := &rpcRequest{ - service: getHeader("Micro-Service", msg.Header), - method: getHeader("Micro-Method", msg.Header), - endpoint: getHeader("Micro-Endpoint", msg.Header), - contentType: ct, - codec: rcodec, - header: msg.Header, - body: msg.Body, - socket: psock, - stream: stream, - } - - // internal response - response := &rpcResponse{ - header: make(map[string]string), - socket: psock, - codec: rcodec, - } - - // set router - r := server.Router(s.router) - - // if not nil use the router specified - if s.opts.Router != nil { - // create a wrapped function - handler := func(ctx context.Context, req server.Request, rsp interface{}) error { - return s.opts.Router.ServeRequest(ctx, req, rsp.(server.Response)) - } - - // execute the wrapper for it - for i := len(s.opts.HdlrWrappers); i > 0; i-- { - handler = s.opts.HdlrWrappers[i-1](handler) - } - - // set the router - r = rpcRouter{h: handler} - } - - // wait for two coroutines to exit - // serve the request and process the outbound messages - wg.Add(2) - - // process the outbound messages from the socket - go func(id string, psock *socket.Socket) { - defer func() { - // TODO: don't hack this but if its grpc just break out of the stream - // We do this because the underlying connection is h2 and its a stream - switch protocol { - case "grpc": - sock.Close() - } - // release the socket - pool.Release(psock) - // signal we're done - wg.Done() - - // recover any panics for outbound process - if r := recover(); r != nil { - if logger.V(logger.ErrorLevel, log) { - log.Error("panic recovered: ", r) - log.Error(string(debug.Stack())) - } - } - }() - - for { - // get the message from our internal handler/stream - m := new(transport.Message) - if err := psock.Process(m); err != nil { - return - } - - // send the message back over the socket - if err := sock.Send(m); err != nil { - return - } - } - }(id, psock) - - // serve the request in a go routine as this may be a stream - go func(id string, psock *socket.Socket) { - defer func() { - // release the socket - pool.Release(psock) - // signal we're done - wg.Done() - - // recover any panics for call handler - if r := recover(); r != nil { - log.Error("panic recovered: ", r) - log.Error(string(debug.Stack())) - } - }() - - // serve the actual request using the request router - if serveRequestError := r.ServeRequest(ctx, request, response); serveRequestError != nil { - // write an error response - writeError := rcodec.Write(&codec.Message{ - Header: msg.Header, - Error: serveRequestError.Error(), - Type: codec.Error, - }, nil) - - // if the server request is an EOS error we let the socket know - // sometimes the socket is already closed on the other side, so we can ignore that error - alreadyClosed := serveRequestError == lastStreamResponseError && writeError == io.EOF - - // could not write error response - if writeError != nil && !alreadyClosed { - log.Debugf("rpc: unable to write error response: %v", writeError) - } - } - }(id, psock) - } -} - -func (s *rpcServer) newCodec(contentType string) (codec.NewCodec, error) { - if cf, ok := s.opts.Codecs[contentType]; ok { - return cf, nil - } - if cf, ok := DefaultCodecs[contentType]; ok { - return cf, nil - } - return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType) -} - -func (s *rpcServer) Options() server.Options { - s.RLock() - opts := s.opts - s.RUnlock() - return opts -} - -func (s *rpcServer) Init(opts ...server.Option) error { - s.Lock() - defer s.Unlock() - - for _, opt := range opts { - opt(&s.opts) - } - // update router if its the default - if s.opts.Router == nil { - r := newRpcRouter() - r.hdlrWrappers = s.opts.HdlrWrappers - r.serviceMap = s.router.serviceMap - r.subWrappers = s.opts.SubWrappers - s.router = r - } - - s.rsvc = nil - - return nil -} - -func (s *rpcServer) NewHandler(h interface{}, opts ...server.HandlerOption) server.Handler { - return s.router.NewHandler(h, opts...) -} - -func (s *rpcServer) Handle(h server.Handler) error { - s.Lock() - defer s.Unlock() - - if err := s.router.Handle(h); err != nil { - return err - } - - s.handlers[h.Name()] = h - - return nil -} - -func (s *rpcServer) NewSubscriber(topic string, sb interface{}, opts ...server.SubscriberOption) server.Subscriber { - return s.router.NewSubscriber(topic, sb, opts...) -} - -func (s *rpcServer) Subscribe(sb server.Subscriber) error { - s.Lock() - defer s.Unlock() - - if err := s.router.Subscribe(sb); err != nil { - return err - } - - s.subscribers[sb] = nil - return nil -} - -func (s *rpcServer) Register() error { - s.RLock() - rsvc := s.rsvc - config := s.Options() - s.RUnlock() - - // only register if it exists or is not noop - if config.Registry == nil || config.Registry.String() == "noop" { - return nil - } - - regFunc := func(service *registry.Service) error { - // create registry options - rOpts := []registry.RegisterOption{ - registry.RegisterTTL(config.RegisterTTL), - registry.RegisterDomain(s.opts.Namespace), - } - - var regErr error - - for i := 0; i < 3; i++ { - // attempt to register - if err := config.Registry.Register(service, rOpts...); err != nil { - // set the error - regErr = err - // backoff then retry - time.Sleep(backoff.Do(i + 1)) - continue - } - // success so nil error - regErr = nil - break - } - - return regErr - } - - // have we registered before? - if rsvc != nil { - if err := regFunc(rsvc); err != nil { - return err - } - return nil - } - - var err error - var advt, host, port string - var cacheService bool - - // check the advertise address first - // if it exists then use it, otherwise - // use the address - if len(config.Advertise) > 0 { - advt = config.Advertise - } else { - advt = config.Address - } - - if cnt := strings.Count(advt, ":"); cnt >= 1 { - // ipv6 address in format [host]:port or ipv4 host:port - host, port, err = net.SplitHostPort(advt) - if err != nil { - return err - } - } else { - host = advt - } - - if ip := net.ParseIP(host); ip != nil { - cacheService = true - } - - addr, err := addr.Extract(host) - if err != nil { - return err - } - - // make copy of metadata - md := metadata.Copy(config.Metadata) - - // mq-rpc(eg. nats) doesn't need the port. its addr is queue name. - if port != "" { - addr = mnet.HostPort(addr, port) - } - - // register service - node := ®istry.Node{ - Id: config.Name + "-" + config.Id, - Address: addr, - Metadata: md, - } - - node.Metadata["transport"] = config.Transport.String() - node.Metadata["broker"] = config.Broker.String() - node.Metadata["server"] = s.String() - node.Metadata["registry"] = config.Registry.String() - node.Metadata["protocol"] = "mucp" - - s.RLock() - - // Maps are ordered randomly, sort the keys for consistency - var handlerList []string - for n, e := range s.handlers { - // Only advertise non internal handlers - if !e.Options().Internal { - handlerList = append(handlerList, n) - } - } - - sort.Strings(handlerList) - - var subscriberList []server.Subscriber - for e := range s.subscribers { - // Only advertise non internal subscribers - if !e.Options().Internal { - subscriberList = append(subscriberList, e) - } - } - - sort.Slice(subscriberList, func(i, j int) bool { - return subscriberList[i].Topic() > subscriberList[j].Topic() - }) - - endpoints := make([]*registry.Endpoint, 0, len(handlerList)+len(subscriberList)) - - for _, n := range handlerList { - endpoints = append(endpoints, s.handlers[n].Endpoints()...) - } - - for _, e := range subscriberList { - endpoints = append(endpoints, e.Endpoints()...) - } - - service := ®istry.Service{ - Name: config.Name, - Version: config.Version, - Nodes: []*registry.Node{node}, - Endpoints: endpoints, - } - - // get registered value - registered := s.registered - - s.RUnlock() - - if !registered { - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Registry [%s] Registering node: %s", config.Registry.String(), node.Id) - } - } - - // register the service - if err := regFunc(service); err != nil { - return err - } - - // already registered? don't need to register subscribers - if registered { - return nil - } - - s.Lock() - defer s.Unlock() - - // set what we're advertising - s.opts.Advertise = addr - - // router can exchange messages - if s.opts.Router != nil { - // subscribe to the topic with own name - sub, err := s.opts.Broker.Subscribe(config.Name, s.HandleEvent) - if err != nil { - return err - } - - // save the subscriber - s.subscriber = sub - } - - // subscribe for all of the subscribers - for sb := range s.subscribers { - var opts []broker.SubscribeOption - if queue := sb.Options().Queue; len(queue) > 0 { - opts = append(opts, broker.Queue(queue)) - } - - if cx := sb.Options().Context; cx != nil { - opts = append(opts, broker.SubscribeContext(cx)) - } - - sub, err := config.Broker.Subscribe(sb.Topic(), s.HandleEvent, opts...) - if err != nil { - return err - } - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Subscribing to topic: %s", sub.Topic()) - } - s.subscribers[sb] = []broker.Subscriber{sub} - } - if cacheService { - s.rsvc = service - } - s.registered = true - - return nil -} - -func (s *rpcServer) Deregister() error { - var err error - var advt, host, port string - - s.RLock() - config := s.Options() - s.RUnlock() - - // only register if it exists or is not noop - if config.Registry == nil || config.Registry.String() == "noop" { - return nil - } - - // check the advertise address first - // if it exists then use it, otherwise - // use the address - if len(config.Advertise) > 0 { - advt = config.Advertise - } else { - advt = config.Address - } - - if cnt := strings.Count(advt, ":"); cnt >= 1 { - // ipv6 address in format [host]:port or ipv4 host:port - host, port, err = net.SplitHostPort(advt) - if err != nil { - return err - } - } else { - host = advt - } - - addr, err := addr.Extract(host) - if err != nil { - return err - } - - // mq-rpc(eg. nats) doesn't need the port. its addr is queue name. - if port != "" { - addr = mnet.HostPort(addr, port) - } - - node := ®istry.Node{ - Id: config.Name + "-" + config.Id, - Address: addr, - } - - service := ®istry.Service{ - Name: config.Name, - Version: config.Version, - Nodes: []*registry.Node{node}, - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Registry [%s] Deregistering node: %s", config.Registry.String(), node.Id) - } - if err := config.Registry.Deregister(service, registry.DeregisterDomain(s.opts.Namespace)); err != nil { - return err - } - - s.Lock() - s.rsvc = nil - - if !s.registered { - s.Unlock() - return nil - } - - s.registered = false - - // close the subscriber - if s.subscriber != nil { - s.subscriber.Unsubscribe() - s.subscriber = nil - } - - for sb, subs := range s.subscribers { - for _, sub := range subs { - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Unsubscribing %s from topic: %s", node.Id, sub.Topic()) - } - sub.Unsubscribe() - } - s.subscribers[sb] = nil - } - - s.Unlock() - return nil -} - -func (s *rpcServer) Start() error { - s.RLock() - if s.started { - s.RUnlock() - return nil - } - s.RUnlock() - - config := s.Options() - - // start listening on the transport - ts, err := config.Transport.Listen(config.Address) - if err != nil { - return err - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Transport [%s] Listening on %s", config.Transport.String(), ts.Addr()) - } - - // swap address - s.Lock() - addr := s.opts.Address - s.opts.Address = ts.Addr() - s.Unlock() - - bname := config.Broker.String() - - // connect to the broker - if err := config.Broker.Connect(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Broker [%s] connect error: %v", bname, err) - } - return err - } - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Broker [%s] Connected to %s", bname, config.Broker.Address()) - } - - // use RegisterCheck func before register - if err = s.opts.RegisterCheck(s.opts.Context); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s register check error: %s", config.Name, config.Id, err) - } - } else { - // announce self to the world - if err = s.Register(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s register error: %s", config.Name, config.Id, err) - } - } - } - - exit := make(chan bool) - - go func() { - for { - // listen for connections - err := ts.Accept(s.ServeConn) - - // TODO: listen for messages - // msg := broker.Exchange(service).Consume() - - select { - // check if we're supposed to exit - case <-exit: - return - // check the error and backoff - default: - if err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Accept error: %v", err) - } - time.Sleep(time.Second) - continue - } - } - - // no error just exit - return - } - }() - - go func() { - t := new(time.Ticker) - - // only process if it exists - if s.opts.RegisterInterval > time.Duration(0) { - // new ticker - t = time.NewTicker(s.opts.RegisterInterval) - } - - // return error chan - var ch chan error - - Loop: - for { - select { - // register self on interval - case <-t.C: - s.RLock() - registered := s.registered - s.RUnlock() - rerr := s.opts.RegisterCheck(s.opts.Context) - if rerr != nil && registered { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s register check error: %s, deregister it", config.Name, config.Id, rerr) - } - // deregister self in case of error - if err := s.Deregister(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s deregister error: %s", config.Name, config.Id, err) - } - } - } else if rerr != nil && !registered { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s register check error: %s", config.Name, config.Id, rerr) - } - continue - } - if err := s.Register(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s register error: %s", config.Name, config.Id, err) - } - } - // wait for exit - case ch = <-s.exit: - t.Stop() - close(exit) - break Loop - } - } - - s.RLock() - registered := s.registered - s.RUnlock() - if registered { - // deregister self - if err := s.Deregister(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Server %s-%s deregister error: %s", config.Name, config.Id, err) - } - } - } - - s.Lock() - swg := s.wg - s.Unlock() - - // wait for requests to finish - if swg != nil { - swg.Wait() - } - - // close transport listener - ch <- ts.Close() - - if logger.V(logger.InfoLevel, logger.DefaultLogger) { - log.Infof("Broker [%s] Disconnected from %s", bname, config.Broker.Address()) - } - // disconnect the broker - if err := config.Broker.Disconnect(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - log.Errorf("Broker [%s] Disconnect error: %v", bname, err) - } - } - - // swap back address - s.Lock() - s.opts.Address = addr - s.Unlock() - }() - - // mark the server as started - s.Lock() - s.started = true - s.Unlock() - - return nil -} - -func (s *rpcServer) Stop() error { - s.RLock() - if !s.started { - s.RUnlock() - return nil - } - s.RUnlock() - - ch := make(chan error) - s.exit <- ch - - err := <-ch - s.Lock() - s.started = false - s.Unlock() - - return err -} - -func (s *rpcServer) String() string { - return "mucp" -} diff --git a/server/mucp/rpc_stream.go b/server/mucp/rpc_stream.go deleted file mode 100644 index 977524b5..00000000 --- a/server/mucp/rpc_stream.go +++ /dev/null @@ -1,105 +0,0 @@ -package mucp - -import ( - "context" - "errors" - "io" - "sync" - - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/server" -) - -// Implements the Streamer interface -type rpcStream struct { - sync.RWMutex - id string - closed bool - err error - request server.Request - codec codec.Codec - context context.Context -} - -func (r *rpcStream) Context() context.Context { - return r.context -} - -func (r *rpcStream) Request() server.Request { - return r.request -} - -func (r *rpcStream) Send(msg interface{}) error { - r.Lock() - defer r.Unlock() - - resp := codec.Message{ - Target: r.request.Service(), - Method: r.request.Method(), - Endpoint: r.request.Endpoint(), - Id: r.id, - Type: codec.Response, - } - - if err := r.codec.Write(&resp, msg); err != nil { - r.err = err - } - - return nil -} - -func (r *rpcStream) Recv(msg interface{}) error { - req := new(codec.Message) - req.Type = codec.Request - - err := r.codec.ReadHeader(req, req.Type) - r.Lock() - defer r.Unlock() - if err != nil { - // discard body - r.codec.ReadBody(nil) - r.err = err - return err - } - - // check the error - if len(req.Error) > 0 { - // Check the client closed the stream - switch req.Error { - case lastStreamResponseError.Error(): - // discard body - r.Unlock() - r.codec.ReadBody(nil) - r.Lock() - r.err = io.EOF - return io.EOF - default: - return errors.New(req.Error) - } - } - - // we need to stay up to date with sequence numbers - r.id = req.Id - r.Unlock() - err = r.codec.ReadBody(msg) - r.Lock() - if err != nil { - r.err = err - return err - } - - return nil -} - -func (r *rpcStream) Error() error { - r.RLock() - defer r.RUnlock() - return r.err -} - -func (r *rpcStream) Close() error { - r.Lock() - defer r.Unlock() - r.closed = true - return r.codec.Close() -} diff --git a/server/mucp/rpc_stream_test.go b/server/mucp/rpc_stream_test.go deleted file mode 100644 index 1965536b..00000000 --- a/server/mucp/rpc_stream_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package mucp - -import ( - "bytes" - "fmt" - "io" - "math/rand" - "sync" - "testing" - "time" - - "github.com/golang/protobuf/proto" - "github.com/unistack-org/micro/v3/codec/json" - protoCodec "github.com/unistack-org/micro/v3/codec/proto" -) - -// protoStruct implements proto.Message -type protoStruct struct { - Payload string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` -} - -func (m *protoStruct) Reset() { *m = protoStruct{} } -func (m *protoStruct) String() string { return proto.CompactTextString(m) } -func (*protoStruct) ProtoMessage() {} - -// safeBuffer throws away everything and wont Read data back -type safeBuffer struct { - sync.RWMutex - buf []byte - off int -} - -func (b *safeBuffer) Write(p []byte) (n int, err error) { - if len(p) == 0 { - return 0, nil - } - // Cannot retain p, so we must copy it: - p2 := make([]byte, len(p)) - copy(p2, p) - b.Lock() - b.buf = append(b.buf, p2...) - b.Unlock() - return len(p2), nil -} - -func (b *safeBuffer) Read(p []byte) (n int, err error) { - if len(p) == 0 { - return 0, nil - } - b.RLock() - n = copy(p, b.buf[b.off:]) - b.RUnlock() - if n == 0 { - return 0, io.EOF - } - b.off += n - return n, nil -} - -func (b *safeBuffer) Close() error { - return nil -} - -func TestRPCStream_Sequence(t *testing.T) { - buffer := new(bytes.Buffer) - rwc := readWriteCloser{ - rbuf: buffer, - wbuf: buffer, - } - codec := json.NewCodec(&rwc) - streamServer := rpcStream{ - codec: codec, - request: &rpcRequest{ - codec: codec, - }, - } - - // Check if sequence is correct - for i := 0; i < 1000; i++ { - if err := streamServer.Send(fmt.Sprintf(`{"test":"value %d"}`, i)); err != nil { - t.Errorf("Unexpected Send error: %s", err) - } - } - - for i := 0; i < 1000; i++ { - var msg string - if err := streamServer.Recv(&msg); err != nil { - t.Errorf("Unexpected Recv error: %s", err) - } - if msg != fmt.Sprintf(`{"test":"value %d"}`, i) { - t.Errorf("Unexpected msg: %s", msg) - } - } -} - -func TestRPCStream_Concurrency(t *testing.T) { - buffer := new(safeBuffer) - codec := protoCodec.NewCodec(buffer) - streamServer := rpcStream{ - codec: codec, - request: &rpcRequest{ - codec: codec, - }, - } - - var wg sync.WaitGroup - // Check if race conditions happen - for i := 0; i < 10; i++ { - wg.Add(2) - - go func() { - for i := 0; i < 50; i++ { - msg := protoStruct{Payload: "test"} - <-time.After(time.Duration(rand.Intn(50)) * time.Millisecond) - if err := streamServer.Send(msg); err != nil { - t.Errorf("Unexpected Send error: %s", err) - } - } - wg.Done() - }() - - go func() { - for i := 0; i < 50; i++ { - <-time.After(time.Duration(rand.Intn(50)) * time.Millisecond) - if err := streamServer.Recv(&protoStruct{}); err != nil { - t.Errorf("Unexpected Recv error: %s", err) - } - } - wg.Done() - }() - } - wg.Wait() -} diff --git a/server/mucp/rpc_util.go b/server/mucp/rpc_util.go deleted file mode 100644 index 66cf8674..00000000 --- a/server/mucp/rpc_util.go +++ /dev/null @@ -1,32 +0,0 @@ -package mucp - -import ( - "sync" -) - -// waitgroup for global management of connections -type waitGroup struct { - // local waitgroup - lg sync.WaitGroup - // global waitgroup - gg *sync.WaitGroup -} - -func (w *waitGroup) Add(i int) { - w.lg.Add(i) - if w.gg != nil { - w.gg.Add(i) - } -} - -func (w *waitGroup) Done() { - w.lg.Done() - if w.gg != nil { - w.gg.Done() - } -} - -func (w *waitGroup) Wait() { - // only wait on local group - w.lg.Wait() -} diff --git a/server/mucp/subscriber.go b/server/mucp/subscriber.go deleted file mode 100644 index 814318f5..00000000 --- a/server/mucp/subscriber.go +++ /dev/null @@ -1,185 +0,0 @@ -package mucp - -import ( - "fmt" - "reflect" - - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/server" - "github.com/unistack-org/micro/v3/transport" -) - -const ( - subSig = "func(context.Context, interface{}) error" -) - -type handler struct { - method reflect.Value - reqType reflect.Type - ctxType reflect.Type -} - -type subscriber struct { - topic string - rcvr reflect.Value - typ reflect.Type - subscriber interface{} - handlers []*handler - endpoints []*registry.Endpoint - opts server.SubscriberOptions -} - -func newMessage(msg transport.Message) *broker.Message { - return &broker.Message{ - Header: msg.Header, - Body: msg.Body, - } -} - -func newSubscriber(topic string, sub interface{}, opts ...server.SubscriberOption) server.Subscriber { - options := server.SubscriberOptions{ - AutoAck: true, - } - - for _, o := range opts { - o(&options) - } - - var endpoints []*registry.Endpoint - var handlers []*handler - - if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func { - h := &handler{ - method: reflect.ValueOf(sub), - } - - switch typ.NumIn() { - case 1: - h.reqType = typ.In(0) - case 2: - h.ctxType = typ.In(0) - h.reqType = typ.In(1) - } - - handlers = append(handlers, h) - - endpoints = append(endpoints, ®istry.Endpoint{ - Name: "Func", - Request: extractSubValue(typ), - Metadata: map[string]string{ - "topic": topic, - "subscriber": "true", - }, - }) - } else { - hdlr := reflect.ValueOf(sub) - name := reflect.Indirect(hdlr).Type().Name() - - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - h := &handler{ - method: method.Func, - } - - switch method.Type.NumIn() { - case 2: - h.reqType = method.Type.In(1) - case 3: - h.ctxType = method.Type.In(1) - h.reqType = method.Type.In(2) - } - - handlers = append(handlers, h) - - endpoints = append(endpoints, ®istry.Endpoint{ - Name: name + "." + method.Name, - Request: extractSubValue(method.Type), - Metadata: map[string]string{ - "topic": topic, - "subscriber": "true", - }, - }) - } - } - - return &subscriber{ - rcvr: reflect.ValueOf(sub), - typ: reflect.TypeOf(sub), - topic: topic, - subscriber: sub, - handlers: handlers, - endpoints: endpoints, - opts: options, - } -} - -func validateSubscriber(sub server.Subscriber) error { - typ := reflect.TypeOf(sub.Subscriber()) - var argType reflect.Type - - if typ.Kind() == reflect.Func { - name := "Func" - switch typ.NumIn() { - case 2: - argType = typ.In(1) - default: - return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig) - } - if !isExportedOrBuiltinType(argType) { - return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType) - } - if typ.NumOut() != 1 { - return fmt.Errorf("subscriber %v has wrong number of outs: %v require signature %s", - name, typ.NumOut(), subSig) - } - if returnType := typ.Out(0); returnType != typeOfError { - return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String()) - } - } else { - hdlr := reflect.ValueOf(sub.Subscriber()) - name := reflect.Indirect(hdlr).Type().Name() - - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - - switch method.Type.NumIn() { - case 3: - argType = method.Type.In(2) - default: - return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s", - name, method.Name, method.Type.NumIn(), subSig) - } - - if !isExportedOrBuiltinType(argType) { - return fmt.Errorf("%v argument type not exported: %v", name, argType) - } - if method.Type.NumOut() != 1 { - return fmt.Errorf( - "subscriber %v.%v has wrong number of outs: %v require signature %s", - name, method.Name, method.Type.NumOut(), subSig) - } - if returnType := method.Type.Out(0); returnType != typeOfError { - return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String()) - } - } - } - - return nil -} - -func (s *subscriber) Topic() string { - return s.topic -} - -func (s *subscriber) Subscriber() interface{} { - return s.subscriber -} - -func (s *subscriber) Endpoints() []*registry.Endpoint { - return s.endpoints -} - -func (s *subscriber) Options() server.SubscriberOptions { - return s.opts -} diff --git a/server/options.go b/server/options.go index 5d7dc35f..18f6a8cd 100644 --- a/server/options.go +++ b/server/options.go @@ -8,13 +8,10 @@ import ( "github.com/unistack-org/micro/v3/auth" "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/broker/http" "github.com/unistack-org/micro/v3/codec" "github.com/unistack-org/micro/v3/debug/trace" "github.com/unistack-org/micro/v3/registry" - "github.com/unistack-org/micro/v3/registry/mdns" "github.com/unistack-org/micro/v3/transport" - thttp "github.com/unistack-org/micro/v3/transport/http" ) type Options struct { @@ -64,18 +61,6 @@ func newOptions(opt ...Option) Options { o(&opts) } - if opts.Broker == nil { - opts.Broker = http.NewBroker() - } - - if opts.Registry == nil { - opts.Registry = mdns.NewRegistry() - } - - if opts.Transport == nil { - opts.Transport = thttp.NewTransport() - } - if opts.RegisterCheck == nil { opts.RegisterCheck = DefaultRegisterCheck } @@ -228,9 +213,6 @@ func TLSConfig(t *tls.Config) Option { // set the default transport if one is not // already set. Required for Init call below. - if o.Transport == nil { - o.Transport = thttp.NewTransport() - } // set the transport tls o.Transport.Init( diff --git a/server/proto/server.pb.go b/server/proto/server.pb.go deleted file mode 100644 index 3fbb696c..00000000 --- a/server/proto/server.pb.go +++ /dev/null @@ -1,349 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: server/proto/server.proto - -package go_micro_server - -import ( - proto "github.com/golang/protobuf/proto" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type HandleRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` - Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"` -} - -func (x *HandleRequest) Reset() { - *x = HandleRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_server_proto_server_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HandleRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HandleRequest) ProtoMessage() {} - -func (x *HandleRequest) ProtoReflect() protoreflect.Message { - mi := &file_server_proto_server_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 HandleRequest.ProtoReflect.Descriptor instead. -func (*HandleRequest) Descriptor() ([]byte, []int) { - return file_server_proto_server_proto_rawDescGZIP(), []int{0} -} - -func (x *HandleRequest) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -func (x *HandleRequest) GetEndpoint() string { - if x != nil { - return x.Endpoint - } - return "" -} - -func (x *HandleRequest) GetProtocol() string { - if x != nil { - return x.Protocol - } - return "" -} - -type HandleResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *HandleResponse) Reset() { - *x = HandleResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_server_proto_server_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HandleResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HandleResponse) ProtoMessage() {} - -func (x *HandleResponse) ProtoReflect() protoreflect.Message { - mi := &file_server_proto_server_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 HandleResponse.ProtoReflect.Descriptor instead. -func (*HandleResponse) Descriptor() ([]byte, []int) { - return file_server_proto_server_proto_rawDescGZIP(), []int{1} -} - -type SubscribeRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` -} - -func (x *SubscribeRequest) Reset() { - *x = SubscribeRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_server_proto_server_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubscribeRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubscribeRequest) ProtoMessage() {} - -func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { - mi := &file_server_proto_server_proto_msgTypes[2] - 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 SubscribeRequest.ProtoReflect.Descriptor instead. -func (*SubscribeRequest) Descriptor() ([]byte, []int) { - return file_server_proto_server_proto_rawDescGZIP(), []int{2} -} - -func (x *SubscribeRequest) GetTopic() string { - if x != nil { - return x.Topic - } - return "" -} - -type SubscribeResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SubscribeResponse) Reset() { - *x = SubscribeResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_server_proto_server_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubscribeResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubscribeResponse) ProtoMessage() {} - -func (x *SubscribeResponse) ProtoReflect() protoreflect.Message { - mi := &file_server_proto_server_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead. -func (*SubscribeResponse) Descriptor() ([]byte, []int) { - return file_server_proto_server_proto_rawDescGZIP(), []int{3} -} - -var File_server_proto_server_proto protoreflect.FileDescriptor - -var file_server_proto_server_proto_rawDesc = []byte{ - 0x0a, 0x19, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x2e, - 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x61, 0x0a, 0x0d, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, - 0x10, 0x0a, 0x0e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x28, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x22, 0x13, 0x0a, 0x11, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x32, 0xab, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x06, 0x48, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_server_proto_server_proto_rawDescOnce sync.Once - file_server_proto_server_proto_rawDescData = file_server_proto_server_proto_rawDesc -) - -func file_server_proto_server_proto_rawDescGZIP() []byte { - file_server_proto_server_proto_rawDescOnce.Do(func() { - file_server_proto_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_server_proto_server_proto_rawDescData) - }) - return file_server_proto_server_proto_rawDescData -} - -var file_server_proto_server_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_server_proto_server_proto_goTypes = []interface{}{ - (*HandleRequest)(nil), // 0: go.micro.server.HandleRequest - (*HandleResponse)(nil), // 1: go.micro.server.HandleResponse - (*SubscribeRequest)(nil), // 2: go.micro.server.SubscribeRequest - (*SubscribeResponse)(nil), // 3: go.micro.server.SubscribeResponse -} -var file_server_proto_server_proto_depIdxs = []int32{ - 0, // 0: go.micro.server.Server.Handle:input_type -> go.micro.server.HandleRequest - 2, // 1: go.micro.server.Server.Subscribe:input_type -> go.micro.server.SubscribeRequest - 1, // 2: go.micro.server.Server.Handle:output_type -> go.micro.server.HandleResponse - 3, // 3: go.micro.server.Server.Subscribe:output_type -> go.micro.server.SubscribeResponse - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_server_proto_server_proto_init() } -func file_server_proto_server_proto_init() { - if File_server_proto_server_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_server_proto_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HandleRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_server_proto_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HandleResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_server_proto_server_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_server_proto_server_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeResponse); 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_server_proto_server_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_server_proto_server_proto_goTypes, - DependencyIndexes: file_server_proto_server_proto_depIdxs, - MessageInfos: file_server_proto_server_proto_msgTypes, - }.Build() - File_server_proto_server_proto = out.File - file_server_proto_server_proto_rawDesc = nil - file_server_proto_server_proto_goTypes = nil - file_server_proto_server_proto_depIdxs = nil -} diff --git a/server/proto/server.pb.micro.go b/server/proto/server.pb.micro.go deleted file mode 100644 index 8f77b59d..00000000 --- a/server/proto/server.pb.micro.go +++ /dev/null @@ -1,110 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: server/proto/server.proto - -package go_micro_server - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Server service - -func NewServerEndpoints() []*api.Endpoint { - return []*api.Endpoint{} -} - -// Client API for Server service - -type ServerService interface { - Handle(ctx context.Context, in *HandleRequest, opts ...client.CallOption) (*HandleResponse, error) - Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (*SubscribeResponse, error) -} - -type serverService struct { - c client.Client - name string -} - -func NewServerService(name string, c client.Client) ServerService { - return &serverService{ - c: c, - name: name, - } -} - -func (c *serverService) Handle(ctx context.Context, in *HandleRequest, opts ...client.CallOption) (*HandleResponse, error) { - req := c.c.NewRequest(c.name, "Server.Handle", in) - out := new(HandleResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *serverService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (*SubscribeResponse, error) { - req := c.c.NewRequest(c.name, "Server.Subscribe", in) - out := new(SubscribeResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Server service - -type ServerHandler interface { - Handle(context.Context, *HandleRequest, *HandleResponse) error - Subscribe(context.Context, *SubscribeRequest, *SubscribeResponse) error -} - -func RegisterServerHandler(s server.Server, hdlr ServerHandler, opts ...server.HandlerOption) error { - type server interface { - Handle(ctx context.Context, in *HandleRequest, out *HandleResponse) error - Subscribe(ctx context.Context, in *SubscribeRequest, out *SubscribeResponse) error - } - type Server struct { - server - } - h := &serverHandler{hdlr} - return s.Handle(s.NewHandler(&Server{h}, opts...)) -} - -type serverHandler struct { - ServerHandler -} - -func (h *serverHandler) Handle(ctx context.Context, in *HandleRequest, out *HandleResponse) error { - return h.ServerHandler.Handle(ctx, in, out) -} - -func (h *serverHandler) Subscribe(ctx context.Context, in *SubscribeRequest, out *SubscribeResponse) error { - return h.ServerHandler.Subscribe(ctx, in, out) -} diff --git a/server/proto/server.proto b/server/proto/server.proto deleted file mode 100644 index 2440edbe..00000000 --- a/server/proto/server.proto +++ /dev/null @@ -1,22 +0,0 @@ -syntax = "proto3"; - -package go.micro.server; - -service Server { - rpc Handle(HandleRequest) returns (HandleResponse) {}; - rpc Subscribe(SubscribeRequest) returns (SubscribeResponse) {}; -} - -message HandleRequest { - string service = 1; - string endpoint = 2; - string protocol = 3; -} - -message HandleResponse {} - -message SubscribeRequest { - string topic = 1; -} - -message SubscribeResponse {} diff --git a/server/proto/server_grpc.pb.go b/server/proto/server_grpc.pb.go deleted file mode 100644 index 398f139c..00000000 --- a/server/proto/server_grpc.pb.go +++ /dev/null @@ -1,126 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package go_micro_server - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// ServerClient is the client API for Server service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ServerClient interface { - Handle(ctx context.Context, in *HandleRequest, opts ...grpc.CallOption) (*HandleResponse, error) - Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) -} - -type serverClient struct { - cc grpc.ClientConnInterface -} - -func NewServerClient(cc grpc.ClientConnInterface) ServerClient { - return &serverClient{cc} -} - -func (c *serverClient) Handle(ctx context.Context, in *HandleRequest, opts ...grpc.CallOption) (*HandleResponse, error) { - out := new(HandleResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.Server/Handle", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *serverClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) { - out := new(SubscribeResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.Server/Subscribe", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ServerServer is the server API for Server service. -// All implementations must embed UnimplementedServerServer -// for forward compatibility -type ServerServer interface { - Handle(context.Context, *HandleRequest) (*HandleResponse, error) - Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error) - mustEmbedUnimplementedServerServer() -} - -// UnimplementedServerServer must be embedded to have forward compatible implementations. -type UnimplementedServerServer struct { -} - -func (*UnimplementedServerServer) Handle(context.Context, *HandleRequest) (*HandleResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Handle not implemented") -} -func (*UnimplementedServerServer) Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Subscribe not implemented") -} -func (*UnimplementedServerServer) mustEmbedUnimplementedServerServer() {} - -func RegisterServerServer(s *grpc.Server, srv ServerServer) { - s.RegisterService(&_Server_serviceDesc, srv) -} - -func _Server_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(HandleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ServerServer).Handle(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.Server/Handle", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ServerServer).Handle(ctx, req.(*HandleRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Server_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SubscribeRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ServerServer).Subscribe(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.Server/Subscribe", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ServerServer).Subscribe(ctx, req.(*SubscribeRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Server_serviceDesc = grpc.ServiceDesc{ - ServiceName: "go.micro.server.Server", - HandlerType: (*ServerServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Handle", - Handler: _Server_Handle_Handler, - }, - { - MethodName: "Subscribe", - Handler: _Server_Subscribe_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "server/proto/server.proto", -} diff --git a/store/cache/cache.go b/store/cache/cache.go deleted file mode 100644 index 54393e0a..00000000 --- a/store/cache/cache.go +++ /dev/null @@ -1,128 +0,0 @@ -package cache - -import ( - "github.com/unistack-org/micro/v3/store" - "github.com/unistack-org/micro/v3/store/memory" -) - -// cache store is a store with caching to reduce IO where applicable. -// A memory store is used to cache reads from the given backing store. -// Reads are read through, writes are write-through -type cache struct { - m store.Store // the memory store - b store.Store // the backing store, could be file, cockroach etc - options store.Options -} - -// NewStore returns a new cache store -func NewStore(store store.Store, opts ...store.Option) store.Store { - cf := &cache{ - m: memory.NewStore(opts...), - b: store, - } - return cf - -} - -func (c *cache) init(opts ...store.Option) error { - for _, o := range opts { - o(&c.options) - } - return nil -} - -// Init initialises the underlying stores -func (c *cache) Init(opts ...store.Option) error { - if err := c.init(opts...); err != nil { - return err - } - if err := c.m.Init(opts...); err != nil { - return err - } - return c.b.Init(opts...) -} - -// Options allows you to view the current options. -func (c *cache) Options() store.Options { - return c.options -} - -// Read takes a single key name and optional ReadOptions. It returns matching []*Record or an error. -func (c *cache) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) { - recs, err := c.m.Read(key, opts...) - if err != nil && err != store.ErrNotFound { - return nil, err - } - if len(recs) > 0 { - return recs, nil - } - recs, err = c.b.Read(key, opts...) - if err == nil { - for _, rec := range recs { - if err := c.m.Write(rec); err != nil { - return nil, err - } - } - } - return recs, err -} - -// Write() writes a record to the store, and returns an error if the record was not written. -// If the write succeeds in writing to memory but fails to write through to file, you'll receive an error -// but the value may still reside in memory so appropriate action should be taken. -func (c *cache) Write(r *store.Record, opts ...store.WriteOption) error { - if err := c.m.Write(r, opts...); err != nil { - return err - } - return c.b.Write(r, opts...) -} - -// Delete removes the record with the corresponding key from the store. -// If the delete succeeds in writing to memory but fails to write through to file, you'll receive an error -// but the value may still reside in memory so appropriate action should be taken. -func (c *cache) Delete(key string, opts ...store.DeleteOption) error { - if err := c.m.Delete(key, opts...); err != nil { - return err - } - return c.b.Delete(key, opts...) -} - -// List returns any keys that match, or an empty list with no error if none matched. -func (c *cache) List(opts ...store.ListOption) ([]string, error) { - keys, err := c.m.List(opts...) - if err != nil && err != store.ErrNotFound { - return nil, err - } - if len(keys) > 0 { - return keys, nil - } - keys, err = c.b.List(opts...) - if err == nil { - for _, key := range keys { - recs, err := c.b.Read(key) - if err != nil { - return nil, err - } - for _, r := range recs { - if err := c.m.Write(r); err != nil { - return nil, err - } - } - - } - } - return keys, err -} - -// Close the store and the underlying store -func (c *cache) Close() error { - if err := c.m.Close(); err != nil { - return err - } - return c.b.Close() -} - -// String returns the name of the implementation. -func (c *cache) String() string { - return "cache" -} diff --git a/store/cache/cache_test.go b/store/cache/cache_test.go deleted file mode 100644 index 36460bbe..00000000 --- a/store/cache/cache_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package cache - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/unistack-org/micro/v3/store" - "github.com/unistack-org/micro/v3/store/file" -) - -func cleanup(db string, s store.Store) { - s.Close() - dir := filepath.Join(file.DefaultDir, db+"/") - os.RemoveAll(dir) -} - -func TestRead(t *testing.T) { - cf := NewStore(file.NewStore()) - cf.Init() - cfInt := cf.(*cache) - defer cleanup(file.DefaultDatabase, cf) - - _, err := cf.Read("key1") - assert.Error(t, err, "Unexpected record") - cfInt.b.Write(&store.Record{ - Key: "key1", - Value: []byte("foo"), - }) - recs, err := cf.Read("key1") - assert.NoError(t, err) - assert.Len(t, recs, 1, "Expected a record to be pulled from file store") - recs, err = cfInt.m.Read("key1") - assert.NoError(t, err) - assert.Len(t, recs, 1, "Expected a memory store to be populatedfrom file store") - -} - -func TestWrite(t *testing.T) { - cf := NewStore(file.NewStore()) - cf.Init() - cfInt := cf.(*cache) - defer cleanup(file.DefaultDatabase, cf) - - cf.Write(&store.Record{ - Key: "key1", - Value: []byte("foo"), - }) - recs, _ := cfInt.m.Read("key1") - assert.Len(t, recs, 1, "Expected a record in the memory store") - recs, _ = cfInt.b.Read("key1") - assert.Len(t, recs, 1, "Expected a record in the file store") - -} - -func TestDelete(t *testing.T) { - cf := NewStore(file.NewStore()) - cf.Init() - cfInt := cf.(*cache) - defer cleanup(file.DefaultDatabase, cf) - - cf.Write(&store.Record{ - Key: "key1", - Value: []byte("foo"), - }) - recs, _ := cfInt.m.Read("key1") - assert.Len(t, recs, 1, "Expected a record in the memory store") - recs, _ = cfInt.b.Read("key1") - assert.Len(t, recs, 1, "Expected a record in the file store") - cf.Delete("key1") - - _, err := cfInt.m.Read("key1") - assert.Error(t, err, "Expected no records in memory store") - _, err = cfInt.b.Read("key1") - assert.Error(t, err, "Expected no records in file store") - -} - -func TestList(t *testing.T) { - cf := NewStore(file.NewStore()) - cf.Init() - cfInt := cf.(*cache) - defer cleanup(file.DefaultDatabase, cf) - - keys, err := cf.List() - assert.NoError(t, err) - assert.Len(t, keys, 0) - cfInt.b.Write(&store.Record{ - Key: "key1", - Value: []byte("foo"), - }) - - cfInt.b.Write(&store.Record{ - Key: "key2", - Value: []byte("foo"), - }) - keys, err = cf.List() - assert.NoError(t, err) - assert.Len(t, keys, 2) - -} diff --git a/store/cockroach/cockroach.go b/store/cockroach/cockroach.go deleted file mode 100644 index 6f30623b..00000000 --- a/store/cockroach/cockroach.go +++ /dev/null @@ -1,524 +0,0 @@ -// Package cockroach implements the cockroach store -package cockroach - -import ( - "database/sql" - "fmt" - "net/url" - "regexp" - "strings" - "sync" - "time" - - "github.com/lib/pq" - "github.com/pkg/errors" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/store" -) - -// DefaultDatabase is the namespace that the sql store -// will use if no namespace is provided. -var ( - DefaultDatabase = "micro" - DefaultTable = "micro" -) - -var ( - re = regexp.MustCompile("[^a-zA-Z0-9]+") - - statements = map[string]string{ - "list": "SELECT key, value, metadata, expiry FROM %s.%s WHERE key LIKE $1 ORDER BY key DESC LIMIT $2 OFFSET $3;", - "read": "SELECT key, value, metadata, expiry FROM %s.%s WHERE key = $1;", - "readMany": "SELECT key, value, metadata, expiry FROM %s.%s WHERE key LIKE $1;", - "readOffset": "SELECT key, value, metadata, expiry FROM %s.%s WHERE key LIKE $1 ORDER BY key DESC LIMIT $2 OFFSET $3;", - "write": "INSERT INTO %s.%s(key, value, metadata, expiry) VALUES ($1, $2::bytea, $3, $4) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, metadata = EXCLUDED.metadata, expiry = EXCLUDED.expiry;", - "delete": "DELETE FROM %s.%s WHERE key = $1;", - } -) - -type sqlStore struct { - options store.Options - db *sql.DB - - sync.RWMutex - // known databases - databases map[string]bool -} - -func (s *sqlStore) getDB(database, table string) (string, string) { - if len(database) == 0 { - if len(s.options.Database) > 0 { - database = s.options.Database - } else { - database = DefaultDatabase - } - } - - if len(table) == 0 { - if len(s.options.Table) > 0 { - table = s.options.Table - } else { - table = DefaultTable - } - } - - // store.namespace must only contain letters, numbers and underscores - database = re.ReplaceAllString(database, "_") - table = re.ReplaceAllString(table, "_") - - return database, table -} - -func (s *sqlStore) createDB(database, table string) error { - database, table = s.getDB(database, table) - - s.Lock() - defer s.Unlock() - - if _, ok := s.databases[database+":"+table]; ok { - return nil - } - - if err := s.initDB(database, table); err != nil { - return err - } - - s.databases[database+":"+table] = true - return nil -} - -func (s *sqlStore) initDB(database, table string) error { - if s.db == nil { - return errors.New("Database connection not initialised") - } - - // Create the namespace's database - _, err := s.db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s;", database)) - if err != nil { - return err - } - - _, err = s.db.Exec(fmt.Sprintf("SET DATABASE = %s;", database)) - if err != nil { - return errors.Wrap(err, "Couldn't set database") - } - - // Create a table for the namespace's prefix - _, err = s.db.Exec(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s - ( - key text NOT NULL, - value bytea, - metadata JSONB, - expiry timestamp with time zone, - CONSTRAINT %s_pkey PRIMARY KEY (key) - );`, table, table)) - if err != nil { - return errors.Wrap(err, "Couldn't create table") - } - - // Create Index - _, err = s.db.Exec(fmt.Sprintf(`CREATE INDEX IF NOT EXISTS "%s" ON %s.%s USING btree ("key");`, "key_index_"+table, database, table)) - if err != nil { - return err - } - - // Create Metadata Index - _, err = s.db.Exec(fmt.Sprintf(`CREATE INDEX IF NOT EXISTS "%s" ON %s.%s USING GIN ("metadata");`, "metadata_index_"+table, database, table)) - if err != nil { - return err - } - - return nil -} - -func (s *sqlStore) configure() error { - if len(s.options.Nodes) == 0 { - s.options.Nodes = []string{"postgresql://root@localhost:26257?sslmode=disable"} - } - - source := s.options.Nodes[0] - // check if it is a standard connection string eg: host=%s port=%d user=%s password=%s dbname=%s sslmode=disable - // if err is nil which means it would be a URL like postgre://xxxx?yy=zz - _, err := url.Parse(source) - if err != nil { - if !strings.Contains(source, " ") { - source = fmt.Sprintf("host=%s", source) - } - } - - // create source from first node - db, err := sql.Open("postgres", source) - if err != nil { - return err - } - - if err := db.Ping(); err != nil { - return err - } - - if s.db != nil { - s.db.Close() - } - - // save the values - s.db = db - - // get DB - database, table := s.getDB(s.options.Database, s.options.Table) - - // initialise the database - return s.initDB(database, table) -} - -func (s *sqlStore) prepare(database, table, query string) (*sql.Stmt, error) { - st, ok := statements[query] - if !ok { - return nil, errors.New("unsupported statement") - } - - // get DB - database, table = s.getDB(database, table) - - q := fmt.Sprintf(st, database, table) - stmt, err := s.db.Prepare(q) - if err != nil { - return nil, err - } - return stmt, nil -} - -func (s *sqlStore) Close() error { - if s.db != nil { - return s.db.Close() - } - return nil -} - -func (s *sqlStore) Init(opts ...store.Option) error { - for _, o := range opts { - o(&s.options) - } - // reconfigure - return s.configure() -} - -// List all the known records -func (s *sqlStore) List(opts ...store.ListOption) ([]string, error) { - var options store.ListOptions - for _, o := range opts { - o(&options) - } - - // create the db if not exists - if err := s.createDB(options.Database, options.Table); err != nil { - return nil, err - } - limit := sql.NullInt32{} - offset := 0 - pattern := "%" - if options.Prefix != "" || options.Suffix != "" { - if options.Prefix != "" { - pattern = options.Prefix + pattern - } - if options.Suffix != "" { - pattern += options.Suffix - } - } - if options.Offset > 0 { - offset = int(options.Offset) - } - if options.Limit > 0 { - limit = sql.NullInt32{Int32: int32(options.Limit), Valid: true} - } - - st, err := s.prepare(options.Database, options.Table, "list") - if err != nil { - return nil, err - } - defer st.Close() - - rows, err := st.Query(pattern, limit, offset) - if err != nil { - if err == sql.ErrNoRows { - return nil, nil - } - return nil, err - } - defer rows.Close() - records, err := s.rowsToRecords(rows) - if err != nil { - return nil, err - } - keys := make([]string, 0, len(records)) - for _, k := range records { - keys = append(keys, k.Key) - } - rowErr := rows.Close() - if rowErr != nil { - // transaction rollback or something - return keys, rowErr - } - if err := rows.Err(); err != nil { - return keys, err - } - return keys, nil -} - -// rowToRecord converts from sql.Row to a store.Record. If the record has expired it will issue a delete in a separate goroutine -func (s *sqlStore) rowToRecord(row *sql.Row) (*store.Record, error) { - var timehelper pq.NullTime - record := &store.Record{} - metadata := make(Metadata) - - if err := row.Scan(&record.Key, &record.Value, &metadata, &timehelper); err != nil { - if err == sql.ErrNoRows { - return record, store.ErrNotFound - } - return nil, err - } - - // set the metadata - record.Metadata = toMetadata(&metadata) - if timehelper.Valid { - if timehelper.Time.Before(time.Now()) { - // record has expired - go s.Delete(record.Key) - return nil, store.ErrNotFound - } - record.Expiry = time.Until(timehelper.Time) - - } - return record, nil -} - -// rowsToRecords converts from sql.Rows to []*store.Record. If a record has expired it will issue a delete in a separate goroutine -func (s *sqlStore) rowsToRecords(rows *sql.Rows) ([]*store.Record, error) { - var records []*store.Record - var timehelper pq.NullTime - - for rows.Next() { - record := &store.Record{} - metadata := make(Metadata) - - if err := rows.Scan(&record.Key, &record.Value, &metadata, &timehelper); err != nil { - return records, err - } - - // set the metadata - record.Metadata = toMetadata(&metadata) - - if timehelper.Valid { - if timehelper.Time.Before(time.Now()) { - // record has expired - go s.Delete(record.Key) - } else { - record.Expiry = time.Until(timehelper.Time) - records = append(records, record) - } - } else { - records = append(records, record) - } - } - return records, nil -} - -// Read a single key -func (s *sqlStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) { - var options store.ReadOptions - for _, o := range opts { - o(&options) - } - - // create the db if not exists - if err := s.createDB(options.Database, options.Table); err != nil { - return nil, err - } - - if options.Prefix || options.Suffix { - return s.read(key, options) - } - - st, err := s.prepare(options.Database, options.Table, "read") - if err != nil { - return nil, err - } - defer st.Close() - - row := st.QueryRow(key) - record, err := s.rowToRecord(row) - if err != nil { - return nil, err - } - var records []*store.Record - return append(records, record), nil -} - -// Read Many records -func (s *sqlStore) read(key string, options store.ReadOptions) ([]*store.Record, error) { - pattern := "%" - if options.Prefix { - pattern = key + pattern - } - if options.Suffix { - pattern += key - } - - var rows *sql.Rows - var st *sql.Stmt - var err error - - if options.Limit != 0 { - st, err = s.prepare(options.Database, options.Table, "readOffset") - if err != nil { - return nil, err - } - defer st.Close() - - rows, err = st.Query(pattern, options.Limit, options.Offset) - } else { - st, err = s.prepare(options.Database, options.Table, "readMany") - if err != nil { - return nil, err - } - defer st.Close() - - rows, err = st.Query(pattern) - } - if err != nil { - if err == sql.ErrNoRows { - return []*store.Record{}, nil - } - return []*store.Record{}, errors.Wrap(err, "sqlStore.read failed") - } - - defer rows.Close() - - records, err := s.rowsToRecords(rows) - if err != nil { - return nil, err - } - rowErr := rows.Close() - if rowErr != nil { - // transaction rollback or something - return records, rowErr - } - if err := rows.Err(); err != nil { - return records, err - } - - return records, nil -} - -// Write records -func (s *sqlStore) Write(r *store.Record, opts ...store.WriteOption) error { - var options store.WriteOptions - for _, o := range opts { - o(&options) - } - - // create the db if not exists - if err := s.createDB(options.Database, options.Table); err != nil { - return err - } - - st, err := s.prepare(options.Database, options.Table, "write") - if err != nil { - return err - } - defer st.Close() - - metadata := make(Metadata) - for k, v := range r.Metadata { - metadata[k] = v - } - - var expiry time.Time - // expiry from options takes precedence - if !options.Expiry.IsZero() { - expiry = options.Expiry - } else if r.Expiry != 0 { - expiry = time.Now().Add(r.Expiry) - } - if options.TTL != 0 { - expiry = time.Now().Add(options.TTL) - } - - if expiry.IsZero() { - _, err = st.Exec(r.Key, r.Value, metadata, nil) - } else { - _, err = st.Exec(r.Key, r.Value, metadata, expiry) - } - - if err != nil { - return errors.Wrap(err, "Couldn't insert record "+r.Key) - } - - return nil -} - -// Delete records with keys -func (s *sqlStore) Delete(key string, opts ...store.DeleteOption) error { - var options store.DeleteOptions - for _, o := range opts { - o(&options) - } - - // create the db if not exists - if err := s.createDB(options.Database, options.Table); err != nil { - return err - } - - st, err := s.prepare(options.Database, options.Table, "delete") - if err != nil { - return err - } - defer st.Close() - - result, err := st.Exec(key) - if err != nil { - return err - } - - _, err = result.RowsAffected() - if err != nil { - return err - } - - return nil -} - -func (s *sqlStore) Options() store.Options { - return s.options -} - -func (s *sqlStore) String() string { - return "cockroach" -} - -// NewStore returns a new micro Store backed by sql -func NewStore(opts ...store.Option) store.Store { - options := store.Options{ - Database: DefaultDatabase, - Table: DefaultTable, - } - - for _, o := range opts { - o(&options) - } - - // new store - s := new(sqlStore) - // set the options - s.options = options - // mark known databases - s.databases = make(map[string]bool) - // best-effort configure the store - if err := s.configure(); err != nil { - if logger.V(logger.ErrorLevel, logger.DefaultLogger) { - logger.Error("Error configuring store ", err) - } - } - - // return store - return s -} diff --git a/store/cockroach/cockroach_test.go b/store/cockroach/cockroach_test.go deleted file mode 100644 index 425f6859..00000000 --- a/store/cockroach/cockroach_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package cockroach - -import ( - "database/sql" - "fmt" - "os" - "testing" - "time" - - "github.com/kr/pretty" - "github.com/unistack-org/micro/v3/store" -) - -func TestSQL(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { - t.Skip() - } - connection := fmt.Sprintf( - "host=%s port=%d user=%s sslmode=disable dbname=%s", - "localhost", - 26257, - "root", - "test", - ) - db, err := sql.Open("postgres", connection) - if err != nil { - t.Fatal(err) - } - if err := db.Ping(); err != nil { - t.Skip("store/cockroach: can't connect to db") - } - db.Close() - - sqlStore := NewStore( - store.Database("testsql"), - store.Nodes(connection), - ) - - if err := sqlStore.Init(); err != nil { - t.Fatal(err) - } - - keys, err := sqlStore.List() - if err != nil { - t.Error(err) - } else { - t.Logf("%# v\n", pretty.Formatter(keys)) - } - - err = sqlStore.Write( - &store.Record{ - Key: "test", - Value: []byte("foo"), - }, - ) - if err != nil { - t.Error(err) - } - err = sqlStore.Write( - &store.Record{ - Key: "bar", - Value: []byte("baz"), - }, - ) - if err != nil { - t.Error(err) - } - err = sqlStore.Write( - &store.Record{ - Key: "qux", - Value: []byte("aasad"), - }, - ) - if err != nil { - t.Error(err) - } - err = sqlStore.Delete("qux") - if err != nil { - t.Error(err) - } - - err = sqlStore.Write(&store.Record{ - Key: "test", - Value: []byte("bar"), - Expiry: time.Second * 10, - }) - if err != nil { - t.Error(err) - } - - records, err := sqlStore.Read("test") - if err != nil { - t.Error(err) - } - t.Logf("%# v\n", pretty.Formatter(records)) - if string(records[0].Value) != "bar" { - t.Error("Expected bar, got ", string(records[0].Value)) - } - - time.Sleep(11 * time.Second) - _, err = sqlStore.Read("test") - switch err { - case nil: - t.Error("Key test should have expired") - case store.ErrNotFound: - break - default: - t.Error(err) - } - sqlStore.Delete("bar") - sqlStore.Write(&store.Record{Key: "aaa", Value: []byte("bbb"), Expiry: 10 * time.Second}) - sqlStore.Write(&store.Record{Key: "aaaa", Value: []byte("bbb"), Expiry: 10 * time.Second}) - sqlStore.Write(&store.Record{Key: "aaaaa", Value: []byte("bbb"), Expiry: 10 * time.Second}) - results, err := sqlStore.Read("a", store.ReadPrefix()) - if err != nil { - t.Error(err) - } - if len(results) != 3 { - t.Fatal("Results should have returned 3 records") - } - time.Sleep(10 * time.Second) - results, err = sqlStore.Read("a", store.ReadPrefix()) - if err != nil { - t.Error(err) - } - if len(results) != 0 { - t.Fatal("Results should have returned 0 records") - } - - sqlStore.Write(&store.Record{Key: "bbb", Value: []byte("bbb")}, store.WriteExpiry(time.Now().Add(10*time.Second))) - sqlStore.Write(&store.Record{Key: "bbbb", Value: []byte("bbb")}, store.WriteExpiry(time.Now().Add(10*time.Second))) - sqlStore.Write(&store.Record{Key: "bbbbb", Value: []byte("bbb")}, store.WriteExpiry(time.Now().Add(10*time.Second))) - results, err = sqlStore.Read("b", store.ReadPrefix()) - if err != nil { - t.Error(err) - } - if len(results) != 3 { - t.Fatalf("Results should have returned 3 records. Received %d", len(results)) - } - time.Sleep(10 * time.Second) - results, err = sqlStore.Read("b", store.ReadPrefix()) - if err != nil { - t.Error(err) - } - if len(results) != 0 { - t.Fatalf("Results should have returned 0 records. Received %d", len(results)) - } - - sqlStore.Write(&store.Record{Key: "ccc", Value: []byte("bbb")}, store.WriteTTL(10*time.Second)) - sqlStore.Write(&store.Record{Key: "cccc", Value: []byte("bbb")}, store.WriteTTL(10*time.Second)) - sqlStore.Write(&store.Record{Key: "ccccc", Value: []byte("bbb")}, store.WriteTTL(10*time.Second)) - results, err = sqlStore.Read("c", store.ReadPrefix()) - if err != nil { - t.Error(err) - } - if len(results) != 3 { - t.Fatalf("Results should have returned 3 records. Received %d", len(results)) - } - time.Sleep(10 * time.Second) - results, err = sqlStore.Read("c", store.ReadPrefix()) - if err != nil { - t.Error(err) - } - if len(results) != 0 { - t.Fatalf("Results should have returned 0 records. Received %d", len(results)) - } -} diff --git a/store/cockroach/metadata.go b/store/cockroach/metadata.go deleted file mode 100644 index 21bb284e..00000000 --- a/store/cockroach/metadata.go +++ /dev/null @@ -1,45 +0,0 @@ -package cockroach - -import ( - "database/sql/driver" - "encoding/json" - "errors" -) - -// https://github.com/upper/db/blob/master/postgresql/custom_types.go#L43 -type Metadata map[string]interface{} - -// Scan satisfies the sql.Scanner interface. -func (m *Metadata) Scan(src interface{}) error { - source, ok := src.([]byte) - if !ok { - return errors.New("Type assertion .([]byte) failed.") - } - - var i interface{} - err := json.Unmarshal(source, &i) - if err != nil { - return err - } - - *m, ok = i.(map[string]interface{}) - if !ok { - return errors.New("Type assertion .(map[string]interface{}) failed.") - } - - return nil -} - -// Value satisfies the driver.Valuer interface. -func (m Metadata) Value() (driver.Value, error) { - j, err := json.Marshal(m) - return j, err -} - -func toMetadata(m *Metadata) map[string]interface{} { - md := make(map[string]interface{}) - for k, v := range *m { - md[k] = v - } - return md -} diff --git a/store/file/file.go b/store/file/file.go deleted file mode 100644 index faa4462a..00000000 --- a/store/file/file.go +++ /dev/null @@ -1,395 +0,0 @@ -// Package local is a file system backed store -package file - -import ( - "encoding/json" - "os" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/unistack-org/micro/v3/store" - bolt "go.etcd.io/bbolt" -) - -var ( - // DefaultDatabase is the namespace that the bbolt store - // will use if no namespace is provided. - DefaultDatabase = "micro" - // DefaultTable when none is specified - DefaultTable = "micro" - // DefaultDir is the default directory for bbolt files - DefaultDir = filepath.Join(os.TempDir(), "micro", "store") - - // bucket used for data storage - dataBucket = "data" -) - -// NewStore returns a file store -func NewStore(opts ...store.Option) store.Store { - s := &fileStore{} - s.init(opts...) - return s -} - -type fileStore struct { - options store.Options - dir string -} - -// record stored by us -type record struct { - Key string - Value []byte - Metadata map[string]interface{} - ExpiresAt time.Time -} - -func key(database, table string) string { - return database + ":" + table -} - -func (m *fileStore) delete(db *bolt.DB, key string) error { - return db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(dataBucket)) - if b == nil { - return nil - } - return b.Delete([]byte(key)) - }) -} - -func (m *fileStore) init(opts ...store.Option) error { - for _, o := range opts { - o(&m.options) - } - - if m.options.Database == "" { - m.options.Database = DefaultDatabase - } - - if m.options.Table == "" { - // bbolt requires bucketname to not be empty - m.options.Table = DefaultTable - } - - // create a directory /tmp/micro - dir := filepath.Join(DefaultDir, m.options.Database) - // Ignoring this as the folder might exist. - // Reads/Writes updates will return with sensible error messages - // about the dir not existing in case this cannot create the path anyway - os.MkdirAll(dir, 0700) - - return nil -} - -func (f *fileStore) getDB(database, table string) (*bolt.DB, error) { - if len(database) == 0 { - database = f.options.Database - } - if len(table) == 0 { - table = f.options.Table - } - - // create a directory /tmp/micro - dir := filepath.Join(DefaultDir, database) - // create the database handle - fname := table + ".db" - // make the dir - os.MkdirAll(dir, 0700) - // database path - dbPath := filepath.Join(dir, fname) - - // create new db handle - // Bolt DB only allows one process to open the file R/W so make sure we're doing this under a lock - return bolt.Open(dbPath, 0700, &bolt.Options{Timeout: 5 * time.Second}) -} - -func (m *fileStore) list(db *bolt.DB, limit, offset uint) []string { - var allItems []string - - db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(dataBucket)) - // nothing to read - if b == nil { - return nil - } - - // @todo very inefficient - if err := b.ForEach(func(k, v []byte) error { - storedRecord := &record{} - - if err := json.Unmarshal(v, storedRecord); err != nil { - return err - } - - if !storedRecord.ExpiresAt.IsZero() { - if storedRecord.ExpiresAt.Before(time.Now()) { - return nil - } - } - - allItems = append(allItems, string(k)) - - return nil - }); err != nil { - return err - } - - return nil - }) - - allKeys := make([]string, len(allItems)) - - for i, k := range allItems { - allKeys[i] = k - } - - if limit != 0 || offset != 0 { - sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] }) - end := len(allKeys) - if limit > 0 { - calcLimit := int(offset + limit) - if calcLimit < end { - end = calcLimit - } - } - - if int(offset) >= end { - return nil - } - return allKeys[offset:end] - } - - return allKeys -} - -func (m *fileStore) get(db *bolt.DB, k string) (*store.Record, error) { - var value []byte - - db.View(func(tx *bolt.Tx) error { - // @todo this is still very experimental... - b := tx.Bucket([]byte(dataBucket)) - if b == nil { - return nil - } - - value = b.Get([]byte(k)) - return nil - }) - - if value == nil { - return nil, store.ErrNotFound - } - - storedRecord := &record{} - - if err := json.Unmarshal(value, storedRecord); err != nil { - return nil, err - } - - newRecord := &store.Record{} - newRecord.Key = storedRecord.Key - newRecord.Value = storedRecord.Value - newRecord.Metadata = make(map[string]interface{}) - - for k, v := range storedRecord.Metadata { - newRecord.Metadata[k] = v - } - - if !storedRecord.ExpiresAt.IsZero() { - if storedRecord.ExpiresAt.Before(time.Now()) { - return nil, store.ErrNotFound - } - newRecord.Expiry = time.Until(storedRecord.ExpiresAt) - } - - return newRecord, nil -} - -func (m *fileStore) set(db *bolt.DB, r *store.Record) error { - // copy the incoming record and then - // convert the expiry in to a hard timestamp - item := &record{} - item.Key = r.Key - item.Value = r.Value - item.Metadata = make(map[string]interface{}) - - if r.Expiry != 0 { - item.ExpiresAt = time.Now().Add(r.Expiry) - } - - for k, v := range r.Metadata { - item.Metadata[k] = v - } - - // marshal the data - data, _ := json.Marshal(item) - - return db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(dataBucket)) - if b == nil { - var err error - b, err = tx.CreateBucketIfNotExists([]byte(dataBucket)) - if err != nil { - return err - } - } - return b.Put([]byte(r.Key), data) - }) -} - -func (f *fileStore) Close() error { - return nil -} - -func (f *fileStore) Init(opts ...store.Option) error { - return f.init(opts...) -} - -func (m *fileStore) Delete(key string, opts ...store.DeleteOption) error { - var deleteOptions store.DeleteOptions - for _, o := range opts { - o(&deleteOptions) - } - - db, err := m.getDB(deleteOptions.Database, deleteOptions.Table) - if err != nil { - return err - } - defer db.Close() - - return m.delete(db, key) -} - -func (m *fileStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) { - var readOpts store.ReadOptions - for _, o := range opts { - o(&readOpts) - } - - db, err := m.getDB(readOpts.Database, readOpts.Table) - if err != nil { - return nil, err - } - defer db.Close() - - var keys []string - - // Handle Prefix / suffix - // TODO: do range scan here rather than listing all keys - if readOpts.Prefix || readOpts.Suffix { - // list the keys - k := m.list(db, readOpts.Limit, readOpts.Offset) - - // check for prefix and suffix - for _, v := range k { - if readOpts.Prefix && !strings.HasPrefix(v, key) { - continue - } - if readOpts.Suffix && !strings.HasSuffix(v, key) { - continue - } - keys = append(keys, v) - } - } else { - keys = []string{key} - } - - var results []*store.Record - - for _, k := range keys { - r, err := m.get(db, k) - if err != nil { - return results, err - } - results = append(results, r) - } - - return results, nil -} - -func (m *fileStore) Write(r *store.Record, opts ...store.WriteOption) error { - var writeOpts store.WriteOptions - for _, o := range opts { - o(&writeOpts) - } - - db, err := m.getDB(writeOpts.Database, writeOpts.Table) - if err != nil { - return err - } - defer db.Close() - - if len(opts) > 0 { - // Copy the record before applying options, or the incoming record will be mutated - newRecord := store.Record{} - newRecord.Key = r.Key - newRecord.Value = r.Value - newRecord.Metadata = make(map[string]interface{}) - newRecord.Expiry = r.Expiry - - if !writeOpts.Expiry.IsZero() { - newRecord.Expiry = time.Until(writeOpts.Expiry) - } - if writeOpts.TTL != 0 { - newRecord.Expiry = writeOpts.TTL - } - - for k, v := range r.Metadata { - newRecord.Metadata[k] = v - } - - return m.set(db, &newRecord) - } - - return m.set(db, r) -} - -func (m *fileStore) Options() store.Options { - return m.options -} - -func (m *fileStore) List(opts ...store.ListOption) ([]string, error) { - var listOptions store.ListOptions - - for _, o := range opts { - o(&listOptions) - } - - db, err := m.getDB(listOptions.Database, listOptions.Table) - if err != nil { - return nil, err - } - defer db.Close() - - // TODO apply prefix/suffix in range query - allKeys := m.list(db, listOptions.Limit, listOptions.Offset) - - if len(listOptions.Prefix) > 0 { - var prefixKeys []string - for _, k := range allKeys { - if strings.HasPrefix(k, listOptions.Prefix) { - prefixKeys = append(prefixKeys, k) - } - } - allKeys = prefixKeys - } - - if len(listOptions.Suffix) > 0 { - var suffixKeys []string - for _, k := range allKeys { - if strings.HasSuffix(k, listOptions.Suffix) { - suffixKeys = append(suffixKeys, k) - } - } - allKeys = suffixKeys - } - - return allKeys, nil -} - -func (m *fileStore) String() string { - return "file" -} diff --git a/store/file/file_test.go b/store/file/file_test.go deleted file mode 100644 index 9ff31c55..00000000 --- a/store/file/file_test.go +++ /dev/null @@ -1,268 +0,0 @@ -package file - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/davecgh/go-spew/spew" - "github.com/kr/pretty" - "github.com/unistack-org/micro/v3/store" -) - -func cleanup(db string, s store.Store) { - s.Close() - dir := filepath.Join(DefaultDir, db+"/") - os.RemoveAll(dir) -} - -func TestFileStoreReInit(t *testing.T) { - s := NewStore(store.Table("aaa")) - defer cleanup(DefaultDatabase, s) - s.Init(store.Table("bbb")) - if s.Options().Table != "bbb" { - t.Error("Init didn't reinitialise the store") - } -} - -func TestFileStoreBasic(t *testing.T) { - s := NewStore() - defer cleanup(DefaultDatabase, s) - fileTest(s, t) -} - -func TestFileStoreTable(t *testing.T) { - s := NewStore(store.Table("testTable")) - defer cleanup(DefaultDatabase, s) - fileTest(s, t) -} - -func TestFileStoreDatabase(t *testing.T) { - s := NewStore(store.Database("testdb")) - defer cleanup("testdb", s) - fileTest(s, t) -} - -func TestFileStoreDatabaseTable(t *testing.T) { - s := NewStore(store.Table("testTable"), store.Database("testdb")) - defer cleanup("testdb", s) - fileTest(s, t) -} - -func fileTest(s store.Store, t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Options %s %v\n", s.String(), s.Options()) - } - // Read and Write an expiring Record - if err := s.Write(&store.Record{ - Key: "Hello", - Value: []byte("World"), - Expiry: time.Millisecond * 150, - }); err != nil { - t.Error(err) - } - - if r, err := s.Read("Hello"); err != nil { - t.Fatal(err) - } else { - if len(r) != 1 { - t.Error("Read returned multiple records") - } - if r[0].Key != "Hello" { - t.Errorf("Expected %s, got %s", "Hello", r[0].Key) - } - if string(r[0].Value) != "World" { - t.Errorf("Expected %s, got %s", "World", r[0].Value) - } - } - - // wait for expiry - time.Sleep(time.Millisecond * 200) - - if _, err := s.Read("Hello"); err != store.ErrNotFound { - t.Errorf("Expected %# v, got %# v", store.ErrNotFound, err) - } - - // Write 3 records with various expiry and get with Table - records := []*store.Record{ - &store.Record{ - Key: "foo", - Value: []byte("foofoo"), - }, - &store.Record{ - Key: "foobar", - Value: []byte("foobarfoobar"), - Expiry: time.Millisecond * 100, - }, - } - - for _, r := range records { - if err := s.Write(r); err != nil { - t.Errorf("Couldn't write k: %s, v: %# v (%s)", r.Key, pretty.Formatter(r.Value), err) - } - } - - if results, err := s.Read("foo", store.ReadPrefix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else { - if len(results) != 2 { - t.Errorf("Expected 2 items, got %d", len(results)) - //t.Logf("Table test: %v\n", spew.Sdump(results)) - } - } - - // wait for the expiry - time.Sleep(time.Millisecond * 200) - - if results, err := s.Read("foo", store.ReadPrefix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else if len(results) != 1 { - t.Errorf("Expected 1 item, got %d", len(results)) - //t.Logf("Table test: %v\n", spew.Sdump(results)) - } - - if err := s.Delete("foo"); err != nil { - t.Errorf("Delete failed (%v)", err) - } - - if results, err := s.Read("foo"); err != store.ErrNotFound { - t.Errorf("Expected read failure read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else { - if len(results) != 0 { - t.Errorf("Expected 0 items, got %d (%# v)", len(results), spew.Sdump(results)) - } - } - - // Write 3 records with various expiry and get with Suffix - records = []*store.Record{ - &store.Record{ - Key: "foo", - Value: []byte("foofoo"), - }, - &store.Record{ - Key: "barfoo", - Value: []byte("barfoobarfoo"), - - Expiry: time.Millisecond * 100, - }, - &store.Record{ - Key: "bazbarfoo", - Value: []byte("bazbarfoobazbarfoo"), - Expiry: 2 * time.Millisecond * 100, - }, - } - for _, r := range records { - if err := s.Write(r); err != nil { - t.Errorf("Couldn't write k: %s, v: %# v (%s)", r.Key, pretty.Formatter(r.Value), err) - } - } - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else { - if len(results) != 3 { - t.Errorf("Expected 3 items, got %d", len(results)) - //t.Logf("Table test: %v\n", spew.Sdump(results)) - } - - } - time.Sleep(time.Millisecond * 100) - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else { - if len(results) != 2 { - t.Errorf("Expected 2 items, got %d", len(results)) - //t.Logf("Table test: %v\n", spew.Sdump(results)) - } - - } - time.Sleep(time.Millisecond * 100) - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else { - if len(results) != 1 { - t.Errorf("Expected 1 item, got %d", len(results)) - // t.Logf("Table test: %# v\n", spew.Sdump(results)) - } - } - if err := s.Delete("foo"); err != nil { - t.Errorf("Delete failed (%v)", err) - } - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", spew.Sdump(results), err) - } else { - if len(results) != 0 { - t.Errorf("Expected 0 items, got %d (%# v)", len(results), spew.Sdump(results)) - } - } - - // Test Table, Suffix and WriteOptions - if err := s.Write(&store.Record{ - Key: "foofoobarbar", - Value: []byte("something"), - }, store.WriteTTL(time.Millisecond*100)); err != nil { - t.Error(err) - } - if err := s.Write(&store.Record{ - Key: "foofoo", - Value: []byte("something"), - }, store.WriteExpiry(time.Now().Add(time.Millisecond*100))); err != nil { - t.Error(err) - } - if err := s.Write(&store.Record{ - Key: "barbar", - Value: []byte("something"), - // TTL has higher precedence than expiry - }, store.WriteExpiry(time.Now().Add(time.Hour)), store.WriteTTL(time.Millisecond*100)); err != nil { - t.Error(err) - } - - if results, err := s.Read("foo", store.ReadPrefix(), store.ReadSuffix()); err != nil { - t.Error(err) - } else { - if len(results) != 1 { - t.Errorf("Expected 1 results, got %d: %# v", len(results), spew.Sdump(results)) - } - } - - time.Sleep(time.Millisecond * 100) - - if results, err := s.List(); err != nil { - t.Errorf("List failed: %s", err) - } else { - if len(results) != 0 { - t.Errorf("Expiry options were not effective, results :%v", spew.Sdump(results)) - } - } - - // write the following records - for i := 0; i < 10; i++ { - s.Write(&store.Record{ - Key: fmt.Sprintf("a%d", i), - Value: []byte{}, - }) - } - - // read back a few records - if results, err := s.Read("a", store.ReadLimit(5), store.ReadPrefix()); err != nil { - t.Error(err) - } else { - if len(results) != 5 { - t.Fatal("Expected 5 results, got ", len(results)) - } - if !strings.HasPrefix(results[0].Key, "a") { - t.Fatalf("Expected a prefix, got %s", results[0].Key) - } - } - - // read the rest back - if results, err := s.Read("a", store.ReadLimit(30), store.ReadOffset(5), store.ReadPrefix()); err != nil { - t.Fatal(err) - } else { - if len(results) != 5 { - t.Fatal("Expected 5 results, got ", len(results)) - } - } -} diff --git a/store/memory/memory.go b/store/memory/memory.go deleted file mode 100644 index fab5a333..00000000 --- a/store/memory/memory.go +++ /dev/null @@ -1,299 +0,0 @@ -// Package memory is a in-memory store store -package memory - -import ( - "path/filepath" - "sort" - "strings" - "time" - - "github.com/patrickmn/go-cache" - "github.com/pkg/errors" - "github.com/unistack-org/micro/v3/store" -) - -// NewStore returns a memory store -func NewStore(opts ...store.Option) store.Store { - s := &memoryStore{ - options: store.Options{ - Database: "micro", - Table: "micro", - }, - store: cache.New(cache.NoExpiration, 5*time.Minute), - } - for _, o := range opts { - o(&s.options) - } - return s -} - -type memoryStore struct { - options store.Options - - store *cache.Cache -} - -type storeRecord struct { - key string - value []byte - metadata map[string]interface{} - expiresAt time.Time -} - -func (m *memoryStore) key(prefix, key string) string { - return filepath.Join(prefix, key) -} - -func (m *memoryStore) prefix(database, table string) string { - if len(database) == 0 { - database = m.options.Database - } - if len(table) == 0 { - table = m.options.Table - } - return filepath.Join(database, table) -} - -func (m *memoryStore) get(prefix, key string) (*store.Record, error) { - key = m.key(prefix, key) - - var storedRecord *storeRecord - r, found := m.store.Get(key) - if !found { - return nil, store.ErrNotFound - } - - storedRecord, ok := r.(*storeRecord) - if !ok { - return nil, errors.New("Retrieved a non *storeRecord from the cache") - } - - // Copy the record on the way out - newRecord := &store.Record{} - newRecord.Key = strings.TrimPrefix(storedRecord.key, prefix+"/") - newRecord.Value = make([]byte, len(storedRecord.value)) - newRecord.Metadata = make(map[string]interface{}) - - // copy the value into the new record - copy(newRecord.Value, storedRecord.value) - - // check if we need to set the expiry - if !storedRecord.expiresAt.IsZero() { - newRecord.Expiry = time.Until(storedRecord.expiresAt) - } - - // copy in the metadata - for k, v := range storedRecord.metadata { - newRecord.Metadata[k] = v - } - - return newRecord, nil -} - -func (m *memoryStore) set(prefix string, r *store.Record) { - key := m.key(prefix, r.Key) - - // copy the incoming record and then - // convert the expiry in to a hard timestamp - i := &storeRecord{} - i.key = r.Key - i.value = make([]byte, len(r.Value)) - i.metadata = make(map[string]interface{}) - - // copy the the value - copy(i.value, r.Value) - - // set the expiry - if r.Expiry != 0 { - i.expiresAt = time.Now().Add(r.Expiry) - } - - // set the metadata - for k, v := range r.Metadata { - i.metadata[k] = v - } - - m.store.Set(key, i, r.Expiry) -} - -func (m *memoryStore) delete(prefix, key string) { - key = m.key(prefix, key) - m.store.Delete(key) -} - -func (m *memoryStore) list(prefix string, limit, offset uint) []string { - allItems := m.store.Items() - allKeys := make([]string, len(allItems)) - i := 0 - - for k := range allItems { - if !strings.HasPrefix(k, prefix+"/") { - continue - } - allKeys[i] = strings.TrimPrefix(k, prefix+"/") - i++ - } - - if limit != 0 || offset != 0 { - sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] }) - sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] }) - end := len(allKeys) - if limit > 0 { - calcLimit := int(offset + limit) - if calcLimit < end { - end = calcLimit - } - } - - if int(offset) >= end { - return nil - } - return allKeys[offset:end] - } - - return allKeys -} - -func (m *memoryStore) Close() error { - m.store.Flush() - return nil -} - -func (m *memoryStore) Init(opts ...store.Option) error { - for _, o := range opts { - o(&m.options) - } - return nil -} - -func (m *memoryStore) String() string { - return "memory" -} - -func (m *memoryStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) { - readOpts := store.ReadOptions{} - for _, o := range opts { - o(&readOpts) - } - - prefix := m.prefix(readOpts.Database, readOpts.Table) - - var keys []string - - // Handle Prefix / suffix - if readOpts.Prefix || readOpts.Suffix { - k := m.list(prefix, readOpts.Limit, readOpts.Offset) - - for _, kk := range k { - if readOpts.Prefix && !strings.HasPrefix(kk, key) { - continue - } - - if readOpts.Suffix && !strings.HasSuffix(kk, key) { - continue - } - - keys = append(keys, kk) - } - } else { - keys = []string{key} - } - - var results []*store.Record - - for _, k := range keys { - r, err := m.get(prefix, k) - if err != nil { - return results, err - } - results = append(results, r) - } - - return results, nil -} - -func (m *memoryStore) Write(r *store.Record, opts ...store.WriteOption) error { - writeOpts := store.WriteOptions{} - for _, o := range opts { - o(&writeOpts) - } - - prefix := m.prefix(writeOpts.Database, writeOpts.Table) - - if len(opts) > 0 { - // Copy the record before applying options, or the incoming record will be mutated - newRecord := store.Record{} - newRecord.Key = r.Key - newRecord.Value = make([]byte, len(r.Value)) - newRecord.Metadata = make(map[string]interface{}) - copy(newRecord.Value, r.Value) - newRecord.Expiry = r.Expiry - - if !writeOpts.Expiry.IsZero() { - newRecord.Expiry = time.Until(writeOpts.Expiry) - } - if writeOpts.TTL != 0 { - newRecord.Expiry = writeOpts.TTL - } - - for k, v := range r.Metadata { - newRecord.Metadata[k] = v - } - - m.set(prefix, &newRecord) - return nil - } - - // set - m.set(prefix, r) - - return nil -} - -func (m *memoryStore) Delete(key string, opts ...store.DeleteOption) error { - deleteOptions := store.DeleteOptions{} - for _, o := range opts { - o(&deleteOptions) - } - - prefix := m.prefix(deleteOptions.Database, deleteOptions.Table) - m.delete(prefix, key) - return nil -} - -func (m *memoryStore) Options() store.Options { - return m.options -} - -func (m *memoryStore) List(opts ...store.ListOption) ([]string, error) { - listOptions := store.ListOptions{} - - for _, o := range opts { - o(&listOptions) - } - - prefix := m.prefix(listOptions.Database, listOptions.Table) - keys := m.list(prefix, listOptions.Limit, listOptions.Offset) - - if len(listOptions.Prefix) > 0 { - var prefixKeys []string - for _, k := range keys { - if strings.HasPrefix(k, listOptions.Prefix) { - prefixKeys = append(prefixKeys, k) - } - } - keys = prefixKeys - } - - if len(listOptions.Suffix) > 0 { - var suffixKeys []string - for _, k := range keys { - if strings.HasSuffix(k, listOptions.Suffix) { - suffixKeys = append(suffixKeys, k) - } - } - keys = suffixKeys - } - - return keys, nil -} diff --git a/store/memory/memory_test.go b/store/memory/memory_test.go deleted file mode 100644 index 3fe1f42e..00000000 --- a/store/memory/memory_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package memory - -import ( - "fmt" - "os" - "testing" - "time" - - "github.com/kr/pretty" - "github.com/unistack-org/micro/v3/store" -) - -func TestMemoryReInit(t *testing.T) { - s := NewStore(store.Table("aaa")) - s.Init(store.Table("")) - if len(s.Options().Table) > 0 { - t.Error("Init didn't reinitialise the store") - } -} - -func TestMemoryBasic(t *testing.T) { - s := NewStore() - s.Init() - basictest(s, t) -} - -func TestMemoryPrefix(t *testing.T) { - s := NewStore() - s.Init(store.Table("some-prefix")) - basictest(s, t) -} - -func TestMemoryNamespace(t *testing.T) { - s := NewStore() - s.Init(store.Database("some-namespace")) - basictest(s, t) -} - -func TestMemoryNamespacePrefix(t *testing.T) { - s := NewStore() - s.Init(store.Table("some-prefix"), store.Database("some-namespace")) - basictest(s, t) -} - -func basictest(s store.Store, t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Testing store %s, with options %# v\n", s.String(), pretty.Formatter(s.Options())) - } - // Read and Write an expiring Record - if err := s.Write(&store.Record{ - Key: "Hello", - Value: []byte("World"), - Expiry: time.Millisecond * 100, - }); err != nil { - t.Error(err) - } - if r, err := s.Read("Hello"); err != nil { - t.Error(err) - } else { - if len(r) != 1 { - t.Error("Read returned multiple records") - } - if r[0].Key != "Hello" { - t.Errorf("Expected %s, got %s", "Hello", r[0].Key) - } - if string(r[0].Value) != "World" { - t.Errorf("Expected %s, got %s", "World", r[0].Value) - } - } - time.Sleep(time.Millisecond * 200) - if _, err := s.Read("Hello"); err != store.ErrNotFound { - t.Errorf("Expected %# v, got %# v", store.ErrNotFound, err) - } - - // Write 3 records with various expiry and get with prefix - records := []*store.Record{ - &store.Record{ - Key: "foo", - Value: []byte("foofoo"), - }, - &store.Record{ - Key: "foobar", - Value: []byte("foobarfoobar"), - Expiry: time.Millisecond * 100, - }, - &store.Record{ - Key: "foobarbaz", - Value: []byte("foobarbazfoobarbaz"), - Expiry: 2 * time.Millisecond * 100, - }, - } - for _, r := range records { - if err := s.Write(r); err != nil { - t.Errorf("Couldn't write k: %s, v: %# v (%s)", r.Key, pretty.Formatter(r.Value), err) - } - } - if results, err := s.Read("foo", store.ReadPrefix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 3 { - t.Errorf("Expected 3 items, got %d", len(results)) - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Prefix test: %v\n", pretty.Formatter(results)) - } - } - time.Sleep(time.Millisecond * 100) - if results, err := s.Read("foo", store.ReadPrefix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 2 { - t.Errorf("Expected 2 items, got %d", len(results)) - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Prefix test: %v\n", pretty.Formatter(results)) - } - } - time.Sleep(time.Millisecond * 100) - if results, err := s.Read("foo", store.ReadPrefix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 1 { - t.Errorf("Expected 1 item, got %d", len(results)) - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Prefix test: %# v\n", pretty.Formatter(results)) - } - } - if err := s.Delete("foo", func(d *store.DeleteOptions) {}); err != nil { - t.Errorf("Delete failed (%v)", err) - } - if results, err := s.Read("foo", store.ReadPrefix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 0 { - t.Errorf("Expected 0 items, got %d (%# v)", len(results), pretty.Formatter(results)) - } - } - - // Write 3 records with various expiry and get with Suffix - records = []*store.Record{ - &store.Record{ - Key: "foo", - Value: []byte("foofoo"), - }, - &store.Record{ - Key: "barfoo", - Value: []byte("barfoobarfoo"), - Expiry: time.Millisecond * 100, - }, - &store.Record{ - Key: "bazbarfoo", - Value: []byte("bazbarfoobazbarfoo"), - Expiry: 2 * time.Millisecond * 100, - }, - } - for _, r := range records { - if err := s.Write(r); err != nil { - t.Errorf("Couldn't write k: %s, v: %# v (%s)", r.Key, pretty.Formatter(r.Value), err) - } - } - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 3 { - t.Errorf("Expected 3 items, got %d", len(results)) - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Prefix test: %v\n", pretty.Formatter(results)) - } - } - time.Sleep(time.Millisecond * 100) - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 2 { - t.Errorf("Expected 2 items, got %d", len(results)) - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Prefix test: %v\n", pretty.Formatter(results)) - } - } - time.Sleep(time.Millisecond * 100) - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 1 { - t.Errorf("Expected 1 item, got %d", len(results)) - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Prefix test: %# v\n", pretty.Formatter(results)) - } - } - if err := s.Delete("foo"); err != nil { - t.Errorf("Delete failed (%v)", err) - } - if results, err := s.Read("foo", store.ReadSuffix()); err != nil { - t.Errorf("Couldn't read all \"foo\" keys, got %# v (%s)", pretty.Formatter(results), err) - } else { - if len(results) != 0 { - t.Errorf("Expected 0 items, got %d (%# v)", len(results), pretty.Formatter(results)) - } - } - - // Test Prefix, Suffix and WriteOptions - if err := s.Write(&store.Record{ - Key: "foofoobarbar", - Value: []byte("something"), - }, store.WriteTTL(time.Millisecond*100)); err != nil { - t.Error(err) - } - if err := s.Write(&store.Record{ - Key: "foofoo", - Value: []byte("something"), - }, store.WriteExpiry(time.Now().Add(time.Millisecond*100))); err != nil { - t.Error(err) - } - if err := s.Write(&store.Record{ - Key: "barbar", - Value: []byte("something"), - // TTL has higher precedence than expiry - }, store.WriteExpiry(time.Now().Add(time.Hour)), store.WriteTTL(time.Millisecond*100)); err != nil { - t.Error(err) - } - if results, err := s.Read("foo", store.ReadPrefix(), store.ReadSuffix()); err != nil { - t.Error(err) - } else { - if len(results) != 1 { - t.Errorf("Expected 1 results, got %d: %# v", len(results), pretty.Formatter(results)) - } - } - time.Sleep(time.Millisecond * 100) - if results, err := s.List(); err != nil { - t.Errorf("List failed: %s", err) - } else { - if len(results) != 0 { - t.Error("Expiry options were not effective") - } - } - s.Write(&store.Record{Key: "a", Value: []byte("a")}) - s.Write(&store.Record{Key: "aa", Value: []byte("aa")}) - s.Write(&store.Record{Key: "aaa", Value: []byte("aaa")}) - if results, err := s.Read("b", store.ReadPrefix()); err != nil { - t.Error(err) - } else { - if len(results) != 0 { - t.Errorf("Expected 0 results, got %d", len(results)) - } - } - - s.Close() // reset the store - for i := 0; i < 10; i++ { - s.Write(&store.Record{ - Key: fmt.Sprintf("a%d", i), - Value: []byte{}, - }) - } - if results, err := s.Read("a", store.ReadLimit(5), store.ReadPrefix()); err != nil { - t.Error(err) - } else { - if len(results) != 5 { - t.Fatal("Expected 5 results, got ", len(results)) - } - if results[0].Key != "a0" { - t.Fatalf("Expected a0, got %s", results[0].Key) - } - if results[4].Key != "a4" { - t.Fatalf("Expected a4, got %s", results[4].Key) - } - } - if results, err := s.Read("a", store.ReadLimit(30), store.ReadOffset(5), store.ReadPrefix()); err != nil { - t.Error(err) - } else { - if len(results) != 5 { - t.Error("Expected 5 results, got ", len(results)) - } - } -} diff --git a/store/test/store_test.go b/store/test/store_test.go index ce8cf07f..fa0ff29b 100644 --- a/store/test/store_test.go +++ b/store/test/store_test.go @@ -1,3 +1,5 @@ +// +build ignore + // Package test provides a way to run tests against all the various implementations of the Store interface. // It can't live in the store package itself because of circular import issues package test @@ -47,7 +49,7 @@ func cacheCleanup(db string, s store.Store) { } func TestStoreReInit(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { t.Skip() } @@ -73,7 +75,7 @@ func TestStoreReInit(t *testing.T) { } func TestStoreBasic(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { t.Skip() } @@ -97,7 +99,7 @@ func TestStoreBasic(t *testing.T) { } func TestStoreTable(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { t.Skip() } @@ -120,7 +122,7 @@ func TestStoreTable(t *testing.T) { } func TestStoreDatabase(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { t.Skip() } @@ -143,7 +145,7 @@ func TestStoreDatabase(t *testing.T) { } func TestStoreDatabaseTable(t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) > 0 { + if len(os.Getenv("INTEGRATION_TESTS")) > 0 { t.Skip() } @@ -166,7 +168,7 @@ func TestStoreDatabaseTable(t *testing.T) { } func runStoreTest(s store.Store, t *testing.T) { - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { + if len(os.Getenv("INTEGRATION_TESTS")) == 0 { t.Logf("Options %s %v\n", s.String(), s.Options()) } diff --git a/sync/etcd/etcd.go b/sync/etcd/etcd.go deleted file mode 100644 index 513b96c7..00000000 --- a/sync/etcd/etcd.go +++ /dev/null @@ -1,178 +0,0 @@ -// Package etcd is an etcd implementation of lock -package etcd - -import ( - "context" - "errors" - "log" - "path" - "strings" - gosync "sync" - - client "github.com/coreos/etcd/clientv3" - cc "github.com/coreos/etcd/clientv3/concurrency" - "github.com/unistack-org/micro/v3/sync" -) - -type etcdSync struct { - options sync.Options - path string - client *client.Client - - mtx gosync.Mutex - locks map[string]*etcdLock -} - -type etcdLock struct { - s *cc.Session - m *cc.Mutex -} - -type etcdLeader struct { - opts sync.LeaderOptions - e *cc.Election - id string -} - -func (e *etcdSync) Leader(id string, opts ...sync.LeaderOption) (sync.Leader, error) { - var options sync.LeaderOptions - for _, o := range opts { - o(&options) - } - - // make path - path := path.Join(e.path, strings.Replace(e.options.Prefix+id, "/", "-", -1)) - - s, err := cc.NewSession(e.client) - if err != nil { - return nil, err - } - - l := cc.NewElection(s, path) - - if err := l.Campaign(context.TODO(), id); err != nil { - return nil, err - } - - return &etcdLeader{ - opts: options, - e: l, - id: id, - }, nil -} - -func (e *etcdLeader) Status() chan bool { - ch := make(chan bool, 1) - ech := e.e.Observe(context.Background()) - - go func() { - for r := range ech { - if string(r.Kvs[0].Value) != e.id { - ch <- true - close(ch) - return - } - } - }() - - return ch -} - -func (e *etcdLeader) Resign() error { - return e.e.Resign(context.Background()) -} - -func (e *etcdSync) Init(opts ...sync.Option) error { - for _, o := range opts { - o(&e.options) - } - return nil -} - -func (e *etcdSync) Options() sync.Options { - return e.options -} - -func (e *etcdSync) Lock(id string, opts ...sync.LockOption) error { - var options sync.LockOptions - for _, o := range opts { - o(&options) - } - - // make path - path := path.Join(e.path, strings.Replace(e.options.Prefix+id, "/", "-", -1)) - - var sopts []cc.SessionOption - if options.TTL > 0 { - sopts = append(sopts, cc.WithTTL(int(options.TTL.Seconds()))) - } - - s, err := cc.NewSession(e.client, sopts...) - if err != nil { - return err - } - - m := cc.NewMutex(s, path) - - if err := m.Lock(context.TODO()); err != nil { - return err - } - - e.mtx.Lock() - e.locks[id] = &etcdLock{ - s: s, - m: m, - } - e.mtx.Unlock() - return nil -} - -func (e *etcdSync) Unlock(id string) error { - e.mtx.Lock() - defer e.mtx.Unlock() - v, ok := e.locks[id] - if !ok { - return errors.New("lock not found") - } - err := v.m.Unlock(context.Background()) - delete(e.locks, id) - return err -} - -func (e *etcdSync) String() string { - return "etcd" -} - -func NewSync(opts ...sync.Option) sync.Sync { - var options sync.Options - for _, o := range opts { - o(&options) - } - - var endpoints []string - - for _, addr := range options.Nodes { - if len(addr) > 0 { - endpoints = append(endpoints, addr) - } - } - - if len(endpoints) == 0 { - endpoints = []string{"http://127.0.0.1:2379"} - } - - // TODO: parse addresses - c, err := client.New(client.Config{ - Endpoints: endpoints, - }) - if err != nil { - log.Fatal(err) - } - - return &etcdSync{ - path: "/micro/sync", - client: c, - options: options, - locks: make(map[string]*etcdLock), - } -} diff --git a/sync/memory/memory.go b/sync/memory/memory.go deleted file mode 100644 index c097fc7b..00000000 --- a/sync/memory/memory.go +++ /dev/null @@ -1,202 +0,0 @@ -// Package memory provides a sync.Mutex implementation of the lock for local use -package memory - -import ( - gosync "sync" - "time" - - "github.com/unistack-org/micro/v3/sync" -) - -type memorySync struct { - options sync.Options - - mtx gosync.RWMutex - locks map[string]*memoryLock -} - -type memoryLock struct { - id string - time time.Time - ttl time.Duration - release chan bool -} - -type memoryLeader struct { - opts sync.LeaderOptions - id string - resign func(id string) error - status chan bool -} - -func (m *memoryLeader) Resign() error { - return m.resign(m.id) -} - -func (m *memoryLeader) Status() chan bool { - return m.status -} - -func (m *memorySync) Leader(id string, opts ...sync.LeaderOption) (sync.Leader, error) { - var once gosync.Once - var options sync.LeaderOptions - for _, o := range opts { - o(&options) - } - - // acquire a lock for the id - if err := m.Lock(id); err != nil { - return nil, err - } - - // return the leader - return &memoryLeader{ - opts: options, - id: id, - resign: func(id string) error { - once.Do(func() { - m.Unlock(id) - }) - return nil - }, - // TODO: signal when Unlock is called - status: make(chan bool, 1), - }, nil -} - -func (m *memorySync) Init(opts ...sync.Option) error { - for _, o := range opts { - o(&m.options) - } - return nil -} - -func (m *memorySync) Options() sync.Options { - return m.options -} - -func (m *memorySync) Lock(id string, opts ...sync.LockOption) error { - // lock our access - m.mtx.Lock() - - var options sync.LockOptions - for _, o := range opts { - o(&options) - } - - lk, ok := m.locks[id] - if !ok { - m.locks[id] = &memoryLock{ - id: id, - time: time.Now(), - ttl: options.TTL, - release: make(chan bool), - } - // unlock - m.mtx.Unlock() - return nil - } - - m.mtx.Unlock() - - // set wait time - var wait <-chan time.Time - var ttl <-chan time.Time - - // decide if we should wait - if options.Wait > time.Duration(0) { - wait = time.After(options.Wait) - } - - // check the ttl of the lock - if lk.ttl > time.Duration(0) { - // time lived for the lock - live := time.Since(lk.time) - - // set a timer for the leftover ttl - if live > lk.ttl { - // release the lock if it expired - _ = m.Unlock(id) - } else { - ttl = time.After(live) - } - } - -lockLoop: - for { - // wait for the lock to be released - select { - case <-lk.release: - m.mtx.Lock() - - // someone locked before us - lk, ok = m.locks[id] - if ok { - m.mtx.Unlock() - continue - } - - // got chance to lock - m.locks[id] = &memoryLock{ - id: id, - time: time.Now(), - ttl: options.TTL, - release: make(chan bool), - } - - m.mtx.Unlock() - - break lockLoop - case <-ttl: - // ttl exceeded - _ = m.Unlock(id) - // TODO: check the ttl again above - ttl = nil - // try acquire - continue - case <-wait: - return sync.ErrLockTimeout - } - } - - return nil -} - -func (m *memorySync) Unlock(id string) error { - m.mtx.Lock() - defer m.mtx.Unlock() - - lk, ok := m.locks[id] - // no lock exists - if !ok { - return nil - } - - // delete the lock - delete(m.locks, id) - - select { - case <-lk.release: - return nil - default: - close(lk.release) - } - - return nil -} - -func (m *memorySync) String() string { - return "memory" -} - -func NewSync(opts ...sync.Option) sync.Sync { - var options sync.Options - for _, o := range opts { - o(&options) - } - - return &memorySync{ - options: options, - locks: make(map[string]*memoryLock), - } -} diff --git a/transport/grpc/grpc.go b/transport/grpc/grpc.go deleted file mode 100644 index b3ee5cff..00000000 --- a/transport/grpc/grpc.go +++ /dev/null @@ -1,176 +0,0 @@ -// Package grpc provides a grpc transport -package grpc - -import ( - "context" - "crypto/tls" - "net" - - "github.com/unistack-org/micro/v3/transport" - maddr "github.com/unistack-org/micro/v3/util/addr" - mnet "github.com/unistack-org/micro/v3/util/net" - mls "github.com/unistack-org/micro/v3/util/tls" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - - pb "github.com/unistack-org/micro/v3/transport/grpc/proto" -) - -type grpcTransport struct { - opts transport.Options -} - -type grpcTransportListener struct { - listener net.Listener - secure bool - tls *tls.Config -} - -func getTLSConfig(addr string) (*tls.Config, error) { - hosts := []string{addr} - - // check if its a valid host:port - if host, _, err := net.SplitHostPort(addr); err == nil { - if len(host) == 0 { - hosts = maddr.IPs() - } else { - hosts = []string{host} - } - } - - // generate a certificate - cert, err := mls.Certificate(hosts...) - if err != nil { - return nil, err - } - - return &tls.Config{Certificates: []tls.Certificate{cert}}, nil -} - -func (t *grpcTransportListener) Addr() string { - return t.listener.Addr().String() -} - -func (t *grpcTransportListener) Close() error { - return t.listener.Close() -} - -func (t *grpcTransportListener) Accept(fn func(transport.Socket)) error { - var opts []grpc.ServerOption - - // setup tls if specified - if t.secure || t.tls != nil { - config := t.tls - if config == nil { - var err error - addr := t.listener.Addr().String() - config, err = getTLSConfig(addr) - if err != nil { - return err - } - } - - creds := credentials.NewTLS(config) - opts = append(opts, grpc.Creds(creds)) - } - - // new service - srv := grpc.NewServer(opts...) - - // register service - pb.RegisterTransportServer(srv, µTransport{addr: t.listener.Addr().String(), fn: fn}) - - // start serving - return srv.Serve(t.listener) -} - -func (t *grpcTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { - dopts := transport.DialOptions{ - Timeout: transport.DefaultDialTimeout, - } - - for _, opt := range opts { - opt(&dopts) - } - - options := []grpc.DialOption{} - - if t.opts.Secure || t.opts.TLSConfig != nil { - config := t.opts.TLSConfig - if config == nil { - config = &tls.Config{ - InsecureSkipVerify: true, - } - } - creds := credentials.NewTLS(config) - options = append(options, grpc.WithTransportCredentials(creds)) - } else { - options = append(options, grpc.WithInsecure()) - } - - // dial the server - ctx, cancel := context.WithTimeout(context.Background(), dopts.Timeout) - defer cancel() - conn, err := grpc.DialContext(ctx, addr, options...) - if err != nil { - return nil, err - } - - // create stream - stream, err := pb.NewTransportClient(conn).Stream(context.Background()) - if err != nil { - return nil, err - } - - // return a client - return &grpcTransportClient{ - conn: conn, - stream: stream, - local: "localhost", - remote: addr, - }, nil -} - -func (t *grpcTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) { - var options transport.ListenOptions - for _, o := range opts { - o(&options) - } - - ln, err := mnet.Listen(addr, func(addr string) (net.Listener, error) { - return net.Listen("tcp", addr) - }) - if err != nil { - return nil, err - } - - return &grpcTransportListener{ - listener: ln, - tls: t.opts.TLSConfig, - secure: t.opts.Secure, - }, nil -} - -func (t *grpcTransport) Init(opts ...transport.Option) error { - for _, o := range opts { - o(&t.opts) - } - return nil -} - -func (t *grpcTransport) Options() transport.Options { - return t.opts -} - -func (t *grpcTransport) String() string { - return "grpc" -} - -func NewTransport(opts ...transport.Option) transport.Transport { - var options transport.Options - for _, o := range opts { - o(&options) - } - return &grpcTransport{opts: options} -} diff --git a/transport/grpc/grpc_test.go b/transport/grpc/grpc_test.go deleted file mode 100644 index 0d0cf57a..00000000 --- a/transport/grpc/grpc_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package grpc - -import ( - "net" - "testing" - - "github.com/unistack-org/micro/v3/transport" -) - -func expectedPort(t *testing.T, expected string, lsn transport.Listener) { - _, port, err := net.SplitHostPort(lsn.Addr()) - if err != nil { - t.Errorf("Expected address to be `%s`, got error: %v", expected, err) - } - - if port != expected { - lsn.Close() - t.Errorf("Expected address to be `%s`, got `%s`", expected, port) - } -} - -func TestGRPCTransportPortRange(t *testing.T) { - tp := NewTransport() - - lsn1, err := tp.Listen(":44444-44448") - if err != nil { - t.Errorf("Did not expect an error, got %s", err) - } - expectedPort(t, "44444", lsn1) - - lsn2, err := tp.Listen(":44444-44448") - if err != nil { - t.Errorf("Did not expect an error, got %s", err) - } - expectedPort(t, "44445", lsn2) - - lsn, err := tp.Listen(":0") - if err != nil { - t.Errorf("Did not expect an error, got %s", err) - } - - lsn.Close() - lsn1.Close() - lsn2.Close() -} - -func TestGRPCTransportCommunication(t *testing.T) { - tr := NewTransport() - - l, err := tr.Listen(":0") - if err != nil { - t.Errorf("Unexpected listen err: %v", err) - } - defer l.Close() - - fn := func(sock transport.Socket) { - defer sock.Close() - - for { - var m transport.Message - if err := sock.Recv(&m); err != nil { - return - } - - if err := sock.Send(&m); err != nil { - return - } - } - } - - done := make(chan bool) - - go func() { - if err := l.Accept(fn); err != nil { - select { - case <-done: - default: - t.Errorf("Unexpected accept err: %v", err) - } - } - }() - - c, err := tr.Dial(l.Addr()) - if err != nil { - t.Errorf("Unexpected dial err: %v", err) - } - defer c.Close() - - m := transport.Message{ - Header: map[string]string{ - "X-Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - if err := c.Send(&m); err != nil { - t.Errorf("Unexpected send err: %v", err) - } - - var rm transport.Message - - if err := c.Recv(&rm); err != nil { - t.Errorf("Unexpected recv err: %v", err) - } - - if string(rm.Body) != string(m.Body) { - t.Errorf("Expected %v, got %v", m.Body, rm.Body) - } - - close(done) -} diff --git a/transport/grpc/handler.go b/transport/grpc/handler.go deleted file mode 100644 index 3d03eb4a..00000000 --- a/transport/grpc/handler.go +++ /dev/null @@ -1,44 +0,0 @@ -package grpc - -import ( - "runtime/debug" - - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/transport" - pb "github.com/unistack-org/micro/v3/transport/grpc/proto" - "google.golang.org/grpc/peer" -) - -// microTransport satisfies the pb.TransportServer inteface -type microTransport struct { - pb.UnimplementedTransportServer - addr string - fn func(transport.Socket) -} - -func (m *microTransport) Stream(ts pb.Transport_StreamServer) (err error) { - - sock := &grpcTransportSocket{ - stream: ts, - local: m.addr, - } - - p, ok := peer.FromContext(ts.Context()) - if ok { - sock.remote = p.Addr.String() - } - - defer func() { - if r := recover(); r != nil { - logger.Error(r, string(debug.Stack())) - sock.Close() - err = errors.InternalServerError("go.micro.transport", "panic recovered: %v", r) - } - }() - - // execute socket func - m.fn(sock) - - return err -} diff --git a/transport/grpc/proto/transport.pb.go b/transport/grpc/proto/transport.pb.go deleted file mode 100644 index b32a3324..00000000 --- a/transport/grpc/proto/transport.pb.go +++ /dev/null @@ -1,174 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: transport/grpc/proto/transport.proto - -package go_micro_transport_grpc - -import ( - proto "github.com/golang/protobuf/proto" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Message struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Header map[string]string `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"` -} - -func (x *Message) Reset() { - *x = Message{} - if protoimpl.UnsafeEnabled { - mi := &file_transport_grpc_proto_transport_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Message) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Message) ProtoMessage() {} - -func (x *Message) ProtoReflect() protoreflect.Message { - mi := &file_transport_grpc_proto_transport_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 Message.ProtoReflect.Descriptor instead. -func (*Message) Descriptor() ([]byte, []int) { - return file_transport_grpc_proto_transport_proto_rawDescGZIP(), []int{0} -} - -func (x *Message) GetHeader() map[string]string { - if x != nil { - return x.Header - } - return nil -} - -func (x *Message) GetBody() []byte { - if x != nil { - return x.Body - } - return nil -} - -var File_transport_grpc_proto_transport_proto protoreflect.FileDescriptor - -var file_transport_grpc_proto_transport_proto_rawDesc = []byte{ - 0x0a, 0x24, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, - 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x22, - 0x9e, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x44, 0x0a, 0x06, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, - 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 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, - 0x32, 0x5f, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, - 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x20, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, - 0x72, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x67, 0x72, 0x70, - 0x63, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x20, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, - 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x67, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, - 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_transport_grpc_proto_transport_proto_rawDescOnce sync.Once - file_transport_grpc_proto_transport_proto_rawDescData = file_transport_grpc_proto_transport_proto_rawDesc -) - -func file_transport_grpc_proto_transport_proto_rawDescGZIP() []byte { - file_transport_grpc_proto_transport_proto_rawDescOnce.Do(func() { - file_transport_grpc_proto_transport_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_grpc_proto_transport_proto_rawDescData) - }) - return file_transport_grpc_proto_transport_proto_rawDescData -} - -var file_transport_grpc_proto_transport_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_transport_grpc_proto_transport_proto_goTypes = []interface{}{ - (*Message)(nil), // 0: go.micro.transport.grpc.Message - nil, // 1: go.micro.transport.grpc.Message.HeaderEntry -} -var file_transport_grpc_proto_transport_proto_depIdxs = []int32{ - 1, // 0: go.micro.transport.grpc.Message.header:type_name -> go.micro.transport.grpc.Message.HeaderEntry - 0, // 1: go.micro.transport.grpc.Transport.Stream:input_type -> go.micro.transport.grpc.Message - 0, // 2: go.micro.transport.grpc.Transport.Stream:output_type -> go.micro.transport.grpc.Message - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] 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_transport_grpc_proto_transport_proto_init() } -func file_transport_grpc_proto_transport_proto_init() { - if File_transport_grpc_proto_transport_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_transport_grpc_proto_transport_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Message); 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_transport_grpc_proto_transport_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_transport_grpc_proto_transport_proto_goTypes, - DependencyIndexes: file_transport_grpc_proto_transport_proto_depIdxs, - MessageInfos: file_transport_grpc_proto_transport_proto_msgTypes, - }.Build() - File_transport_grpc_proto_transport_proto = out.File - file_transport_grpc_proto_transport_proto_rawDesc = nil - file_transport_grpc_proto_transport_proto_goTypes = nil - file_transport_grpc_proto_transport_proto_depIdxs = nil -} diff --git a/transport/grpc/proto/transport.pb.micro.go b/transport/grpc/proto/transport.pb.micro.go deleted file mode 100644 index ec88c7a7..00000000 --- a/transport/grpc/proto/transport.pb.micro.go +++ /dev/null @@ -1,175 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: transport/grpc/proto/transport.proto - -package go_micro_transport_grpc - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Transport service - -func NewTransportEndpoints() []*api.Endpoint { - return []*api.Endpoint{} -} - -// Client API for Transport service - -type TransportService interface { - Stream(ctx context.Context, opts ...client.CallOption) (Transport_StreamService, error) -} - -type transportService struct { - c client.Client - name string -} - -func NewTransportService(name string, c client.Client) TransportService { - return &transportService{ - c: c, - name: name, - } -} - -func (c *transportService) Stream(ctx context.Context, opts ...client.CallOption) (Transport_StreamService, error) { - req := c.c.NewRequest(c.name, "Transport.Stream", &Message{}) - stream, err := c.c.Stream(ctx, req, opts...) - if err != nil { - return nil, err - } - return &transportServiceStream{stream}, nil -} - -type Transport_StreamService interface { - Context() context.Context - SendMsg(interface{}) error - RecvMsg(interface{}) error - Close() error - Send(*Message) error - Recv() (*Message, error) -} - -type transportServiceStream struct { - stream client.Stream -} - -func (x *transportServiceStream) Close() error { - return x.stream.Close() -} - -func (x *transportServiceStream) Context() context.Context { - return x.stream.Context() -} - -func (x *transportServiceStream) SendMsg(m interface{}) error { - return x.stream.Send(m) -} - -func (x *transportServiceStream) RecvMsg(m interface{}) error { - return x.stream.Recv(m) -} - -func (x *transportServiceStream) Send(m *Message) error { - return x.stream.Send(m) -} - -func (x *transportServiceStream) Recv() (*Message, error) { - m := new(Message) - err := x.stream.Recv(m) - if err != nil { - return nil, err - } - return m, nil -} - -// Server API for Transport service - -type TransportHandler interface { - Stream(context.Context, Transport_StreamStream) error -} - -func RegisterTransportHandler(s server.Server, hdlr TransportHandler, opts ...server.HandlerOption) error { - type transport interface { - Stream(ctx context.Context, stream server.Stream) error - } - type Transport struct { - transport - } - h := &transportHandler{hdlr} - return s.Handle(s.NewHandler(&Transport{h}, opts...)) -} - -type transportHandler struct { - TransportHandler -} - -func (h *transportHandler) Stream(ctx context.Context, stream server.Stream) error { - return h.TransportHandler.Stream(ctx, &transportStreamStream{stream}) -} - -type Transport_StreamStream interface { - Context() context.Context - SendMsg(interface{}) error - RecvMsg(interface{}) error - Close() error - Send(*Message) error - Recv() (*Message, error) -} - -type transportStreamStream struct { - stream server.Stream -} - -func (x *transportStreamStream) Close() error { - return x.stream.Close() -} - -func (x *transportStreamStream) Context() context.Context { - return x.stream.Context() -} - -func (x *transportStreamStream) SendMsg(m interface{}) error { - return x.stream.Send(m) -} - -func (x *transportStreamStream) RecvMsg(m interface{}) error { - return x.stream.Recv(m) -} - -func (x *transportStreamStream) Send(m *Message) error { - return x.stream.Send(m) -} - -func (x *transportStreamStream) Recv() (*Message, error) { - m := new(Message) - if err := x.stream.Recv(m); err != nil { - return nil, err - } - return m, nil -} diff --git a/transport/grpc/proto/transport.proto b/transport/grpc/proto/transport.proto deleted file mode 100644 index 1ec353a8..00000000 --- a/transport/grpc/proto/transport.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -package go.micro.transport.grpc; - -service Transport { - rpc Stream(stream Message) returns (stream Message) {} -} - -message Message { - map header = 1; - bytes body = 2; -} diff --git a/transport/grpc/proto/transport_grpc.pb.go b/transport/grpc/proto/transport_grpc.pb.go deleted file mode 100644 index 44d040db..00000000 --- a/transport/grpc/proto/transport_grpc.pb.go +++ /dev/null @@ -1,122 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package go_micro_transport_grpc - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// TransportClient is the client API for Transport service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type TransportClient interface { - Stream(ctx context.Context, opts ...grpc.CallOption) (Transport_StreamClient, error) -} - -type transportClient struct { - cc grpc.ClientConnInterface -} - -func NewTransportClient(cc grpc.ClientConnInterface) TransportClient { - return &transportClient{cc} -} - -func (c *transportClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Transport_StreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Transport_serviceDesc.Streams[0], "/go.micro.transport.grpc.Transport/Stream", opts...) - if err != nil { - return nil, err - } - x := &transportStreamClient{stream} - return x, nil -} - -type Transport_StreamClient interface { - Send(*Message) error - Recv() (*Message, error) - grpc.ClientStream -} - -type transportStreamClient struct { - grpc.ClientStream -} - -func (x *transportStreamClient) Send(m *Message) error { - return x.ClientStream.SendMsg(m) -} - -func (x *transportStreamClient) Recv() (*Message, error) { - m := new(Message) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// TransportServer is the server API for Transport service. -// All implementations must embed UnimplementedTransportServer -// for forward compatibility -type TransportServer interface { - Stream(Transport_StreamServer) error - mustEmbedUnimplementedTransportServer() -} - -// UnimplementedTransportServer must be embedded to have forward compatible implementations. -type UnimplementedTransportServer struct { -} - -func (*UnimplementedTransportServer) Stream(Transport_StreamServer) error { - return status.Errorf(codes.Unimplemented, "method Stream not implemented") -} -func (*UnimplementedTransportServer) mustEmbedUnimplementedTransportServer() {} - -func RegisterTransportServer(s *grpc.Server, srv TransportServer) { - s.RegisterService(&_Transport_serviceDesc, srv) -} - -func _Transport_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TransportServer).Stream(&transportStreamServer{stream}) -} - -type Transport_StreamServer interface { - Send(*Message) error - Recv() (*Message, error) - grpc.ServerStream -} - -type transportStreamServer struct { - grpc.ServerStream -} - -func (x *transportStreamServer) Send(m *Message) error { - return x.ServerStream.SendMsg(m) -} - -func (x *transportStreamServer) Recv() (*Message, error) { - m := new(Message) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -var _Transport_serviceDesc = grpc.ServiceDesc{ - ServiceName: "go.micro.transport.grpc.Transport", - HandlerType: (*TransportServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{ - { - StreamName: "Stream", - Handler: _Transport_Stream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "transport/grpc/proto/transport.proto", -} diff --git a/transport/grpc/socket.go b/transport/grpc/socket.go deleted file mode 100644 index 88baced8..00000000 --- a/transport/grpc/socket.go +++ /dev/null @@ -1,97 +0,0 @@ -package grpc - -import ( - "github.com/unistack-org/micro/v3/transport" - pb "github.com/unistack-org/micro/v3/transport/grpc/proto" - "google.golang.org/grpc" -) - -type grpcTransportClient struct { - conn *grpc.ClientConn - stream pb.Transport_StreamClient - - local string - remote string -} - -type grpcTransportSocket struct { - stream pb.Transport_StreamServer - local string - remote string -} - -func (g *grpcTransportClient) Local() string { - return g.local -} - -func (g *grpcTransportClient) Remote() string { - return g.remote -} - -func (g *grpcTransportClient) Recv(m *transport.Message) error { - if m == nil { - return nil - } - - msg, err := g.stream.Recv() - if err != nil { - return err - } - - m.Header = msg.Header - m.Body = msg.Body - return nil -} - -func (g *grpcTransportClient) Send(m *transport.Message) error { - if m == nil { - return nil - } - - return g.stream.Send(&pb.Message{ - Header: m.Header, - Body: m.Body, - }) -} - -func (g *grpcTransportClient) Close() error { - return g.conn.Close() -} - -func (g *grpcTransportSocket) Local() string { - return g.local -} - -func (g *grpcTransportSocket) Remote() string { - return g.remote -} - -func (g *grpcTransportSocket) Recv(m *transport.Message) error { - if m == nil { - return nil - } - - msg, err := g.stream.Recv() - if err != nil { - return err - } - - m.Header = msg.Header - m.Body = msg.Body - return nil -} - -func (g *grpcTransportSocket) Send(m *transport.Message) error { - if m == nil { - return nil - } - - return g.stream.Send(&pb.Message{ - Header: m.Header, - Body: m.Body, - }) -} - -func (g *grpcTransportSocket) Close() error { - return nil -} diff --git a/transport/http/http.go b/transport/http/http.go deleted file mode 100644 index a73e116f..00000000 --- a/transport/http/http.go +++ /dev/null @@ -1,602 +0,0 @@ -package http - -import ( - "bufio" - "bytes" - "crypto/tls" - "errors" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "sync" - "time" - - "github.com/unistack-org/micro/v3/transport" - maddr "github.com/unistack-org/micro/v3/util/addr" - "github.com/unistack-org/micro/v3/util/buf" - mnet "github.com/unistack-org/micro/v3/util/net" - mls "github.com/unistack-org/micro/v3/util/tls" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" -) - -type httpTransport struct { - opts transport.Options -} - -type httpTransportClient struct { - ht *httpTransport - addr string - conn net.Conn - dialOpts transport.DialOptions - once sync.Once - - sync.RWMutex - - // request must be stored for response processing - r chan *http.Request - bl []*http.Request - buff *bufio.Reader - - // local/remote ip - local string - remote string -} - -type httpTransportSocket struct { - ht *httpTransport - w http.ResponseWriter - r *http.Request - rw *bufio.ReadWriter - - mtx sync.RWMutex - - // the hijacked when using http 1 - conn net.Conn - // for the first request - ch chan *http.Request - - // h2 things - buf *bufio.Reader - // indicate if socket is closed - closed chan bool - - // local/remote ip - local string - remote string -} - -type httpTransportListener struct { - ht *httpTransport - listener net.Listener -} - -func (h *httpTransportClient) Local() string { - return h.local -} - -func (h *httpTransportClient) Remote() string { - return h.remote -} - -func (h *httpTransportClient) Send(m *transport.Message) error { - header := make(http.Header) - - for k, v := range m.Header { - header.Set(k, v) - } - - b := buf.New(bytes.NewBuffer(m.Body)) - defer b.Close() - - req := &http.Request{ - Method: "POST", - URL: &url.URL{ - Scheme: "http", - Host: h.addr, - }, - Header: header, - Body: b, - ContentLength: int64(b.Len()), - Host: h.addr, - } - - h.Lock() - h.bl = append(h.bl, req) - select { - case h.r <- h.bl[0]: - h.bl = h.bl[1:] - default: - } - h.Unlock() - - // set timeout if its greater than 0 - if h.ht.opts.Timeout > time.Duration(0) { - h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)) - } - - return req.Write(h.conn) -} - -func (h *httpTransportClient) Recv(m *transport.Message) error { - if m == nil { - return errors.New("message passed in is nil") - } - - var r *http.Request - if !h.dialOpts.Stream { - rc, ok := <-h.r - if !ok { - return io.EOF - } - r = rc - } - - // set timeout if its greater than 0 - if h.ht.opts.Timeout > time.Duration(0) { - h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)) - } - - rsp, err := http.ReadResponse(h.buff, r) - if err != nil { - return err - } - defer rsp.Body.Close() - - b, err := ioutil.ReadAll(rsp.Body) - if err != nil { - return err - } - - if rsp.StatusCode != 200 { - return errors.New(rsp.Status + ": " + string(b)) - } - - m.Body = b - - if m.Header == nil { - m.Header = make(map[string]string, len(rsp.Header)) - } - - for k, v := range rsp.Header { - if len(v) > 0 { - m.Header[k] = v[0] - } else { - m.Header[k] = "" - } - } - - return nil -} - -func (h *httpTransportClient) Close() error { - h.once.Do(func() { - h.Lock() - h.buff.Reset(nil) - h.Unlock() - close(h.r) - }) - return h.conn.Close() -} - -func (h *httpTransportSocket) Local() string { - return h.local -} - -func (h *httpTransportSocket) Remote() string { - return h.remote -} - -func (h *httpTransportSocket) Recv(m *transport.Message) error { - if m == nil { - return errors.New("message passed in is nil") - } - if m.Header == nil { - m.Header = make(map[string]string, len(h.r.Header)) - } - - // process http 1 - if h.r.ProtoMajor == 1 { - // set timeout if its greater than 0 - if h.ht.opts.Timeout > time.Duration(0) { - h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)) - } - - var r *http.Request - - select { - // get first request - case r = <-h.ch: - // read next request - default: - rr, err := http.ReadRequest(h.rw.Reader) - if err != nil { - return err - } - r = rr - } - - // read body - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return err - } - - // set body - r.Body.Close() - m.Body = b - - // set headers - for k, v := range r.Header { - if len(v) > 0 { - m.Header[k] = v[0] - } else { - m.Header[k] = "" - } - } - - // return early early - return nil - } - - // only process if the socket is open - select { - case <-h.closed: - return io.EOF - default: - // no op - } - - // processing http2 request - // read streaming body - - // set max buffer size - // TODO: adjustable buffer size - buf := make([]byte, 4*1024*1024) - - // read the request body - n, err := h.buf.Read(buf) - // not an eof error - if err != nil { - return err - } - - // check if we have data - if n > 0 { - m.Body = buf[:n] - } - - // set headers - for k, v := range h.r.Header { - if len(v) > 0 { - m.Header[k] = v[0] - } else { - m.Header[k] = "" - } - } - - // set path - m.Header[":path"] = h.r.URL.Path - - return nil -} - -func (h *httpTransportSocket) Send(m *transport.Message) error { - if h.r.ProtoMajor == 1 { - // make copy of header - hdr := make(http.Header) - for k, v := range h.r.Header { - hdr[k] = v - } - - rsp := &http.Response{ - Header: hdr, - Body: ioutil.NopCloser(bytes.NewReader(m.Body)), - Status: "200 OK", - StatusCode: 200, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - ContentLength: int64(len(m.Body)), - } - - for k, v := range m.Header { - rsp.Header.Set(k, v) - } - - // set timeout if its greater than 0 - if h.ht.opts.Timeout > time.Duration(0) { - h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)) - } - - return rsp.Write(h.conn) - } - - // only process if the socket is open - select { - case <-h.closed: - return io.EOF - default: - // no op - } - - // we need to lock to protect the write - h.mtx.RLock() - defer h.mtx.RUnlock() - - // set headers - for k, v := range m.Header { - h.w.Header().Set(k, v) - } - - // write request - _, err := h.w.Write(m.Body) - - // flush the trailers - h.w.(http.Flusher).Flush() - - return err -} - -func (h *httpTransportSocket) error(m *transport.Message) error { - if h.r.ProtoMajor == 1 { - rsp := &http.Response{ - Header: make(http.Header), - Body: ioutil.NopCloser(bytes.NewReader(m.Body)), - Status: "500 Internal Server Error", - StatusCode: 500, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - ContentLength: int64(len(m.Body)), - } - - for k, v := range m.Header { - rsp.Header.Set(k, v) - } - - return rsp.Write(h.conn) - } - - return nil -} - -func (h *httpTransportSocket) Close() error { - h.mtx.Lock() - defer h.mtx.Unlock() - select { - case <-h.closed: - return nil - default: - // close the channel - close(h.closed) - - // close the buffer - h.r.Body.Close() - - // close the connection - if h.r.ProtoMajor == 1 { - return h.conn.Close() - } - } - - return nil -} - -func (h *httpTransportListener) Addr() string { - return h.listener.Addr().String() -} - -func (h *httpTransportListener) Close() error { - return h.listener.Close() -} - -func (h *httpTransportListener) Accept(fn func(transport.Socket)) error { - // create handler mux - mux := http.NewServeMux() - - // register our transport handler - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - var buf *bufio.ReadWriter - var con net.Conn - - // read a regular request - if r.ProtoMajor == 1 { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - r.Body = ioutil.NopCloser(bytes.NewReader(b)) - // hijack the conn - hj, ok := w.(http.Hijacker) - if !ok { - // we're screwed - http.Error(w, "cannot serve conn", http.StatusInternalServerError) - return - } - - conn, bufrw, err := hj.Hijack() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - defer conn.Close() - buf = bufrw - con = conn - } - - // buffered reader - bufr := bufio.NewReader(r.Body) - - // save the request - ch := make(chan *http.Request, 1) - ch <- r - - // create a new transport socket - sock := &httpTransportSocket{ - ht: h.ht, - w: w, - r: r, - rw: buf, - buf: bufr, - ch: ch, - conn: con, - local: h.Addr(), - remote: r.RemoteAddr, - closed: make(chan bool), - } - - // execute the socket - fn(sock) - }) - - // get optional handlers - if h.ht.opts.Context != nil { - handlers, ok := h.ht.opts.Context.Value("http_handlers").(map[string]http.Handler) - if ok { - for pattern, handler := range handlers { - mux.Handle(pattern, handler) - } - } - } - - // default http2 server - srv := &http.Server{ - Handler: mux, - } - - // insecure connection use h2c - if !(h.ht.opts.Secure || h.ht.opts.TLSConfig != nil) { - srv.Handler = h2c.NewHandler(mux, &http2.Server{}) - } - - // begin serving - return srv.Serve(h.listener) -} - -func (h *httpTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { - dopts := transport.DialOptions{ - Timeout: transport.DefaultDialTimeout, - } - - for _, opt := range opts { - opt(&dopts) - } - - var conn net.Conn - var err error - - // TODO: support dial option here rather than using internal config - if h.opts.Secure || h.opts.TLSConfig != nil { - config := h.opts.TLSConfig - if config == nil { - config = &tls.Config{ - InsecureSkipVerify: true, - } - } - config.NextProtos = []string{"http/1.1"} - conn, err = newConn(func(addr string) (net.Conn, error) { - return tls.DialWithDialer(&net.Dialer{Timeout: dopts.Timeout}, "tcp", addr, config) - })(addr) - } else { - conn, err = newConn(func(addr string) (net.Conn, error) { - return net.DialTimeout("tcp", addr, dopts.Timeout) - })(addr) - } - - if err != nil { - return nil, err - } - - return &httpTransportClient{ - ht: h, - addr: addr, - conn: conn, - buff: bufio.NewReader(conn), - dialOpts: dopts, - r: make(chan *http.Request, 1), - local: conn.LocalAddr().String(), - remote: conn.RemoteAddr().String(), - }, nil -} - -func (h *httpTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) { - var options transport.ListenOptions - for _, o := range opts { - o(&options) - } - - var l net.Listener - var err error - - // TODO: support use of listen options - if h.opts.Secure || h.opts.TLSConfig != nil { - config := h.opts.TLSConfig - - fn := func(addr string) (net.Listener, error) { - if config == nil { - hosts := []string{addr} - - // check if its a valid host:port - if host, _, err := net.SplitHostPort(addr); err == nil { - if len(host) == 0 { - hosts = maddr.IPs() - } else { - hosts = []string{host} - } - } - - // generate a certificate - cert, err := mls.Certificate(hosts...) - if err != nil { - return nil, err - } - config = &tls.Config{Certificates: []tls.Certificate{cert}} - } - return tls.Listen("tcp", addr, config) - } - - l, err = mnet.Listen(addr, fn) - } else { - fn := func(addr string) (net.Listener, error) { - return net.Listen("tcp", addr) - } - - l, err = mnet.Listen(addr, fn) - } - - if err != nil { - return nil, err - } - - return &httpTransportListener{ - ht: h, - listener: l, - }, nil -} - -func (h *httpTransport) Init(opts ...transport.Option) error { - for _, o := range opts { - o(&h.opts) - } - return nil -} - -func (h *httpTransport) Options() transport.Options { - return h.opts -} - -func (h *httpTransport) String() string { - return "http" -} - -func NewTransport(opts ...transport.Option) transport.Transport { - var options transport.Options - for _, o := range opts { - o(&options) - } - return &httpTransport{opts: options} -} diff --git a/transport/http/http_proxy.go b/transport/http/http_proxy.go deleted file mode 100644 index 328091b5..00000000 --- a/transport/http/http_proxy.go +++ /dev/null @@ -1,109 +0,0 @@ -package http - -import ( - "bufio" - "encoding/base64" - "fmt" - "io" - "net" - "net/http" - "net/http/httputil" - "net/url" -) - -const ( - proxyAuthHeader = "Proxy-Authorization" -) - -func getURL(addr string) (*url.URL, error) { - r := &http.Request{ - URL: &url.URL{ - Scheme: "https", - Host: addr, - }, - } - return http.ProxyFromEnvironment(r) -} - -type pbuffer struct { - net.Conn - r io.Reader -} - -func (p *pbuffer) Read(b []byte) (int, error) { - return p.r.Read(b) -} - -func proxyDial(conn net.Conn, addr string, proxyURL *url.URL) (_ net.Conn, err error) { - defer func() { - if err != nil { - conn.Close() - } - }() - - r := &http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Host: addr}, - Header: map[string][]string{"User-Agent": {"micro/latest"}}, - } - - if user := proxyURL.User; user != nil { - u := user.Username() - p, _ := user.Password() - auth := []byte(u + ":" + p) - basicAuth := base64.StdEncoding.EncodeToString(auth) - r.Header.Add(proxyAuthHeader, "Basic "+basicAuth) - } - - if err := r.Write(conn); err != nil { - return nil, fmt.Errorf("failed to write the HTTP request: %v", err) - } - - br := bufio.NewReader(conn) - rsp, err := http.ReadResponse(br, r) - if err != nil { - return nil, fmt.Errorf("reading server HTTP response: %v", err) - } - defer rsp.Body.Close() - if rsp.StatusCode != http.StatusOK { - dump, err := httputil.DumpResponse(rsp, true) - if err != nil { - return nil, fmt.Errorf("failed to do connect handshake, status code: %s", rsp.Status) - } - return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump) - } - - return &pbuffer{Conn: conn, r: br}, nil -} - -// Creates a new connection -func newConn(dial func(string) (net.Conn, error)) func(string) (net.Conn, error) { - return func(addr string) (net.Conn, error) { - // get the proxy url - proxyURL, err := getURL(addr) - if err != nil { - return nil, err - } - - // set to addr - callAddr := addr - - // got proxy - if proxyURL != nil { - callAddr = proxyURL.Host - } - - // dial the addr - c, err := dial(callAddr) - if err != nil { - return nil, err - } - - // do proxy connect if we have proxy url - if proxyURL != nil { - c, err = proxyDial(c, addr, proxyURL) - } - - return c, err - } -} diff --git a/transport/http/http_test.go b/transport/http/http_test.go deleted file mode 100644 index 04000a2e..00000000 --- a/transport/http/http_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package http - -import ( - "sync" - "testing" - - "github.com/unistack-org/micro/v3/transport" -) - -func call(b *testing.B, c int) { - b.StopTimer() - - tr := NewTransport() - - // server listen - l, err := tr.Listen("localhost:0") - if err != nil { - b.Fatal(err) - } - defer l.Close() - - // socket func - fn := func(sock transport.Socket) { - defer sock.Close() - - for { - var m transport.Message - if err := sock.Recv(&m); err != nil { - return - } - - if err := sock.Send(&m); err != nil { - return - } - } - } - - done := make(chan bool) - - // accept connections - go func() { - if err := l.Accept(fn); err != nil { - select { - case <-done: - default: - b.Fatalf("Unexpected accept err: %v", err) - } - } - }() - - m := transport.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - // client connection - client, err := tr.Dial(l.Addr()) - if err != nil { - b.Fatalf("Unexpected dial err: %v", err) - } - - send := func(c transport.Client) { - // send message - if err := c.Send(&m); err != nil { - b.Fatalf("Unexpected send err: %v", err) - } - - var rm transport.Message - // receive message - if err := c.Recv(&rm); err != nil { - b.Fatalf("Unexpected recv err: %v", err) - } - } - - // warm - for i := 0; i < 10; i++ { - send(client) - } - - client.Close() - - ch := make(chan int, c*4) - - var wg sync.WaitGroup - wg.Add(c) - - for i := 0; i < c; i++ { - go func() { - cl, err := tr.Dial(l.Addr()) - if err != nil { - b.Fatalf("Unexpected dial err: %v", err) - } - defer cl.Close() - - for range ch { - send(cl) - } - - wg.Done() - }() - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - ch <- i - } - - b.StopTimer() - close(ch) - - wg.Wait() - - // finish - close(done) -} - -func BenchmarkTransport1(b *testing.B) { - call(b, 1) -} - -func BenchmarkTransport8(b *testing.B) { - call(b, 8) -} - -func BenchmarkTransport16(b *testing.B) { - call(b, 16) -} - -func BenchmarkTransport64(b *testing.B) { - call(b, 64) -} - -func BenchmarkTransport128(b *testing.B) { - call(b, 128) -} diff --git a/transport/http/http_transport_test.go b/transport/http/http_transport_test.go deleted file mode 100644 index ca1ead1c..00000000 --- a/transport/http/http_transport_test.go +++ /dev/null @@ -1,248 +0,0 @@ -package http - -import ( - "io" - "net" - "testing" - "time" - - "github.com/unistack-org/micro/v3/transport" -) - -func expectedPort(t *testing.T, expected string, lsn transport.Listener) { - _, port, err := net.SplitHostPort(lsn.Addr()) - if err != nil { - t.Errorf("Expected address to be `%s`, got error: %v", expected, err) - } - - if port != expected { - lsn.Close() - t.Errorf("Expected address to be `%s`, got `%s`", expected, port) - } -} - -func TestHTTPTransportPortRange(t *testing.T) { - tp := NewTransport() - - lsn1, err := tp.Listen(":44444-44448") - if err != nil { - t.Errorf("Did not expect an error, got %s", err) - } - expectedPort(t, "44444", lsn1) - - lsn2, err := tp.Listen(":44444-44448") - if err != nil { - t.Errorf("Did not expect an error, got %s", err) - } - expectedPort(t, "44445", lsn2) - - lsn, err := tp.Listen("127.0.0.1:0") - if err != nil { - t.Errorf("Did not expect an error, got %s", err) - } - - lsn.Close() - lsn1.Close() - lsn2.Close() -} - -func TestHTTPTransportCommunication(t *testing.T) { - tr := NewTransport() - - l, err := tr.Listen("127.0.0.1:0") - if err != nil { - t.Errorf("Unexpected listen err: %v", err) - } - defer l.Close() - - fn := func(sock transport.Socket) { - defer sock.Close() - - for { - var m transport.Message - if err := sock.Recv(&m); err != nil { - return - } - - if err := sock.Send(&m); err != nil { - return - } - } - } - - done := make(chan bool) - - go func() { - if err := l.Accept(fn); err != nil { - select { - case <-done: - default: - t.Errorf("Unexpected accept err: %v", err) - } - } - }() - - c, err := tr.Dial(l.Addr()) - if err != nil { - t.Errorf("Unexpected dial err: %v", err) - } - defer c.Close() - - m := transport.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - if err := c.Send(&m); err != nil { - t.Errorf("Unexpected send err: %v", err) - } - - var rm transport.Message - - if err := c.Recv(&rm); err != nil { - t.Errorf("Unexpected recv err: %v", err) - } - - if string(rm.Body) != string(m.Body) { - t.Errorf("Expected %v, got %v", m.Body, rm.Body) - } - - close(done) -} - -func TestHTTPTransportError(t *testing.T) { - tr := NewTransport() - - l, err := tr.Listen("127.0.0.1:0") - if err != nil { - t.Errorf("Unexpected listen err: %v", err) - } - defer l.Close() - - fn := func(sock transport.Socket) { - defer sock.Close() - - for { - var m transport.Message - if err := sock.Recv(&m); err != nil { - if err == io.EOF { - return - } - t.Fatal(err) - } - - sock.(*httpTransportSocket).error(&transport.Message{ - Body: []byte(`an error occurred`), - }) - } - } - - done := make(chan bool) - - go func() { - if err := l.Accept(fn); err != nil { - select { - case <-done: - default: - t.Errorf("Unexpected accept err: %v", err) - } - } - }() - - c, err := tr.Dial(l.Addr()) - if err != nil { - t.Errorf("Unexpected dial err: %v", err) - } - defer c.Close() - - m := transport.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - if err := c.Send(&m); err != nil { - t.Errorf("Unexpected send err: %v", err) - } - - var rm transport.Message - - err = c.Recv(&rm) - if err == nil { - t.Fatal("Expected error but got nil") - } - - if err.Error() != "500 Internal Server Error: an error occurred" { - t.Fatalf("Did not receive expected error, got: %v", err) - } - - close(done) -} - -func TestHTTPTransportTimeout(t *testing.T) { - tr := NewTransport(transport.Timeout(time.Millisecond * 100)) - - l, err := tr.Listen("127.0.0.1:0") - if err != nil { - t.Errorf("Unexpected listen err: %v", err) - } - defer l.Close() - - done := make(chan bool) - - fn := func(sock transport.Socket) { - defer func() { - sock.Close() - close(done) - }() - - go func() { - select { - case <-done: - return - case <-time.After(time.Second): - t.Fatal("deadline not executed") - } - }() - - for { - var m transport.Message - - if err := sock.Recv(&m); err != nil { - return - } - } - } - - go func() { - if err := l.Accept(fn); err != nil { - select { - case <-done: - default: - t.Errorf("Unexpected accept err: %v", err) - } - } - }() - - c, err := tr.Dial(l.Addr()) - if err != nil { - t.Errorf("Unexpected dial err: %v", err) - } - defer c.Close() - - m := transport.Message{ - Header: map[string]string{ - "Content-Type": "application/json", - }, - Body: []byte(`{"message": "Hello World"}`), - } - - if err := c.Send(&m); err != nil { - t.Errorf("Unexpected send err: %v", err) - } - - <-done -} diff --git a/transport/http/options.go b/transport/http/options.go deleted file mode 100644 index 3a02a5f7..00000000 --- a/transport/http/options.go +++ /dev/null @@ -1,23 +0,0 @@ -package http - -import ( - "context" - "net/http" - - "github.com/unistack-org/micro/v3/transport" -) - -// Handle registers the handler for the given pattern. -func Handle(pattern string, handler http.Handler) transport.Option { - return func(o *transport.Options) { - if o.Context == nil { - o.Context = context.Background() - } - handlers, ok := o.Context.Value("http_handlers").(map[string]http.Handler) - if !ok { - handlers = make(map[string]http.Handler) - } - handlers[pattern] = handler - o.Context = context.WithValue(o.Context, "http_handlers", handlers) - } -} diff --git a/transport/memory/memory.go b/transport/memory/memory.go deleted file mode 100644 index faf8a973..00000000 --- a/transport/memory/memory.go +++ /dev/null @@ -1,275 +0,0 @@ -// Package memory is an in-memory transport -package memory - -import ( - "context" - "errors" - "fmt" - "math/rand" - "net" - "sync" - "time" - - "github.com/unistack-org/micro/v3/transport" - maddr "github.com/unistack-org/micro/v3/util/addr" - mnet "github.com/unistack-org/micro/v3/util/net" -) - -type memorySocket struct { - recv chan *transport.Message - send chan *transport.Message - // sock exit - exit chan bool - // listener exit - lexit chan bool - - local string - remote string - - // for send/recv transport.Timeout - timeout time.Duration - ctx context.Context - sync.RWMutex -} - -type memoryClient struct { - *memorySocket - opts transport.DialOptions -} - -type memoryListener struct { - addr string - exit chan bool - conn chan *memorySocket - lopts transport.ListenOptions - topts transport.Options - sync.RWMutex - ctx context.Context -} - -type memoryTransport struct { - opts transport.Options - sync.RWMutex - listeners map[string]*memoryListener -} - -func (ms *memorySocket) Recv(m *transport.Message) error { - ms.RLock() - defer ms.RUnlock() - - ctx := ms.ctx - if ms.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout) - defer cancel() - } - - select { - case <-ctx.Done(): - return ctx.Err() - case <-ms.exit: - return errors.New("connection closed") - case <-ms.lexit: - return errors.New("server connection closed") - case cm := <-ms.recv: - *m = *cm - } - return nil -} - -func (ms *memorySocket) Local() string { - return ms.local -} - -func (ms *memorySocket) Remote() string { - return ms.remote -} - -func (ms *memorySocket) Send(m *transport.Message) error { - ms.RLock() - defer ms.RUnlock() - - ctx := ms.ctx - if ms.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout) - defer cancel() - } - - select { - case <-ctx.Done(): - return ctx.Err() - case <-ms.exit: - return errors.New("connection closed") - case <-ms.lexit: - return errors.New("server connection closed") - case ms.send <- m: - } - return nil -} - -func (ms *memorySocket) Close() error { - ms.Lock() - defer ms.Unlock() - select { - case <-ms.exit: - return nil - default: - close(ms.exit) - } - return nil -} - -func (m *memoryListener) Addr() string { - return m.addr -} - -func (m *memoryListener) Close() error { - m.Lock() - defer m.Unlock() - select { - case <-m.exit: - return nil - default: - close(m.exit) - } - return nil -} - -func (m *memoryListener) Accept(fn func(transport.Socket)) error { - for { - select { - case <-m.exit: - return nil - case c := <-m.conn: - go fn(&memorySocket{ - lexit: c.lexit, - exit: c.exit, - send: c.recv, - recv: c.send, - local: c.Remote(), - remote: c.Local(), - timeout: m.topts.Timeout, - ctx: m.topts.Context, - }) - } - } -} - -func (m *memoryTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { - m.RLock() - defer m.RUnlock() - - listener, ok := m.listeners[addr] - if !ok { - return nil, errors.New("could not dial " + addr) - } - - var options transport.DialOptions - for _, o := range opts { - o(&options) - } - - client := &memoryClient{ - &memorySocket{ - send: make(chan *transport.Message), - recv: make(chan *transport.Message), - exit: make(chan bool), - lexit: listener.exit, - local: addr, - remote: addr, - timeout: m.opts.Timeout, - ctx: m.opts.Context, - }, - options, - } - - // pseudo connect - select { - case <-listener.exit: - return nil, errors.New("connection error") - case listener.conn <- client.memorySocket: - } - - return client, nil -} - -func (m *memoryTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) { - m.Lock() - defer m.Unlock() - - var options transport.ListenOptions - for _, o := range opts { - o(&options) - } - - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - - addr, err = maddr.Extract(host) - if err != nil { - return nil, err - } - - // if zero port then randomly assign one - if len(port) > 0 && port == "0" { - i := rand.Intn(20000) - port = fmt.Sprintf("%d", 10000+i) - } - - // set addr with port - addr = mnet.HostPort(addr, port) - - if _, ok := m.listeners[addr]; ok { - return nil, errors.New("already listening on " + addr) - } - - listener := &memoryListener{ - lopts: options, - topts: m.opts, - addr: addr, - conn: make(chan *memorySocket), - exit: make(chan bool), - ctx: m.opts.Context, - } - - m.listeners[addr] = listener - - return listener, nil -} - -func (m *memoryTransport) Init(opts ...transport.Option) error { - for _, o := range opts { - o(&m.opts) - } - return nil -} - -func (m *memoryTransport) Options() transport.Options { - return m.opts -} - -func (m *memoryTransport) String() string { - return "memory" -} - -func NewTransport(opts ...transport.Option) transport.Transport { - var options transport.Options - - rand.Seed(time.Now().UnixNano()) - - for _, o := range opts { - o(&options) - } - - if options.Context == nil { - options.Context = context.Background() - } - - return &memoryTransport{ - opts: options, - listeners: make(map[string]*memoryListener), - } -} diff --git a/transport/memory/memory_test.go b/transport/memory/memory_test.go deleted file mode 100644 index 6d759e42..00000000 --- a/transport/memory/memory_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package memory - -import ( - "os" - "testing" - - "github.com/unistack-org/micro/v3/transport" -) - -func TestMemoryTransport(t *testing.T) { - tr := NewTransport() - - // bind / listen - l, err := tr.Listen("127.0.0.1:8080") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l.Close() - - // accept - go func() { - if err := l.Accept(func(sock transport.Socket) { - for { - var m transport.Message - if err := sock.Recv(&m); err != nil { - return - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Server Received %s", string(m.Body)) - } - if err := sock.Send(&transport.Message{ - Body: []byte(`pong`), - }); err != nil { - return - } - } - }); err != nil { - t.Fatalf("Unexpected error accepting %v", err) - } - }() - - // dial - c, err := tr.Dial("127.0.0.1:8080") - if err != nil { - t.Fatalf("Unexpected error dialing %v", err) - } - defer c.Close() - - // send <=> receive - for i := 0; i < 3; i++ { - if err := c.Send(&transport.Message{ - Body: []byte(`ping`), - }); err != nil { - return - } - var m transport.Message - if err := c.Recv(&m); err != nil { - return - } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - t.Logf("Client Received %s", string(m.Body)) - } - } - -} - -func TestListener(t *testing.T) { - tr := NewTransport() - - // bind / listen on random port - l, err := tr.Listen(":0") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l.Close() - - // try again - l2, err := tr.Listen(":0") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l2.Close() - - // now make sure it still fails - l3, err := tr.Listen(":8080") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l3.Close() - - if _, err := tr.Listen(":8080"); err == nil { - t.Fatal("Expected error binding to :8080 got nil") - } -} diff --git a/transport/quic/quic.go b/transport/quic/quic.go deleted file mode 100644 index 594faca3..00000000 --- a/transport/quic/quic.go +++ /dev/null @@ -1,194 +0,0 @@ -// Package quic provides a QUIC based transport -package quic - -import ( - "context" - "crypto/tls" - "encoding/gob" - "time" - - quic "github.com/lucas-clemente/quic-go" - "github.com/unistack-org/micro/v3/transport" - utls "github.com/unistack-org/micro/v3/util/tls" -) - -type quicSocket struct { - s quic.Session - st quic.Stream - enc *gob.Encoder - dec *gob.Decoder -} - -type quicTransport struct { - opts transport.Options -} - -type quicClient struct { - *quicSocket - t *quicTransport - opts transport.DialOptions -} - -type quicListener struct { - l quic.Listener - t *quicTransport - opts transport.ListenOptions -} - -func (q *quicClient) Close() error { - return q.quicSocket.st.Close() -} - -func (q *quicSocket) Recv(m *transport.Message) error { - return q.dec.Decode(&m) -} - -func (q *quicSocket) Send(m *transport.Message) error { - // set the write deadline - if err := q.st.SetWriteDeadline(time.Now().Add(time.Second * 10)); err != nil { - return err - } - // send the data - return q.enc.Encode(m) -} - -func (q *quicSocket) Close() error { - return q.s.CloseWithError(0, "") -} - -func (q *quicSocket) Local() string { - return q.s.LocalAddr().String() -} - -func (q *quicSocket) Remote() string { - return q.s.RemoteAddr().String() -} - -func (q *quicListener) Addr() string { - return q.l.Addr().String() -} - -func (q *quicListener) Close() error { - return q.l.Close() -} - -func (q *quicListener) Accept(fn func(transport.Socket)) error { - for { - s, err := q.l.Accept(context.TODO()) - if err != nil { - return err - } - - stream, err := s.AcceptStream(context.TODO()) - if err != nil { - continue - } - - go func() { - fn(&quicSocket{ - s: s, - st: stream, - enc: gob.NewEncoder(stream), - dec: gob.NewDecoder(stream), - }) - }() - } -} - -func (q *quicTransport) Init(opts ...transport.Option) error { - for _, o := range opts { - o(&q.opts) - } - return nil -} - -func (q *quicTransport) Options() transport.Options { - return q.opts -} - -func (q *quicTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { - var options transport.DialOptions - for _, o := range opts { - o(&options) - } - - config := q.opts.TLSConfig - if config == nil { - config = &tls.Config{ - InsecureSkipVerify: true, - NextProtos: []string{"http/1.1"}, - } - } - s, err := quic.DialAddr(addr, config, &quic.Config{ - MaxIdleTimeout: time.Minute * 2, - KeepAlive: true, - }) - if err != nil { - return nil, err - } - - st, err := s.OpenStreamSync(context.TODO()) - if err != nil { - return nil, err - } - - enc := gob.NewEncoder(st) - dec := gob.NewDecoder(st) - - return &quicClient{ - &quicSocket{ - s: s, - st: st, - enc: enc, - dec: dec, - }, - q, - options, - }, nil -} - -func (q *quicTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) { - var options transport.ListenOptions - for _, o := range opts { - o(&options) - } - - config := q.opts.TLSConfig - if config == nil { - cfg, err := utls.Certificate(addr) - if err != nil { - return nil, err - } - config = &tls.Config{ - Certificates: []tls.Certificate{cfg}, - NextProtos: []string{"http/1.1"}, - } - } - - l, err := quic.ListenAddr(addr, config, &quic.Config{KeepAlive: true}) - if err != nil { - return nil, err - } - - return &quicListener{ - l: l, - t: q, - opts: options, - }, nil -} - -func (q *quicTransport) String() string { - return "quic" -} - -func NewTransport(opts ...transport.Option) transport.Transport { - options := transport.Options{} - - for _, o := range opts { - o(&options) - } - - return &quicTransport{ - opts: options, - } -} diff --git a/tunnel/broker/broker.go b/tunnel/broker/broker.go index 196113d3..bda676bf 100644 --- a/tunnel/broker/broker.go +++ b/tunnel/broker/broker.go @@ -3,11 +3,11 @@ package broker import ( "context" + "fmt" "github.com/unistack-org/micro/v3/broker" "github.com/unistack-org/micro/v3/transport" "github.com/unistack-org/micro/v3/tunnel" - "github.com/unistack-org/micro/v3/tunnel/mucp" ) type tunBroker struct { @@ -144,33 +144,38 @@ func (t *tunSubscriber) Unsubscribe() error { } } -func NewBroker(opts ...broker.Option) broker.Broker { +func NewBroker(opts ...broker.Option) (broker.Broker, error) { options := broker.Options{ Context: context.Background(), } for _, o := range opts { o(&options) } + t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel) if !ok { - t = mucp.NewTunnel() + return nil, fmt.Errorf("tunnel not set") } a, ok := options.Context.Value(tunnelAddr{}).(string) if ok { // initialise address - t.Init(tunnel.Address(a)) + if err := t.Init(tunnel.Address(a)); err != nil { + return nil, err + } } if len(options.Addrs) > 0 { // initialise nodes - t.Init(tunnel.Nodes(options.Addrs...)) + if err := t.Init(tunnel.Nodes(options.Addrs...)); err != nil { + return nil, err + } } return &tunBroker{ opts: options, tunnel: t, - } + }, nil } // WithAddress sets the tunnel address diff --git a/tunnel/mucp/crypto.go b/tunnel/mucp/crypto.go deleted file mode 100644 index 7cfeccef..00000000 --- a/tunnel/mucp/crypto.go +++ /dev/null @@ -1,84 +0,0 @@ -package mucp - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha256" - - "github.com/oxtoacart/bpool" - "github.com/unistack-org/micro/v3/tunnel" -) - -var ( - // the local buffer pool - // gcmStandardNonceSize from crypto/cipher/gcm.go is 12 bytes - // 100 - is max size of pool - noncePool = bpool.NewBytePool(100, 12) -) - -// hash hahes the data into 32 bytes key and returns it -// hash uses sha256 underneath to hash the supplied key -func hash(key []byte) []byte { - sum := sha256.Sum256(key) - return sum[:] -} - -// Encrypt encrypts data and returns the encrypted data -func Encrypt(gcm cipher.AEAD, data []byte) ([]byte, error) { - var err error - - // get new byte array the size of the nonce from pool - // NOTE: we might use smaller nonce size in the future - nonce := noncePool.Get() - if _, err = rand.Read(nonce); err != nil { - return nil, err - } - defer noncePool.Put(nonce) - - // NOTE: we prepend the nonce to the payload - // we need to do this as we need the same nonce - // to decrypt the payload when receiving it - return gcm.Seal(nonce, nonce, data, nil), nil -} - -// Decrypt decrypts the payload and returns the decrypted data -func newCipher(key []byte) (cipher.AEAD, error) { - var err error - - // generate a new AES cipher using our 32 byte key for decrypting the message - c, err := aes.NewCipher(hash(key)) - if err != nil { - return nil, err - } - - // gcm or Galois/Counter Mode, is a mode of operation - // for symmetric key cryptographic block ciphers - // - https://en.wikipedia.org/wiki/Galois/Counter_Mode - gcm, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - - return gcm, nil -} - -func Decrypt(gcm cipher.AEAD, data []byte) ([]byte, error) { - var err error - - nonceSize := gcm.NonceSize() - - if len(data) < nonceSize { - return nil, tunnel.ErrDecryptingData - } - - // NOTE: we need to parse out nonce from the payload - // we prepend the nonce to every encrypted payload - nonce, ciphertext := data[:nonceSize], data[nonceSize:] - ciphertext, err = gcm.Open(ciphertext[:0], nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - return ciphertext, nil -} diff --git a/tunnel/mucp/crypto_test.go b/tunnel/mucp/crypto_test.go deleted file mode 100644 index 41c20fa6..00000000 --- a/tunnel/mucp/crypto_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package mucp - -import ( - "bytes" - "testing" -) - -func TestEncrypt(t *testing.T) { - key := []byte("tokenpassphrase") - gcm, err := newCipher(key) - if err != nil { - t.Fatal(err) - } - - data := []byte("supersecret") - - cipherText, err := Encrypt(gcm, data) - if err != nil { - t.Errorf("failed to encrypt data: %v", err) - } - - // verify the cipherText is not the same as data - if bytes.Equal(data, cipherText) { - t.Error("encrypted data are the same as plaintext") - } -} - -func TestDecrypt(t *testing.T) { - key := []byte("tokenpassphrase") - gcm, err := newCipher(key) - if err != nil { - t.Fatal(err) - } - - data := []byte("supersecret") - - cipherText, err := Encrypt(gcm, data) - if err != nil { - t.Errorf("failed to encrypt data: %v", err) - } - - plainText, err := Decrypt(gcm, cipherText) - if err != nil { - t.Errorf("failed to decrypt data: %v", err) - } - - // verify the plainText is the same as data - if !bytes.Equal(data, plainText) { - t.Error("decrypted data not the same as plaintext") - } -} diff --git a/tunnel/mucp/link.go b/tunnel/mucp/link.go deleted file mode 100644 index 655ece18..00000000 --- a/tunnel/mucp/link.go +++ /dev/null @@ -1,535 +0,0 @@ -package mucp - -import ( - "bytes" - "errors" - "io" - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/transport" -) - -type link struct { - transport.Socket - - // transport to use for connections - transport transport.Transport - - sync.RWMutex - - // stops the link - closed chan bool - // metric used to track metrics - metric chan *metric - // link state channel for testing link - state chan *packet - // send queue for sending packets - sendQueue chan *packet - // receive queue for receiving packets - recvQueue chan *packet - // unique id of this link e.g uuid - // which we define for ourselves - id string - // whether its a loopback connection - // this flag is used by the transport listener - // which accepts inbound quic connections - loopback bool - // whether its actually connected - // dialled side sets it to connected - // after sending the message. the - // listener waits for the connect - connected bool - // the last time we received a keepalive - // on this link from the remote side - lastKeepAlive time.Time - // channels keeps a mapping of channels and last seen - channels map[string]time.Time - // the weighted moving average roundtrip - length int64 - // weighted moving average of bits flowing - rate float64 - // keep an error count on the link - errCount int -} - -// packet send over link -type packet struct { - // message to send or received - message *transport.Message - - // status returned when sent - status chan error - - // receive related error - err error -} - -// metric is used to record link rate -type metric struct { - // amount of data sent - data int - // time taken to send - duration time.Duration - // if an error occurred - status error -} - -var ( - // the 4 byte 0 packet sent to determine the link state - linkRequest = []byte{0, 0, 0, 0} - // the 4 byte 1 filled packet sent to determine link state - linkResponse = []byte{1, 1, 1, 1} - - ErrLinkConnectTimeout = errors.New("link connect timeout") -) - -func newLink(s transport.Socket) *link { - l := &link{ - Socket: s, - id: uuid.New().String(), - lastKeepAlive: time.Now(), - closed: make(chan bool), - channels: make(map[string]time.Time), - state: make(chan *packet, 64), - sendQueue: make(chan *packet, 128), - recvQueue: make(chan *packet, 128), - metric: make(chan *metric, 128), - } - - // process inbound/outbound packets - go l.process() - // manage the link state - go l.manage() - - return l -} - -// setRate sets the bits per second rate as a float64 -func (l *link) setRate(bits int64, delta time.Duration) { - // rate of send in bits per nanosecond - rate := float64(bits) / float64(delta.Nanoseconds()) - - // default the rate if its zero - if l.rate == 0 { - // rate per second - l.rate = rate * 1e9 - } else { - // set new rate per second - l.rate = 0.8*l.rate + 0.2*(rate*1e9) - } -} - -// setRTT sets a nanosecond based moving average roundtrip time for the link -func (l *link) setRTT(d time.Duration) { - l.Lock() - - if l.length <= 0 { - l.length = d.Nanoseconds() - l.Unlock() - return - } - - // https://fishi.devtail.io/weblog/2015/04/12/measuring-bandwidth-and-round-trip-time-tcp-connection-inside-application-layer/ - length := 0.8*float64(l.length) + 0.2*float64(d.Nanoseconds()) - // set new length - l.length = int64(length) - - l.Unlock() -} - -func (l *link) delChannel(ch string) { - l.Lock() - delete(l.channels, ch) - l.Unlock() -} - -func (l *link) getChannel(ch string) time.Time { - l.RLock() - t := l.channels[ch] - l.RUnlock() - return t -} - -func (l *link) setChannel(channels ...string) { - l.Lock() - for _, ch := range channels { - l.channels[ch] = time.Now() - } - l.Unlock() -} - -// set the keepalive time -func (l *link) keepalive() { - l.Lock() - l.lastKeepAlive = time.Now() - l.Unlock() -} - -// process deals with the send queue -func (l *link) process() { - // receive messages - go func() { - for { - m := new(transport.Message) - err := l.recv(m) - if err != nil { - // record the metric - select { - case l.metric <- &metric{status: err}: - default: - } - } - - // process new received message - - pk := &packet{message: m, err: err} - - // this is our link state packet - if m.Header["Micro-Method"] == "link" { - // process link state message - select { - case l.state <- pk: - case <-l.closed: - return - default: - } - continue - } - - // process all messages as is - - select { - case l.recvQueue <- pk: - case <-l.closed: - return - } - } - }() - - // send messages - - for { - select { - case pk := <-l.sendQueue: - // send the message - select { - case pk.status <- l.send(pk.message): - case <-l.closed: - return - } - case <-l.closed: - return - } - } -} - -// manage manages the link state including rtt packets and channel mapping expiry -func (l *link) manage() { - // tick over every minute to expire and fire rtt packets - t1 := time.NewTicker(time.Minute) - defer t1.Stop() - - // used to batch update link metrics - t2 := time.NewTicker(time.Second * 5) - defer t2.Stop() - - // get link id - linkId := l.Id() - - // used to send link state packets - send := func(b []byte) error { - return l.Send(&transport.Message{ - Header: map[string]string{ - "Micro-Method": "link", - "Micro-Link-Id": linkId, - }, Body: b, - }) - } - - // set time now - now := time.Now() - - // send the initial rtt request packet - send(linkRequest) - - for { - select { - // exit if closed - case <-l.closed: - return - // process link state rtt packets - case p := <-l.state: - if p.err != nil { - continue - } - // check the type of message - switch { - case bytes.Equal(p.message.Body, linkRequest): - if logger.V(logger.TraceLevel, log) { - log.Tracef("Link %s received link request", linkId) - } - // send response - if err := send(linkResponse); err != nil { - l.Lock() - l.errCount++ - l.Unlock() - } - case bytes.Equal(p.message.Body, linkResponse): - // set round trip time - d := time.Since(now) - if logger.V(logger.TraceLevel, log) { - log.Tracef("Link %s received link response in %v", linkId, d) - } - // set the RTT - l.setRTT(d) - } - case <-t1.C: - // drop any channel mappings older than 2 minutes - var kill []string - killTime := time.Minute * 2 - - l.RLock() - for ch, t := range l.channels { - if d := time.Since(t); d > killTime { - kill = append(kill, ch) - } - } - l.RUnlock() - - // if nothing to kill don't bother with a wasted lock - if len(kill) == 0 { - continue - } - - // kill the channels! - l.Lock() - for _, ch := range kill { - delete(l.channels, ch) - } - l.Unlock() - - // fire off a link state rtt packet - now = time.Now() - send(linkRequest) - case <-t2.C: - // get a batch of metrics - batch := l.batch() - - // skip if there's no metrics - if len(batch) == 0 { - continue - } - - // lock once to record a batch - l.Lock() - for _, metric := range batch { - l.record(metric) - } - l.Unlock() - } - } -} - -func (l *link) batch() []*metric { - var metrics []*metric - - // pull all the metrics - for { - select { - case m := <-l.metric: - metrics = append(metrics, m) - // non blocking return - default: - return metrics - } - } -} - -func (l *link) record(m *metric) { - // there's an error increment the counter and bail - if m.status != nil { - l.errCount++ - return - } - - // reset the counter - l.errCount = 0 - - // calculate based on data - if m.data > 0 { - // bit sent - bits := m.data * 1024 - - // set the rate - l.setRate(int64(bits), m.duration) - } -} - -func (l *link) send(m *transport.Message) error { - if m.Header == nil { - m.Header = make(map[string]string) - } - // send the message - return l.Socket.Send(m) -} - -// recv a message on the link -func (l *link) recv(m *transport.Message) error { - if m.Header == nil { - m.Header = make(map[string]string) - } - // receive the transport message - return l.Socket.Recv(m) -} - -// Delay is the current load on the link -func (l *link) Delay() int64 { - return int64(len(l.sendQueue) + len(l.recvQueue)) -} - -// Current transfer rate as bits per second (lower is better) -func (l *link) Rate() float64 { - l.RLock() - r := l.rate - l.RUnlock() - return r -} - -func (l *link) Loopback() bool { - l.RLock() - lo := l.loopback - l.RUnlock() - return lo -} - -// Length returns the roundtrip time as nanoseconds (lower is better). -// Returns 0 where no measurement has been taken. -func (l *link) Length() int64 { - l.RLock() - length := l.length - l.RUnlock() - return length -} - -func (l *link) Id() string { - l.RLock() - id := l.id - l.RUnlock() - return id -} - -func (l *link) Close() error { - l.Lock() - defer l.Unlock() - - select { - case <-l.closed: - return nil - default: - l.Socket.Close() - close(l.closed) - } - - return nil -} - -// Send sencs a message on the link -func (l *link) Send(m *transport.Message) error { - // create a new packet to send over the link - p := &packet{ - message: m, - status: make(chan error, 1), - } - - // calculate the data sent - dataSent := len(m.Body) - - // set header length - for k, v := range m.Header { - dataSent += (len(k) + len(v)) - } - - // get time now - now := time.Now() - - // queue the message - select { - case l.sendQueue <- p: - // in the send queue - case <-l.closed: - return io.EOF - } - - // error to use - var err error - - // wait for response - select { - case <-l.closed: - return io.EOF - case err = <-p.status: - } - - // create a metric with - // time taken, size of package, error status - mt := &metric{ - data: dataSent, - duration: time.Since(now), - status: err, - } - - // pass back a metric - // do not block - select { - case l.metric <- mt: - default: - } - - return nil -} - -// Accept accepts a message on the socket -func (l *link) Recv(m *transport.Message) error { - select { - case <-l.closed: - // check if there's any messages left - select { - case pk := <-l.recvQueue: - // check the packet receive error - if pk.err != nil { - return pk.err - } - *m = *pk.message - default: - return io.EOF - } - case pk := <-l.recvQueue: - // check the packet receive error - if pk.err != nil { - return pk.err - } - *m = *pk.message - } - return nil -} - -// State can return connected, closed, error -func (l *link) State() string { - select { - case <-l.closed: - return "closed" - default: - l.RLock() - errCount := l.errCount - l.RUnlock() - - if errCount > 3 { - return "error" - } - - return "connected" - } -} diff --git a/tunnel/mucp/listener.go b/tunnel/mucp/listener.go deleted file mode 100644 index 452d4770..00000000 --- a/tunnel/mucp/listener.go +++ /dev/null @@ -1,212 +0,0 @@ -package mucp - -import ( - "io" - "sync" - - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/tunnel" -) - -type tunListener struct { - // address of the listener - channel string - // token is the tunnel token - token string - // the accept channel - accept chan *session - // the tunnel closed channel - tunClosed chan bool - // the listener session - session *session - // del func to kill listener - delFunc func() - - sync.RWMutex - // the channel to close - closed chan bool -} - -func (t *tunListener) process() { - // our connection map for session - conns := make(map[string]*session) - - defer func() { - // close the sessions - for id, conn := range conns { - conn.Close() - delete(conns, id) - } - // unassign - conns = nil - }() - - for { - select { - case <-t.closed: - return - case <-t.tunClosed: - t.Close() - return - // receive a new message - case m := <-t.session.recv: - var sessionId string - var linkId string - - switch t.session.mode { - case tunnel.Multicast: - sessionId = "multicast" - linkId = "multicast" - case tunnel.Broadcast: - sessionId = "broadcast" - linkId = "broadcast" - default: - sessionId = m.session - linkId = m.link - } - - // get a session - sess, ok := conns[sessionId] - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel listener received channel %s session %s type %s exists: %t", m.channel, m.session, m.typ, ok) - } - if !ok { - // we only process open and session types - switch m.typ { - case "open", "session": - default: - continue - } - - // create a new session session - sess = &session{ - // the session key - key: []byte(t.token + m.channel + sessionId), - // the id of the remote side - tunnel: m.tunnel, - // the channel - channel: m.channel, - // the session id - session: sessionId, - // tunnel token - token: t.token, - // is loopback conn - loopback: m.loopback, - // the link the message was received on - link: linkId, - // set the connection mode - mode: t.session.mode, - // close chan - closed: make(chan bool), - // recv called by the acceptor - recv: make(chan *message, 128), - // use the internal send buffer - send: t.session.send, - // error channel - errChan: make(chan error, 1), - // set the read timeout - readTimeout: t.session.readTimeout, - } - - // save the session - conns[sessionId] = sess - - select { - case <-t.closed: - return - // send to accept chan - case t.accept <- sess: - } - } - - // an existing session was found - - switch m.typ { - case "close": - // don't close multicast sessions - if sess.mode > tunnel.Unicast { - continue - } - - // received a close message - select { - // check if the session is closed - case <-sess.closed: - // no op - delete(conns, sessionId) - default: - // only close if unicast session - // close and delete session - close(sess.closed) - delete(conns, sessionId) - } - - // continue - continue - case "session": - // operate on this - default: - // non operational type - continue - } - - // send this to the accept chan - select { - case <-sess.closed: - delete(conns, sessionId) - case sess.recv <- m: - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel listener sent to recv chan channel %s session %s type %s", m.channel, sessionId, m.typ) - } - } - } - } -} - -func (t *tunListener) Channel() string { - return t.channel -} - -// Close closes tunnel listener -func (t *tunListener) Close() error { - t.Lock() - defer t.Unlock() - - select { - case <-t.closed: - return nil - default: - // close and delete - t.delFunc() - t.session.Close() - close(t.closed) - } - return nil -} - -// Everytime accept is called we essentially block till we get a new connection -func (t *tunListener) Accept() (tunnel.Session, error) { - select { - // if the session is closed return - case <-t.closed: - return nil, io.EOF - case <-t.tunClosed: - // close the listener when the tunnel closes - return nil, io.EOF - // wait for a new connection - case c, ok := <-t.accept: - // check if the accept chan is closed - if !ok { - return nil, io.EOF - } - // return without accept - if c.mode != tunnel.Unicast { - return c, nil - } - // send back the accept - if err := c.Accept(); err != nil { - return nil, err - } - return c, nil - } -} diff --git a/tunnel/mucp/mucp.go b/tunnel/mucp/mucp.go deleted file mode 100644 index d9d8103f..00000000 --- a/tunnel/mucp/mucp.go +++ /dev/null @@ -1,1424 +0,0 @@ -package mucp - -import ( - "errors" - "math/rand" - "strings" - "sync" - "time" - - "github.com/google/uuid" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/tunnel" -) - -var ( - // DiscoverTime sets the time at which we fire discover messages - DiscoverTime = 30 * time.Second - // KeepAliveTime defines time interval we send keepalive messages to outbound links - KeepAliveTime = 30 * time.Second - // ReconnectTime defines time interval we periodically attempt to reconnect dead links - ReconnectTime = 5 * time.Second - - // create a logger - log = logger.NewHelper(logger.DefaultLogger).WithFields(map[string]interface{}{"package": "tunnel"}) -) - -// tun represents a network tunnel -type tun struct { - options tunnel.Options - - sync.RWMutex - - // the unique id for this tunnel - id string - - // tunnel token for session encryption - token string - - // to indicate if we're connected or not - connected bool - - // the send channel for all messages - send chan *message - - // close channel - closed chan bool - - // a map of sessions based on Micro-Tunnel-Channel - sessions map[string]*session - - // outbound links - links map[string]*link - - // listener - listener transport.Listener -} - -// create new tunnel on top of a link -func NewTunnel(opts ...tunnel.Option) *tun { - rand.Seed(time.Now().UnixNano()) - options := tunnel.DefaultOptions() - for _, o := range opts { - o(&options) - } - - return &tun{ - options: options, - id: options.Id, - token: options.Token, - send: make(chan *message, 128), - closed: make(chan bool), - sessions: make(map[string]*session), - links: make(map[string]*link), - } -} - -// Init initializes tunnel options -func (t *tun) Init(opts ...tunnel.Option) error { - t.Lock() - for _, o := range opts { - o(&t.options) - } - t.Unlock() - return nil -} - -// getSession returns a session from the internal session map. -// It does this based on the Micro-Tunnel-Channel and Micro-Tunnel-Session -func (t *tun) getSession(channel, session string) (*session, bool) { - // get the session - t.RLock() - s, ok := t.sessions[channel+session] - t.RUnlock() - return s, ok -} - -// delSession deletes a session if it exists -func (t *tun) delSession(channel, session string) { - t.Lock() - if s, ok := t.sessions[channel+session]; ok { - s.Close() - } - delete(t.sessions, channel+session) - t.Unlock() -} - -// listChannels returns a list of listening channels -func (t *tun) listChannels() []string { - t.RLock() - - //nolint:prealloc - var channels []string - for _, session := range t.sessions { - if session.session != "listener" { - continue - } - channels = append(channels, session.channel) - } - - t.RUnlock() - - return channels -} - -// newSession creates a new session and saves it -func (t *tun) newSession(channel, sessionId string) (*session, bool, error) { - - // new session - s := &session{ - tunnel: t.id, - channel: channel, - session: sessionId, - token: t.token, - closed: make(chan bool), - recv: make(chan *message, 128), - send: t.send, - errChan: make(chan error, 1), - key: []byte(t.token + channel + sessionId), - } - gcm, err := newCipher(s.key) - if err != nil { - return nil, false, err - } - s.gcm = gcm - - // save session - t.Lock() - _, ok := t.sessions[channel+sessionId] - if ok { - // session already exists - t.Unlock() - return nil, false, nil - } - - t.sessions[channel+sessionId] = s - t.Unlock() - - // return session - return s, true, nil -} - -// TODO: use tunnel id as part of the session -func (t *tun) newSessionId() string { - return uuid.New().String() -} - -// announce will send a message to the link to tell the other side of a channel mapping we have. -// This usually happens if someone calls Dial and sends a discover message but otherwise we -// periodically send these messages to asynchronously manage channel mappings. -func (t *tun) announce(channel, session string, link *link) { - // create the "announce" response message for a discover request - msg := &transport.Message{ - Header: map[string]string{ - "Micro-Tunnel": "announce", - "Micro-Tunnel-Id": t.id, - "Micro-Tunnel-Channel": channel, - "Micro-Tunnel-Session": session, - "Micro-Tunnel-Link": link.id, - }, - } - - // if no channel is present we've been asked to discover all channels - if len(channel) == 0 { - // get the list of channels - channels := t.listChannels() - - // if there are no channels continue - if len(channels) == 0 { - return - } - - // create a list of channels as comma separated list - channel = strings.Join(channels, ",") - // set channels as header - msg.Header["Micro-Tunnel-Channel"] = channel - } else { - // otherwise look for a single channel mapping - // looking for existing mapping as a listener - _, exists := t.getSession(channel, "listener") - if !exists { - return - } - } - - if logger.V(logger.TraceLevel, log) { - log.Debugf("Tunnel sending announce for discovery of channel(s) %s", channel) - } - // send back the announcement - if err := link.Send(msg); err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel failed to send announcement for channel(s) %s message: %v", channel, err) - } - } -} - -// manage monitors outbound links and attempts to reconnect to the failed ones -func (t *tun) manage(reconnect time.Duration) { - r := time.NewTicker(reconnect) - defer r.Stop() - - for { - select { - case <-t.closed: - return - case <-r.C: - t.manageLinks() - } - } -} - -// manageLink sends channel discover requests periodically and -// keepalive messages to link -func (t *tun) manageLink(link *link) { - keepalive := time.NewTicker(KeepAliveTime) - defer keepalive.Stop() - discover := time.NewTicker(DiscoverTime) - defer discover.Stop() - - wait := func(d time.Duration) { - // jitter - j := rand.Int63n(int64(d.Seconds() / 2.0)) - time.Sleep(time.Duration(j) * time.Second) - } - - for { - select { - case <-t.closed: - return - case <-link.closed: - return - case <-discover.C: - // wait half the discover time - wait(DiscoverTime) - - // send a discovery message to the link - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel sending discover to link: %v", link.Remote()) - } - if err := t.sendMsg("discover", link); err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel failed to send discover to link %s: %v", link.Remote(), err) - } - } - case <-keepalive.C: - // wait half the keepalive time - wait(KeepAliveTime) - - // send keepalive message - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel sending keepalive to link: %v", link.Remote()) - } - if err := t.sendMsg("keepalive", link); err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel error sending keepalive to link %v: %v", link.Remote(), err) - } - t.delLink(link.Remote()) - return - } - } - } -} - -// manageLinks is a function that can be called to immediately to link setup -// it purges dead links while generating new links for any nodes not connected -func (t *tun) manageLinks() { - delLinks := make(map[*link]string) - connected := make(map[string]bool) - - t.RLock() - - // get list of nodes from options - nodes := t.options.Nodes - - // check the link status and purge dead links - for node, link := range t.links { - // check link status - switch link.State() { - case "closed", "error": - delLinks[link] = node - default: - connected[node] = true - } - } - - t.RUnlock() - - // build a list of links to connect to - var connect []string - - for _, node := range nodes { - // check if we're connected - if _, ok := connected[node]; ok { - continue - } - // add nodes to connect o - connect = append(connect, node) - } - - // delete the dead links - if len(delLinks) > 0 { - t.Lock() - - for link, node := range delLinks { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel deleting dead link for %s", node) - } - // check if the link exists - l, ok := t.links[node] - if ok { - // close and delete - l.Close() - delete(t.links, node) - } - - // if the link does not match our own - if l != link { - // close our link just in case - link.Close() - } - } - - t.Unlock() - } - - var wg sync.WaitGroup - - // establish new links - - for _, node := range connect { - wg.Add(1) - - go func(node string) { - defer wg.Done() - - // create new link - // if we're using quic it should be a max 10 second handshake period - link, err := t.setupLink(node) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel failed to setup node link to %s: %v", node, err) - } - return - } - - t.Lock() - - // just check nothing else was setup in the interim - if _, ok := t.links[node]; ok { - link.Close() - t.Unlock() - return - } - - // save the link - t.links[node] = link - - t.Unlock() - }(node) - } - - // wait for all threads to finish - wg.Wait() -} - -// process outgoing messages sent by all local sessions -func (t *tun) process() { - // manage the send buffer - // all pseudo sessions throw everything down this - for { - select { - case msg := <-t.send: - // build a list of links to send to - var sendTo []*link - var err error - - t.RLock() - - // build the list of links ot send to - for _, link := range t.links { - // get the values we need - link.RLock() - id := link.id - connected := link.connected - loopback := link.loopback - _, exists := link.channels[msg.channel] - link.RUnlock() - - // if the link is not connected skip it - if !connected { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Link for node %s not connected", id) - } - err = tunnel.ErrLinkDisconnected - continue - } - - // if the link was a loopback accepted connection - // and the message is being sent outbound via - // a dialled connection don't use this link - if loopback && msg.outbound { - err = tunnel.ErrLinkLoopback - continue - } - - // if the message was being returned by the loopback listener - // send it back up the loopback link only - if msg.loopback && !loopback { - err = tunnel.ErrLinkRemote - continue - } - - // check the multicast mappings - if msg.mode == tunnel.Multicast { - // channel mapping not found in link - if !exists { - continue - } - } else { - // if we're picking the link check the id - // this is where we explicitly set the link - // in a message received via the listen method - if len(msg.link) > 0 && id != msg.link { - err = tunnel.ErrLinkNotFound - continue - } - } - - // add to link list - sendTo = append(sendTo, link) - } - - t.RUnlock() - - // no links to send to - if len(sendTo) == 0 { - if logger.V(logger.DebugLevel, log) { - log.Debugf("No links to send message type: %s channel: %s", msg.typ, msg.channel) - } - t.respond(msg, err) - continue - } - - // send the message - go t.sendTo(sendTo, msg) - case <-t.closed: - return - } - } -} - -// send response back for a message to the caller -func (t *tun) respond(msg *message, err error) { - select { - case msg.errChan <- err: - default: - } -} - -// sendTo sends a message to the chosen links -func (t *tun) sendTo(links []*link, msg *message) error { - // the function that sends the actual message - send := func(link *link, msg *transport.Message) error { - if err := link.Send(msg); err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel error sending %+v to %s: %v", msg.Header, link.Remote(), err) - } - t.delLink(link.Remote()) - return err - } - return nil - } - - newMsg := &transport.Message{ - Header: make(map[string]string), - } - - // set the data - if msg.data != nil { - for k, v := range msg.data.Header { - newMsg.Header[k] = v - } - newMsg.Body = msg.data.Body - } - - // set message head - newMsg.Header["Micro-Tunnel"] = msg.typ - // set the tunnel id on the outgoing message - newMsg.Header["Micro-Tunnel-Id"] = msg.tunnel - // set the tunnel channel on the outgoing message - newMsg.Header["Micro-Tunnel-Channel"] = msg.channel - // set the session id - newMsg.Header["Micro-Tunnel-Session"] = msg.session - - // error channel for call - errChan := make(chan error, len(links)) - - // execute in parallel - sendTo := func(l *link, m *transport.Message, errChan chan error) { - errChan <- send(l, m) - } - - // send the message - for _, link := range links { - // send the message via the current link - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel sending %+v to %s", newMsg.Header, link.Remote()) - } - - // blast it in a go routine since its multicast/broadcast - if msg.mode > tunnel.Unicast { - // make a copy - m := &transport.Message{ - Header: make(map[string]string), - Body: make([]byte, len(newMsg.Body)), - } - copy(m.Body, newMsg.Body) - for k, v := range newMsg.Header { - m.Header[k] = v - } - - go sendTo(link, m, errChan) - - continue - } - - // otherwise send as unicast - if err := send(link, newMsg); err != nil { - // put in the error chan if it failed - errChan <- err - continue - } - - // sent successfully so just return - t.respond(msg, nil) - return nil - } - - // either all unicast attempts failed or we're - // checking the multicast/broadcast attempts - - var err error - - // check all the errors - for i := 0; i < len(links); i++ { - err = <-errChan - // success - if err == nil { - break - } - } - - // return error. it's non blocking - t.respond(msg, err) - return err -} - -func (t *tun) delLink(remote string) { - t.Lock() - - // get the link - for id, link := range t.links { - if link.id != remote { - continue - } - // close and delete - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel deleting link node: %s remote: %s", id, link.Remote()) - } - link.Close() - delete(t.links, id) - } - - t.Unlock() -} - -// process incoming messages -func (t *tun) listen(link *link) { - // remove the link on exit - defer func() { - t.delLink(link.Remote()) - }() - - // let us know if its a loopback - var loopback bool - var connected bool - - // set the connected value - link.RLock() - connected = link.connected - link.RUnlock() - - for { - // process anything via the net interface - msg := new(transport.Message) - if err := link.Recv(msg); err != nil { - log.Debugf("Tunnel link %s receive error: %v", link.Remote(), err) - return - } - - // TODO: figure out network authentication - // for now we use tunnel token to encrypt/decrypt - // session communication, but we will probably need - // some sort of network authentication (token) to avoid - // having rogue actors spamming the network - - // message type - mtype := msg.Header["Micro-Tunnel"] - // the tunnel id - id := msg.Header["Micro-Tunnel-Id"] - // the tunnel channel - channel := msg.Header["Micro-Tunnel-Channel"] - // the session id - sessionId := msg.Header["Micro-Tunnel-Session"] - - // if its not connected throw away the link - // the first message we process needs to be connect - if !connected && mtype != "connect" { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel link %s not connected", link.id) - } - return - } - - // this state machine block handles the only message types - // that we know or care about; connect, close, open, accept, - // discover, announce, session, keepalive - switch mtype { - case "connect": - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel link %s received connect message", link.Remote()) - } - - link.Lock() - - // check if we're connecting to ourselves? - if id == t.id { - link.loopback = true - loopback = true - } - - // set to remote node - link.id = link.Remote() - // set as connected - link.connected = true - connected = true - - link.Unlock() - - // save the link once connected - t.Lock() - t.links[link.Remote()] = link - t.Unlock() - - // send back an announcement of our channels discovery - go t.announce("", "", link) - // ask for the things on the other wise - go t.sendMsg("discover", link) - // nothing more to do - continue - case "close": - // if there is no channel then we close the link - // as its a signal from the other side to close the connection - if len(channel) == 0 { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel link %s received close message", link.Remote()) - } - return - } - - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel link %s received close message for %s", link.Remote(), channel) - } - // the entire listener was closed by the remote side so we need to - // remove the channel mapping for it. should we also close sessions? - if sessionId == "listener" { - link.delChannel(channel) - // TODO: find all the non listener unicast sessions - // and close them. think aboud edge cases first - continue - } - - // assuming there's a channel and session - // try get the dialing socket - s, exists := t.getSession(channel, sessionId) - if exists && !loopback { - // only delete the session if its unicast - // otherwise ignore close on the multicast - if s.mode == tunnel.Unicast { - // only delete this if its unicast - // but not if its a loopback conn - t.delSession(channel, sessionId) - continue - } - } - // otherwise its a session mapping of sorts - case "keepalive": - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel link %s received keepalive", link.Remote()) - } - // save the keepalive - link.keepalive() - continue - // a new connection dialled outbound - case "open": - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel link %s received open %s %s", link.id, channel, sessionId) - } - // we just let it pass through to be processed - // an accept returned by the listener - case "accept": - s, exists := t.getSession(channel, sessionId) - // just set accepted on anything not unicast - if exists && s.mode > tunnel.Unicast { - s.accepted = true - continue - } - // if its already accepted move on - if exists && s.accepted { - continue - } - // otherwise we're going to process to accept - // a continued session - case "session": - // process message - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel received %+v from %s", msg.Header, link.Remote()) - } - // an announcement of a channel listener - case "announce": - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel received %+v from %s", msg.Header, link.Remote()) - } - // process the announcement - channels := strings.Split(channel, ",") - - // update mapping in the link - link.setChannel(channels...) - - // this was an announcement not intended for anything - // if the dialing side sent "discover" then a session - // id would be present. We skip in case of multicast. - switch sessionId { - case "listener", "multicast", "": - continue - } - - // get the session that asked for the discovery - s, exists := t.getSession(channel, sessionId) - if exists { - // don't bother it's already discovered - if s.discovered { - continue - } - - msg := &message{ - typ: "announce", - tunnel: id, - channel: channel, - session: sessionId, - link: link.id, - } - - // send the announce back to the caller - select { - case <-s.closed: - case s.recv <- msg: - } - } - continue - case "discover": - // send back an announcement - go t.announce(channel, sessionId, link) - continue - default: - // blackhole it - continue - } - - // strip tunnel message header - for k := range msg.Header { - if strings.HasPrefix(k, "Micro-Tunnel") { - delete(msg.Header, k) - } - } - - // if the session id is blank there's nothing we can do - // TODO: check this is the case, is there any reason - // why we'd have a blank session? Is the tunnel - // used for some other purpose? - if len(channel) == 0 || len(sessionId) == 0 { - continue - } - - var s *session - var exists bool - - // If its a loopback connection then we've enabled link direction - // listening side is used for listening, the dialling side for dialling - switch { - case loopback, mtype == "open": - s, exists = t.getSession(channel, "listener") - // only return accept to the session - case mtype == "accept": - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel received accept message for channel: %s session: %s", channel, sessionId) - } - s, exists = t.getSession(channel, sessionId) - if exists && s.accepted { - continue - } - default: - // get the session based on the tunnel id and session - // this could be something we dialed in which case - // we have a session for it otherwise its a listener - s, exists = t.getSession(channel, sessionId) - if !exists { - // try get it based on just the tunnel id - // the assumption here is that a listener - // has no session but its set a listener session - s, exists = t.getSession(channel, "listener") - } - } - - // bail if no session or listener has been found - if !exists { - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel skipping no channel: %s session: %s exists", channel, sessionId) - } - // drop it, we don't care about - // messages we don't know about - continue - } - - // is the session closed? - select { - case <-s.closed: - // closed - delete(t.sessions, channel) - continue - default: - // otherwise process - } - if logger.V(logger.TraceLevel, log) { - log.Tracef("Tunnel using channel: %s session: %s type: %s", s.channel, s.session, mtype) - } - // construct a new transport message - tmsg := &transport.Message{ - Header: msg.Header, - Body: msg.Body, - } - - // construct the internal message - imsg := &message{ - tunnel: id, - typ: mtype, - channel: channel, - session: sessionId, - mode: s.mode, - data: tmsg, - link: link.id, - loopback: loopback, - errChan: make(chan error, 1), - } - - // append to recv backlog - // we don't block if we can't pass it on - select { - case s.recv <- imsg: - default: - } - } -} - -func (t *tun) sendMsg(method string, link *link) error { - return link.Send(&transport.Message{ - Header: map[string]string{ - "Micro-Tunnel": method, - "Micro-Tunnel-Id": t.id, - }, - }) -} - -// setupLink connects to node and returns link if successful -// It returns error if the link failed to be established -func (t *tun) setupLink(node string) (*link, error) { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel setting up link: %s", node) - } - c, err := t.options.Transport.Dial(node) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel failed to connect to %s: %v", node, err) - } - return nil, err - } - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel connected to %s", node) - } - // create a new link - link := newLink(c) - - // set link id to remote side - link.Lock() - link.id = c.Remote() - link.Unlock() - - // send the first connect message - if err := t.sendMsg("connect", link); err != nil { - link.Close() - return nil, err - } - - // we made the outbound connection - // and sent the connect message - link.connected = true - - // process incoming messages - go t.listen(link) - - // manage keepalives and discovery messages - go t.manageLink(link) - - return link, nil -} - -func (t *tun) setupLinks() { - var wg sync.WaitGroup - - for _, node := range t.options.Nodes { - wg.Add(1) - - go func(node string) { - defer wg.Done() - - // we're not trying to fix existing links - if _, ok := t.links[node]; ok { - return - } - - // create new link - link, err := t.setupLink(node) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel failed to setup node link to %s: %v", node, err) - } - return - } - - // save the link - t.links[node] = link - }(node) - } - - // wait for all threads to finish - wg.Wait() -} - -// connect the tunnel to all the nodes and listen for incoming tunnel connections -func (t *tun) connect() error { - l, err := t.options.Transport.Listen(t.options.Address) - if err != nil { - return err - } - - // save the listener - t.listener = l - - go func() { - // accept inbound connections - err := l.Accept(func(sock transport.Socket) { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel accepted connection from %s", sock.Remote()) - } - // create a new link - link := newLink(sock) - - // manage the link - go t.manageLink(link) - - // listen for inbound messages. - // only save the link once connected. - // we do this inside liste - t.listen(link) - }) - - t.RLock() - defer t.RUnlock() - - // still connected but the tunnel died - if err != nil && t.connected { - log.Errorf("Tunnel listener died: %v", err) - } - }() - - return nil -} - -// Connect the tunnel -func (t *tun) Connect() error { - t.Lock() - defer t.Unlock() - - // already connected - if t.connected { - // do it immediately - t.setupLinks() - // setup links - return nil - } - - // connect the tunnel: start the listener - if err := t.connect(); err != nil { - return err - } - - // set as connected - t.connected = true - // create new close channel - t.closed = make(chan bool) - - // process outbound messages to be sent - // process sends to all links - go t.process() - - // call setup before managing them - t.setupLinks() - - // manage the links - go t.manage(ReconnectTime) - - return nil -} - -func (t *tun) close() error { - // close all the sessions - for id, s := range t.sessions { - s.Close() - delete(t.sessions, id) - } - - // close all the links - for node, link := range t.links { - link.Send(&transport.Message{ - Header: map[string]string{ - "Micro-Tunnel": "close", - "Micro-Tunnel-Id": t.id, - }, - }) - link.Close() - delete(t.links, node) - } - - // close the listener - // this appears to be blocking - return t.listener.Close() -} - -// pickLink will pick the best link based on connectivity, delay, rate and length -func (t *tun) pickLink(links []*link) *link { - var metric float64 - var chosen *link - - // find the best link - for i, link := range links { - // don't use disconnected or errored links - if link.State() != "connected" { - continue - } - - // skip the loopback - if link.Loopback() { - continue - } - - // get the link state info - d := float64(link.Delay()) - l := float64(link.Length()) - r := link.Rate() - - // metric = delay x length x rate - m := d * l * r - - // first link so just and go - if i == 0 { - metric = m - chosen = link - continue - } - - // we found a better metric - if m < metric { - metric = m - chosen = link - } - } - - // if there's no link we're just going to mess around - if chosen == nil { - i := rand.Intn(len(links)) - return links[i] - } - - // we chose the link with; - // the lowest delay e.g least messages queued - // the lowest rate e.g the least messages flowing - // the lowest length e.g the smallest roundtrip time - return chosen -} - -func (t *tun) Address() string { - t.RLock() - defer t.RUnlock() - - if !t.connected { - return t.options.Address - } - - return t.listener.Addr() -} - -// Close the tunnel -func (t *tun) Close() error { - t.Lock() - defer t.Unlock() - - if !t.connected { - return nil - } - - if logger.V(logger.DebugLevel, log) { - log.Debug("Tunnel closing") - } - - select { - case <-t.closed: - return nil - default: - close(t.closed) - t.connected = false - } - - // send a close message - // we don't close the link - // just the tunnel - return t.close() -} - -// Dial an address -func (t *tun) Dial(channel string, opts ...tunnel.DialOption) (tunnel.Session, error) { - // get the options - options := tunnel.DialOptions{ - Timeout: tunnel.DefaultDialTimeout, - Wait: true, - } - - for _, o := range opts { - o(&options) - } - - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel dialing %s", channel) - } - - // create a new session - c, ok, err := t.newSession(channel, t.newSessionId()) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Error(err) - } - return nil, err - } else if !ok { - return nil, errors.New("error dialing " + channel) - } - - // set remote - c.remote = channel - // set local - c.local = "local" - // outbound session - c.outbound = true - // set the mode of connection unicast/multicast/broadcast - c.mode = options.Mode - // set the dial timeout - c.dialTimeout = options.Timeout - // set read timeout set to never - c.readTimeout = time.Duration(-1) - // set the link - c.link = options.Link - - var links []*link - // did we measure the rtt - var measured bool - - t.RLock() - - // non multicast so we need to find the link - for _, link := range t.links { - // use the link specified it its available - if len(c.link) > 0 && link.id != c.link { - continue - } - - // get the channel - lastMapped := link.getChannel(channel) - - // we have at least one channel mapping - if !lastMapped.IsZero() { - links = append(links, link) - c.discovered = true - } - } - - t.RUnlock() - - // link option was specified to pick the link - if len(options.Link) > 0 { - // link not found and one was specified so error out - if len(links) == 0 { - // delete session and return error - t.delSession(c.channel, c.session) - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel deleting session %s %s: %v", c.session, c.channel, tunnel.ErrLinkNotFound) - } - return nil, tunnel.ErrLinkNotFound - } - - // assume discovered because we picked - c.discovered = true - - // link asked for and found and now - // we've been asked not to wait so return - if !options.Wait { - c.accepted = true - return c, nil - } - } - - // discovered so set the link if not multicast - if c.discovered && c.mode == tunnel.Unicast { - // pick a link if not specified - if len(c.link) == 0 { - // pickLink will pick the best link - link := t.pickLink(links) - // set the link - c.link = link.id - } - } - - // if its not already discovered we need to attempt to do so - if !c.discovered { - // piggy back roundtrip - nowRTT := time.Now() - - // attempt to discover the link - err := c.Discover() - if err != nil { - t.delSession(c.channel, c.session) - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel deleting session %s %s: %v", c.session, c.channel, err) - } - return nil, err - } - - // set roundtrip - d := time.Since(nowRTT) - - // set the link time - t.RLock() - link, ok := t.links[c.link] - t.RUnlock() - - if ok { - // set the rountrip time - link.setRTT(d) - // set measured to true - measured = true - } - } - - // return early if its not unicast - // we will not wait for "open" for multicast - // and we will not wait it told not to - if c.mode != tunnel.Unicast || !options.Wait { - return c, nil - } - - // Note: we go no further for multicast or broadcast. - // This is a unicast session so we call "open" and wait - // for an "accept" - - // reset now in case we use it - now := time.Now() - - // try to open the session - if err := c.Open(); err != nil { - // delete the session - t.delSession(c.channel, c.session) - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel deleting session %s %s: %v", c.session, c.channel, err) - } - return nil, err - } - - // set time take to open - d := time.Since(now) - - // if we haven't measured the roundtrip do it now - if !measured { - // set the link time - t.RLock() - link, ok := t.links[c.link] - t.RUnlock() - - if ok { - // set the rountrip time - link.setRTT(d) - } - } - - return c, nil -} - -// Accept a connection on the address -func (t *tun) Listen(channel string, opts ...tunnel.ListenOption) (tunnel.Listener, error) { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel listening on %s", channel) - } - options := tunnel.ListenOptions{ - // Read timeout defaults to never - Timeout: time.Duration(-1), - } - - for _, o := range opts { - o(&options) - } - - // create a new session by hashing the address - c, ok, err := t.newSession(channel, "listener") - if err != nil { - if logger.V(logger.ErrorLevel, log) { - log.Error(err) - } - return nil, err - } else if !ok { - return nil, errors.New("already listening on " + channel) - } - - // delete function removes the session when closed - delFunc := func() { - t.delSession(channel, "listener") - } - - // set remote. it will be replaced by the first message received - c.remote = "remote" - // set local - c.local = channel - // set mode - c.mode = options.Mode - // set the timeout - c.readTimeout = options.Timeout - - tl := &tunListener{ - channel: channel, - // tunnel token - token: t.token, - // the accept channel - accept: make(chan *session, 128), - // the channel to close - closed: make(chan bool), - // tunnel closed channel - tunClosed: t.closed, - // the listener session - session: c, - // delete session - delFunc: delFunc, - } - - // this kicks off the internal message processor - // for the listener so it can create pseudo sessions - // per session if they do not exist or pass messages - // to the existign sessions - go tl.process() - - // return the listener - return tl, nil -} - -func (t *tun) Links() []tunnel.Link { - t.RLock() - links := make([]tunnel.Link, 0, len(t.links)) - - for _, link := range t.links { - links = append(links, link) - } - - t.RUnlock() - - return links -} - -func (t *tun) String() string { - return "mucp" -} diff --git a/tunnel/mucp/mucp_test.go b/tunnel/mucp/mucp_test.go deleted file mode 100644 index 4c022250..00000000 --- a/tunnel/mucp/mucp_test.go +++ /dev/null @@ -1,347 +0,0 @@ -package mucp - -import ( - "os" - "sync" - "testing" - "time" - - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/tunnel" -) - -func testBrokenTunAccept(t *testing.T, tun tunnel.Tunnel, wait chan bool, wg *sync.WaitGroup) { - defer wg.Done() - - // listen on some virtual address - tl, err := tun.Listen("test-tunnel") - if err != nil { - t.Fatal(err) - } - - // receiver ready; notify sender - wait <- true - - // accept a connection - c, err := tl.Accept() - if err != nil { - t.Fatal(err) - } - - // accept the message and close the tunnel - // we do this to simulate loss of network connection - m := new(transport.Message) - if err := c.Recv(m); err != nil { - t.Fatal(err) - } - - // close all the links - for _, link := range tun.Links() { - link.Close() - } - - // receiver ready; notify sender - wait <- true - - // accept the message - m = new(transport.Message) - if err := c.Recv(m); err != nil { - t.Fatal(err) - } - - // notify the sender we have received - wait <- true -} - -func testBrokenTunSend(t *testing.T, tun tunnel.Tunnel, wait chan bool, wg *sync.WaitGroup, reconnect time.Duration) { - defer wg.Done() - - // wait for the listener to get ready - <-wait - - // dial a new session - c, err := tun.Dial("test-tunnel") - if err != nil { - t.Fatal(err) - } - defer c.Close() - - m := transport.Message{ - Header: map[string]string{ - "test": "send", - }, - } - - // send the message - if err := c.Send(&m); err != nil { - t.Fatal(err) - } - - // wait for the listener to get ready - <-wait - - // give it time to reconnect - time.Sleep(reconnect) - - // send the message - if err := c.Send(&m); err != nil { - t.Fatal(err) - } - - // wait for the listener to receive the message - // c.Send merely enqueues the message to the link send queue and returns - // in order to verify it was received we wait for the listener to tell us - <-wait -} - -// testAccept will accept connections on the transport, create a new link and tunnel on top -func testAccept(t *testing.T, tun tunnel.Tunnel, wait chan bool, wg *sync.WaitGroup) { - defer wg.Done() - - // listen on some virtual address - tl, err := tun.Listen("test-tunnel") - if err != nil { - t.Fatal(err) - } - - // receiver ready; notify sender - wait <- true - - // accept a connection - c, err := tl.Accept() - if err != nil { - t.Fatal(err) - } - - // get a message - // accept the message - m := new(transport.Message) - if err := c.Recv(m); err != nil { - t.Fatal(err) - } - - if v := m.Header["test"]; v != "send" { - t.Fatalf("Accept side expected test:send header. Received: %s", v) - } - - // now respond - m.Header["test"] = "accept" - if err := c.Send(m); err != nil { - t.Fatal(err) - } - - wait <- true - - return -} - -// testSend will create a new link to an address and then a tunnel on top -func testSend(t *testing.T, tun tunnel.Tunnel, wait chan bool, wg *sync.WaitGroup) { - defer wg.Done() - - // wait for the listener to get ready - <-wait - - // dial a new session - c, err := tun.Dial("test-tunnel") - if err != nil { - t.Fatal(err) - } - defer c.Close() - - m := transport.Message{ - Header: map[string]string{ - "test": "send", - }, - } - - // send the message - if err := c.Send(&m); err != nil { - t.Fatal(err) - } - - // now wait for the response - mr := new(transport.Message) - if err := c.Recv(mr); err != nil { - t.Fatal(err) - } - - <-wait - - if v := mr.Header["test"]; v != "accept" { - t.Fatalf("Message not received from accepted side. Received: %s", v) - } -} - -func TestTunnel(t *testing.T) { - // create a new tunnel client - tunA := NewTunnel( - tunnel.Address("127.0.0.1:9096"), - tunnel.Nodes("127.0.0.1:9097"), - ) - - // create a new tunnel server - tunB := NewTunnel( - tunnel.Address("127.0.0.1:9097"), - ) - - // start tunB - err := tunB.Connect() - if err != nil { - t.Fatal(err) - } - defer tunB.Close() - - // start tunA - err = tunA.Connect() - if err != nil { - t.Fatal(err) - } - defer tunA.Close() - - wait := make(chan bool) - - var wg sync.WaitGroup - - wg.Add(1) - // start the listener - go testAccept(t, tunB, wait, &wg) - - wg.Add(1) - // start the client - go testSend(t, tunA, wait, &wg) - - // wait until done - wg.Wait() -} - -func TestLoopbackTunnel(t *testing.T) { - // create a new tunnel - tun := NewTunnel( - tunnel.Address("127.0.0.1:9096"), - tunnel.Nodes("127.0.0.1:9096"), - ) - - // start tunnel - err := tun.Connect() - if err != nil { - t.Fatal(err) - } - defer tun.Close() - - time.Sleep(500 * time.Millisecond) - - wait := make(chan bool) - - var wg sync.WaitGroup - - wg.Add(1) - // start the listener - go testAccept(t, tun, wait, &wg) - - wg.Add(1) - // start the client - go testSend(t, tun, wait, &wg) - - // wait until done - wg.Wait() -} - -func TestTunnelRTTRate(t *testing.T) { - // create a new tunnel client - tunA := NewTunnel( - tunnel.Address("127.0.0.1:9096"), - tunnel.Nodes("127.0.0.1:9097"), - ) - - // create a new tunnel server - tunB := NewTunnel( - tunnel.Address("127.0.0.1:9097"), - ) - - // start tunB - err := tunB.Connect() - if err != nil { - t.Fatal(err) - } - defer tunB.Close() - - // start tunA - err = tunA.Connect() - if err != nil { - t.Fatal(err) - } - defer tunA.Close() - - wait := make(chan bool) - - var wg sync.WaitGroup - - wg.Add(1) - // start the listener - go testAccept(t, tunB, wait, &wg) - - wg.Add(1) - // start the client - go testSend(t, tunA, wait, &wg) - - // wait until done - wg.Wait() - - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { - // only needed for debug - for _, link := range tunA.Links() { - t.Logf("Link %s length %v rate %v", link.Id(), link.Length(), link.Rate()) - } - - for _, link := range tunB.Links() { - t.Logf("Link %s length %v rate %v", link.Id(), link.Length(), link.Rate()) - } - } -} - -func TestReconnectTunnel(t *testing.T) { - // we manually override the tunnel.ReconnectTime value here - // this is so that we make the reconnects faster than the default 5s - ReconnectTime = 200 * time.Millisecond - - // create a new tunnel client - tunA := NewTunnel( - tunnel.Address("127.0.0.1:9098"), - tunnel.Nodes("127.0.0.1:9099"), - ) - - // create a new tunnel server - tunB := NewTunnel( - tunnel.Address("127.0.0.1:9099"), - ) - - // start tunnel - err := tunB.Connect() - if err != nil { - t.Fatal(err) - } - defer tunB.Close() - - // start tunnel - err = tunA.Connect() - if err != nil { - t.Fatal(err) - } - defer tunA.Close() - - wait := make(chan bool) - - var wg sync.WaitGroup - - wg.Add(1) - // start tunnel listener - go testBrokenTunAccept(t, tunB, wait, &wg) - - wg.Add(1) - // start tunnel sender - go testBrokenTunSend(t, tunA, wait, &wg, ReconnectTime*5) - - // wait until done - wg.Wait() -} diff --git a/tunnel/mucp/session.go b/tunnel/mucp/session.go deleted file mode 100644 index 38a59d25..00000000 --- a/tunnel/mucp/session.go +++ /dev/null @@ -1,503 +0,0 @@ -package mucp - -import ( - "crypto/cipher" - "encoding/base32" - "io" - "sync" - "time" - - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/tunnel" -) - -// session is our pseudo session for transport.Socket -type session struct { - // the tunnel id - tunnel string - // the channel name - channel string - // the session id based on Micro.Tunnel-Session - session string - // token is the session token - token string - // closed - closed chan bool - // remote addr - remote string - // local addr - local string - // send chan - send chan *message - // recv chan - recv chan *message - // if the discovery worked - discovered bool - // if the session was accepted - accepted bool - // outbound marks the session as outbound dialled connection - outbound bool - // lookback marks the session as a loopback on the inbound - loopback bool - // mode of the connection - mode tunnel.Mode - // the dial timeout - dialTimeout time.Duration - // the read timeout - readTimeout time.Duration - // the link on which this message was received - link string - // the error response - errChan chan error - // key for session encryption - key []byte - // cipher for session - gcm cipher.AEAD - sync.RWMutex -} - -// message is sent over the send channel -type message struct { - // type of message - typ string - // tunnel id - tunnel string - // channel name - channel string - // the session id - session string - // outbound marks the message as outbound - outbound bool - // loopback marks the message intended for loopback - loopback bool - // mode of the connection - mode tunnel.Mode - // the link to send the message on - link string - // transport data - data *transport.Message - // the error channel - errChan chan error -} - -func (s *session) Remote() string { - return s.remote -} - -func (s *session) Local() string { - return s.local -} - -func (s *session) Link() string { - return s.link -} - -func (s *session) Id() string { - return s.session -} - -func (s *session) Channel() string { - return s.channel -} - -// newMessage creates a new message based on the session -func (s *session) newMessage(typ string) *message { - return &message{ - typ: typ, - tunnel: s.tunnel, - channel: s.channel, - session: s.session, - outbound: s.outbound, - loopback: s.loopback, - mode: s.mode, - link: s.link, - errChan: s.errChan, - } -} - -func (s *session) sendMsg(msg *message) error { - select { - case <-s.closed: - return io.EOF - case s.send <- msg: - return nil - } -} - -func (s *session) wait(msg *message) error { - // wait for an error response - select { - case err := <-msg.errChan: - if err != nil { - return err - } - case <-s.closed: - return io.EOF - } - - return nil -} - -// waitFor waits for the message type required until the timeout specified -func (s *session) waitFor(msgType string, timeout time.Duration) (*message, error) { - now := time.Now() - - after := func(timeout time.Duration) <-chan time.Time { - if timeout < time.Duration(0) { - return nil - } - - // get the delta - d := time.Since(now) - - // dial timeout minus time since - wait := timeout - d - - if wait < time.Duration(0) { - wait = time.Duration(0) - } - - return time.After(wait) - } - - // wait for the message type - for { - select { - case msg := <-s.recv: - // there may be no message type - if len(msgType) == 0 { - return msg, nil - } - - // ignore what we don't want - if msg.typ != msgType { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel received non %s message in waiting for %s", msg.typ, msgType) - } - continue - } - - // got the message - return msg, nil - case <-after(timeout): - return nil, tunnel.ErrReadTimeout - case <-s.closed: - // check pending message queue - select { - case msg := <-s.recv: - // there may be no message type - if len(msgType) == 0 { - return msg, nil - } - - // ignore what we don't want - if msg.typ != msgType { - if logger.V(logger.DebugLevel, log) { - log.Debugf("Tunnel received non %s message in waiting for %s", msg.typ, msgType) - } - continue - } - - // got the message - return msg, nil - default: - // non blocking - } - return nil, io.EOF - } - } -} - -// Discover attempts to discover the link for a specific channel. -// This is only used by the tunnel.Dial when first connecting. -func (s *session) Discover() error { - // create a new discovery message for this channel - msg := s.newMessage("discover") - // broadcast the message to all links - msg.mode = tunnel.Broadcast - // its an outbound connection since we're dialling - msg.outbound = true - // don't set the link since we don't know where it is - msg.link = "" - - // if multicast then set that as session - if s.mode == tunnel.Multicast { - msg.session = "multicast" - } - - // send discover message - if err := s.sendMsg(msg); err != nil { - return err - } - - // set time now - now := time.Now() - - // after strips down the dial timeout - after := func() time.Duration { - d := time.Since(now) - // dial timeout minus time since - wait := s.dialTimeout - d - // make sure its always > 0 - if wait < time.Duration(0) { - return time.Duration(0) - } - return wait - } - - // the discover message is sent out, now - // wait to hear back about the sent message - select { - case <-time.After(after()): - return tunnel.ErrDialTimeout - case err := <-s.errChan: - if err != nil { - return err - } - } - - // bail early if its not unicast - // we don't need to wait for the announce - if s.mode != tunnel.Unicast { - s.discovered = true - s.accepted = true - return nil - } - - // wait for announce - _, err := s.waitFor("announce", after()) - if err != nil { - return err - } - - // set discovered - s.discovered = true - - return nil -} - -// Open will fire the open message for the session. This is called by the dialler. -// This is to indicate that we want to create a new session. -func (s *session) Open() error { - // create a new message - msg := s.newMessage("open") - - // send open message - if err := s.sendMsg(msg); err != nil { - return err - } - - // wait for an error response for send - if err := s.wait(msg); err != nil { - return err - } - - // now wait for the accept message to be returned - msg, err := s.waitFor("accept", s.dialTimeout) - if err != nil { - return err - } - - // set to accepted - s.accepted = true - // set link - s.link = msg.link - - return nil -} - -// Accept sends the accept response to an open message from a dialled connection -func (s *session) Accept() error { - msg := s.newMessage("accept") - - // send the accept message - if err := s.sendMsg(msg); err != nil { - return err - } - - // wait for send response - return s.wait(msg) -} - -// Announce sends an announcement to notify that this session exists. -// This is primarily used by the listener. -func (s *session) Announce() error { - msg := s.newMessage("announce") - // we don't need an error back - msg.errChan = nil - // announce to all - msg.mode = tunnel.Broadcast - // we don't need the link - msg.link = "" - - // send announce message - return s.sendMsg(msg) -} - -// Send is used to send a message -func (s *session) Send(m *transport.Message) error { - var err error - - s.RLock() - gcm := s.gcm - s.RUnlock() - - if gcm == nil { - gcm, err = newCipher(s.key) - if err != nil { - return err - } - s.Lock() - s.gcm = gcm - s.Unlock() - } - // encrypt the transport message payload - body, err := Encrypt(gcm, m.Body) - if err != nil { - log.Debugf("failed to encrypt message body: %v", err) - return err - } - - // make copy, without rehash and realloc - data := &transport.Message{ - Header: make(map[string]string, len(m.Header)), - Body: body, - } - - // encrypt all the headers - for k, v := range m.Header { - // encrypt the transport message payload - val, err := Encrypt(s.gcm, []byte(v)) - if err != nil { - log.Debugf("failed to encrypt message header %s: %v", k, err) - return err - } - // add the encrypted header value - data.Header[k] = base32.StdEncoding.EncodeToString(val) - } - - // create a new message - msg := s.newMessage("session") - // set the data - msg.data = data - - // if multicast don't set the link - if s.mode != tunnel.Unicast { - msg.link = "" - } - - if logger.V(logger.TraceLevel, log) { - log.Tracef("Appending to send backlog: %v", msg) - } - // send the actual message - if err := s.sendMsg(msg); err != nil { - return err - } - - // wait for an error response - return s.wait(msg) -} - -// Recv is used to receive a message -func (s *session) Recv(m *transport.Message) error { - var msg *message - - msg, err := s.waitFor("", s.readTimeout) - if err != nil { - return err - } - - // check the error if one exists - select { - case err := <-msg.errChan: - return err - default: - } - - if logger.V(logger.TraceLevel, log) { - log.Tracef("Received from recv backlog: %v", msg) - } - - gcm, err := newCipher([]byte(s.token + s.channel + msg.session)) - if err != nil { - if logger.V(logger.ErrorLevel, log) { - log.Errorf("unable to create cipher: %v", err) - } - return err - } - - // decrypt the received payload using the token - // we have to used msg.session because multicast has a shared - // session id of "multicast" in this session struct on - // the listener side - msg.data.Body, err = Decrypt(gcm, msg.data.Body) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("failed to decrypt message body: %v", err) - } - return err - } - - // dencrypt all the headers - for k, v := range msg.data.Header { - // decode the header values - h, err := base32.StdEncoding.DecodeString(v) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("failed to decode message header %s: %v", k, err) - } - return err - } - - // dencrypt the transport message payload - val, err := Decrypt(gcm, h) - if err != nil { - if logger.V(logger.DebugLevel, log) { - log.Debugf("failed to decrypt message header %s: %v", k, err) - } - return err - } - // add decrypted header value - msg.data.Header[k] = string(val) - } - - // set the link - // TODO: decruft, this is only for multicast - // since the session is now a single session - // likely provide as part of message.Link() - msg.data.Header["Micro-Link"] = msg.link - - // set message - *m = *msg.data - // return nil - return nil -} - -// Close closes the session by sending a close message -func (s *session) Close() error { - select { - case <-s.closed: - // no op - default: - close(s.closed) - - // don't send close on multicast or broadcast - if s.mode != tunnel.Unicast { - return nil - } - - // append to backlog - msg := s.newMessage("close") - // no error response on close - msg.errChan = nil - - // send the close message - select { - case s.send <- msg: - case <-time.After(time.Millisecond * 10): - } - } - - return nil -} diff --git a/tunnel/options.go b/tunnel/options.go index fb6b1f52..5d1c1df7 100644 --- a/tunnel/options.go +++ b/tunnel/options.go @@ -5,7 +5,6 @@ import ( "github.com/google/uuid" "github.com/unistack-org/micro/v3/transport" - "github.com/unistack-org/micro/v3/transport/quic" ) var ( @@ -137,9 +136,8 @@ func DialWait(b bool) DialOption { // DefaultOptions returns router default options func DefaultOptions() Options { return Options{ - Id: uuid.New().String(), - Address: DefaultAddress, - Token: DefaultToken, - Transport: quic.NewTransport(), + Id: uuid.New().String(), + Address: DefaultAddress, + Token: DefaultToken, } } diff --git a/tunnel/transport/transport.go b/tunnel/transport/transport.go index 095b96a7..ae54be7d 100644 --- a/tunnel/transport/transport.go +++ b/tunnel/transport/transport.go @@ -3,10 +3,10 @@ package transport import ( "context" + "fmt" "github.com/unistack-org/micro/v3/transport" "github.com/unistack-org/micro/v3/tunnel" - "github.com/unistack-org/micro/v3/tunnel/mucp" ) type tunTransport struct { @@ -32,7 +32,7 @@ func (t *tunTransport) Init(opts ...transport.Option) error { // get the tunnel tun, ok := t.options.Context.Value(tunnelKey{}).(tunnel.Tunnel) if !ok { - tun = mucp.NewTunnel() + return fmt.Errorf("tunnel not set") } // get the transport diff --git a/util/client/client_test.go b/util/client/client_test.go index e9317114..b685f8be 100644 --- a/util/client/client_test.go +++ b/util/client/client_test.go @@ -1,3 +1,5 @@ +// +build ignore + package client_test import ( diff --git a/util/file/client.go b/util/file/client.go deleted file mode 100644 index c65db16d..00000000 --- a/util/file/client.go +++ /dev/null @@ -1,201 +0,0 @@ -package file - -import ( - "bufio" - "context" - "fmt" - "io" - "os" - - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/logger" - proto "github.com/unistack-org/micro/v3/util/file/proto" -) - -// Client is the client interface to access files -type File interface { - Open(filename string, truncate bool) (int64, error) - Stat(filename string) (*proto.StatResponse, error) - GetBlock(sessionId, blockId int64) ([]byte, error) - ReadAt(sessionId, offset, size int64) ([]byte, error) - Read(sessionId int64, buf []byte) (int, error) - Write(sessionId, offset int64, data []byte) error - Close(sessionId int64) error - Download(filename, saveFile string) error - Upload(filename, localFile string) error - DownloadAt(filename, saveFile string, blockId int) error -} - -// NewClient returns a new Client which uses a micro Client -func New(service string, c client.Client, opts ...Option) File { - options := Options{ - Context: context.TODO(), - } - for _, o := range opts { - o(&options) - } - - return &fc{ - c: proto.NewFileService(service, c), - opts: options, - } -} - -const ( - blockSize = 512 * 1024 -) - -type fc struct { - c proto.FileService - opts Options -} - -func (c *fc) Open(filename string, truncate bool) (int64, error) { - rsp, err := c.c.Open(c.opts.Context, &proto.OpenRequest{ - Filename: filename, - Truncate: truncate, - }) - if err != nil { - return 0, err - } - return rsp.Id, nil -} - -func (c *fc) Stat(filename string) (*proto.StatResponse, error) { - return c.c.Stat(c.opts.Context, &proto.StatRequest{Filename: filename}) -} - -func (c *fc) GetBlock(sessionId, blockId int64) ([]byte, error) { - return c.ReadAt(sessionId, blockId*blockSize, blockSize) -} - -func (c *fc) ReadAt(sessionId, offset, size int64) ([]byte, error) { - rsp, err := c.c.Read(c.opts.Context, &proto.ReadRequest{Id: sessionId, Size: size, Offset: offset}) - if err != nil { - return nil, err - } - - if rsp.Eof { - err = io.EOF - } - - if rsp.Data == nil { - rsp.Data = make([]byte, size) - } - - if size != rsp.Size { - return rsp.Data[:rsp.Size], err - } - - return rsp.Data, nil -} - -func (c *fc) Read(sessionId int64, buf []byte) (int, error) { - b, err := c.ReadAt(sessionId, 0, int64(cap(buf))) - if err != nil { - return 0, err - } - copy(buf, b) - return len(b), nil -} - -func (c *fc) Write(sessionId, offset int64, data []byte) error { - _, err := c.c.Write(c.opts.Context, &proto.WriteRequest{ - Id: sessionId, - Offset: offset, - Data: data}) - return err -} - -func (c *fc) Close(sessionId int64) error { - _, err := c.c.Close(c.opts.Context, &proto.CloseRequest{Id: sessionId}) - return err -} - -func (c *fc) Download(filename, saveFile string) error { - return c.DownloadAt(filename, saveFile, 0) -} - -func (c *fc) Upload(filename, localFile string) error { - file, err := os.Open(localFile) - if err != nil { - return err - } - defer file.Close() - - offset := 0 - sessionId, err := c.Open(filename, true) - defer c.Close(sessionId) - if err != nil { - return err - } - reader := bufio.NewReader(file) - part := make([]byte, blockSize) - - for { - count, err := reader.Read(part) - if err != nil { - break - } - err = c.Write(sessionId, int64(offset), part) - if err != nil { - return err - } - offset += count - } - if err != nil && err != io.EOF { - return fmt.Errorf("Error reading %v: %v", localFile, err) - } - return nil -} - -func (c *fc) DownloadAt(filename, saveFile string, blockId int) error { - stat, err := c.Stat(filename) - if err != nil { - return err - } - if stat.Type == "Directory" { - return fmt.Errorf("%s is directory.", filename) - } - - blocks := int(stat.Size / blockSize) - if stat.Size%blockSize != 0 { - blocks += 1 - } - - logger.Infof("Download %s in %d blocks", filename, blocks-blockId) - - file, err := os.OpenFile(saveFile, os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - return err - } - defer file.Close() - - sessionId, err := c.Open(filename, false) - if err != nil { - return err - } - - for i := blockId; i < blocks; i++ { - buf, rerr := c.GetBlock(sessionId, int64(i)) - if rerr != nil && rerr != io.EOF { - return rerr - } - if _, werr := file.WriteAt(buf, int64(i)*blockSize); werr != nil { - return werr - } - - if i%((blocks-blockId)/100+1) == 0 { - logger.Infof("Downloading %s [%d/%d] blocks", filename, i-blockId+1, blocks-blockId) - } - - if rerr == io.EOF { - break - } - } - logger.Infof("Download %s completed", filename) - - c.Close(sessionId) - - return nil -} diff --git a/util/file/file.go b/util/file/file.go deleted file mode 100644 index 3e61d3f3..00000000 --- a/util/file/file.go +++ /dev/null @@ -1,15 +0,0 @@ -package file - -import "os" - -// Exists returns true if the path is existing -func Exists(path string) (bool, error) { - _, err := os.Stat(path) - if err == nil { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return true, err -} diff --git a/util/file/file_test.go b/util/file/file_test.go deleted file mode 100644 index 4ee3fabc..00000000 --- a/util/file/file_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package file - -import ( - "testing" -) - -func TestExists(t *testing.T) { - ok, err := Exists("/") - - if ok { - return - } - - if !ok || err != nil { - t.Fatalf("Test Exists fail, %s", err) - } -} diff --git a/util/file/handler.go b/util/file/handler.go deleted file mode 100644 index 8b9ae691..00000000 --- a/util/file/handler.go +++ /dev/null @@ -1,154 +0,0 @@ -package file - -import ( - "io" - "os" - "path/filepath" - "sync" - - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/logger" - "github.com/unistack-org/micro/v3/server" - proto "github.com/unistack-org/micro/v3/util/file/proto" - "golang.org/x/net/context" -) - -// NewHandler is a handler that can be registered with a micro Server -func NewHandler(readDir string) proto.FileHandler { - return &handler{ - readDir: readDir, - session: &session{ - files: make(map[int64]*os.File), - }, - } -} - -// RegisterHandler is a convenience method for registering a handler -func RegisterHandler(s server.Server, readDir string) { - proto.RegisterFileHandler(s, NewHandler(readDir)) -} - -type handler struct { - readDir string - session *session -} - -func (h *handler) Open(ctx context.Context, req *proto.OpenRequest, rsp *proto.OpenResponse) error { - path := filepath.Join(h.readDir, req.Filename) - flags := os.O_CREATE | os.O_RDWR - if req.GetTruncate() { - flags |= os.O_TRUNC - } - file, err := os.OpenFile(path, flags, 0666) - if err != nil { - return errors.InternalServerError("go.micro.server", err.Error()) - } - - rsp.Id = h.session.Add(file) - rsp.Result = true - - logger.Debugf("Open %s, sessionId=%d", req.Filename, rsp.Id) - - return nil -} - -func (h *handler) Close(ctx context.Context, req *proto.CloseRequest, rsp *proto.CloseResponse) error { - h.session.Delete(req.Id) - logger.Debugf("Close sessionId=%d", req.Id) - return nil -} - -func (h *handler) Stat(ctx context.Context, req *proto.StatRequest, rsp *proto.StatResponse) error { - path := filepath.Join(h.readDir, req.Filename) - fi, err := os.Stat(path) - if os.IsNotExist(err) { - return errors.InternalServerError("go.micro.srv.file", err.Error()) - } - - if fi.IsDir() { - rsp.Type = "Directory" - } else { - rsp.Type = "File" - rsp.Size = fi.Size() - } - - rsp.LastModified = fi.ModTime().Unix() - logger.Debugf("Stat %s, %#v", req.Filename, rsp) - - return nil -} - -func (h *handler) Read(ctx context.Context, req *proto.ReadRequest, rsp *proto.ReadResponse) error { - file := h.session.Get(req.Id) - if file == nil { - return errors.InternalServerError("go.micro.srv.file", "You must call open first.") - } - - rsp.Data = make([]byte, req.Size) - n, err := file.ReadAt(rsp.Data, req.Offset) - if err != nil && err != io.EOF { - return errors.InternalServerError("go.micro.srv.file", err.Error()) - } - - if err == io.EOF { - rsp.Eof = true - } - - rsp.Size = int64(n) - rsp.Data = rsp.Data[:n] - - logger.Debugf("Read sessionId=%d, Offset=%d, n=%d", req.Id, req.Offset, rsp.Size) - - return nil -} - -func (h *handler) Write(ctx context.Context, req *proto.WriteRequest, rsp *proto.WriteResponse) error { - file := h.session.Get(req.Id) - if file == nil { - return errors.InternalServerError("go.micro.srv.file", "You must call open first.") - } - - if _, err := file.WriteAt(req.GetData(), req.GetOffset()); err != nil { - return err - } - - logger.Debugf("Write session id=%d, Offset=%d,", req.Id, req.Offset) - - return nil -} - -type session struct { - sync.Mutex - files map[int64]*os.File - counter int64 -} - -func (s *session) Add(file *os.File) int64 { - s.Lock() - defer s.Unlock() - - s.counter += 1 - s.files[s.counter] = file - - return s.counter -} - -func (s *session) Get(id int64) *os.File { - s.Lock() - defer s.Unlock() - return s.files[id] -} - -func (s *session) Delete(id int64) { - s.Lock() - defer s.Unlock() - - if file, exist := s.files[id]; exist { - file.Close() - delete(s.files, id) - } -} - -func (s *session) Len() int { - return len(s.files) -} diff --git a/util/file/options.go b/util/file/options.go deleted file mode 100644 index 8c2a6f3b..00000000 --- a/util/file/options.go +++ /dev/null @@ -1,15 +0,0 @@ -package file - -import "context" - -type Options struct { - Context context.Context -} - -type Option func(o *Options) - -func WithContext(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} diff --git a/util/file/proto/file.pb.go b/util/file/proto/file.pb.go deleted file mode 100644 index 14dd9504..00000000 --- a/util/file/proto/file.pb.go +++ /dev/null @@ -1,967 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.6.1 -// source: util/file/proto/file.proto - -package go_micro_server - -import ( - proto "github.com/golang/protobuf/proto" - 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) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type OpenRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"` - Truncate bool `protobuf:"varint,2,opt,name=truncate,proto3" json:"truncate,omitempty"` -} - -func (x *OpenRequest) Reset() { - *x = OpenRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OpenRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OpenRequest) ProtoMessage() {} - -func (x *OpenRequest) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_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 OpenRequest.ProtoReflect.Descriptor instead. -func (*OpenRequest) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{0} -} - -func (x *OpenRequest) GetFilename() string { - if x != nil { - return x.Filename - } - return "" -} - -func (x *OpenRequest) GetTruncate() bool { - if x != nil { - return x.Truncate - } - return false -} - -type OpenResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Result bool `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"` -} - -func (x *OpenResponse) Reset() { - *x = OpenResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OpenResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OpenResponse) ProtoMessage() {} - -func (x *OpenResponse) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_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 OpenResponse.ProtoReflect.Descriptor instead. -func (*OpenResponse) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{1} -} - -func (x *OpenResponse) GetId() int64 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *OpenResponse) GetResult() bool { - if x != nil { - return x.Result - } - return false -} - -type CloseRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *CloseRequest) Reset() { - *x = CloseRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseRequest) ProtoMessage() {} - -func (x *CloseRequest) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[2] - 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 CloseRequest.ProtoReflect.Descriptor instead. -func (*CloseRequest) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{2} -} - -func (x *CloseRequest) GetId() int64 { - if x != nil { - return x.Id - } - return 0 -} - -type CloseResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CloseResponse) Reset() { - *x = CloseResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseResponse) ProtoMessage() {} - -func (x *CloseResponse) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CloseResponse.ProtoReflect.Descriptor instead. -func (*CloseResponse) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{3} -} - -type StatRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"` -} - -func (x *StatRequest) Reset() { - *x = StatRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StatRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StatRequest) ProtoMessage() {} - -func (x *StatRequest) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[4] - 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 StatRequest.ProtoReflect.Descriptor instead. -func (*StatRequest) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{4} -} - -func (x *StatRequest) GetFilename() string { - if x != nil { - return x.Filename - } - return "" -} - -type StatResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` - LastModified int64 `protobuf:"varint,3,opt,name=last_modified,json=lastModified,proto3" json:"last_modified,omitempty"` -} - -func (x *StatResponse) Reset() { - *x = StatResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StatResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StatResponse) ProtoMessage() {} - -func (x *StatResponse) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StatResponse.ProtoReflect.Descriptor instead. -func (*StatResponse) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{5} -} - -func (x *StatResponse) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *StatResponse) GetSize() int64 { - if x != nil { - return x.Size - } - return 0 -} - -func (x *StatResponse) GetLastModified() int64 { - if x != nil { - return x.LastModified - } - return 0 -} - -type ReadRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` -} - -func (x *ReadRequest) Reset() { - *x = ReadRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadRequest) ProtoMessage() {} - -func (x *ReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[6] - 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 ReadRequest.ProtoReflect.Descriptor instead. -func (*ReadRequest) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{6} -} - -func (x *ReadRequest) GetId() int64 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *ReadRequest) GetOffset() int64 { - if x != nil { - return x.Offset - } - return 0 -} - -func (x *ReadRequest) GetSize() int64 { - if x != nil { - return x.Size - } - return 0 -} - -type ReadResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Size int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - Eof bool `protobuf:"varint,3,opt,name=eof,proto3" json:"eof,omitempty"` -} - -func (x *ReadResponse) Reset() { - *x = ReadResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadResponse) ProtoMessage() {} - -func (x *ReadResponse) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[7] - 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 ReadResponse.ProtoReflect.Descriptor instead. -func (*ReadResponse) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{7} -} - -func (x *ReadResponse) GetSize() int64 { - if x != nil { - return x.Size - } - return 0 -} - -func (x *ReadResponse) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -func (x *ReadResponse) GetEof() bool { - if x != nil { - return x.Eof - } - return false -} - -type GetRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - BlockId int64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` -} - -func (x *GetRequest) Reset() { - *x = GetRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetRequest) ProtoMessage() {} - -func (x *GetRequest) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[8] - 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 GetRequest.ProtoReflect.Descriptor instead. -func (*GetRequest) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{8} -} - -func (x *GetRequest) GetId() int64 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *GetRequest) GetBlockId() int64 { - if x != nil { - return x.BlockId - } - return 0 -} - -type GetResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BlockId int64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` - Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` -} - -func (x *GetResponse) Reset() { - *x = GetResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetResponse) ProtoMessage() {} - -func (x *GetResponse) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[9] - 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 GetResponse.ProtoReflect.Descriptor instead. -func (*GetResponse) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{9} -} - -func (x *GetResponse) GetBlockId() int64 { - if x != nil { - return x.BlockId - } - return 0 -} - -func (x *GetResponse) GetSize() int64 { - if x != nil { - return x.Size - } - return 0 -} - -func (x *GetResponse) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -type WriteRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` -} - -func (x *WriteRequest) Reset() { - *x = WriteRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WriteRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WriteRequest) ProtoMessage() {} - -func (x *WriteRequest) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[10] - 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 WriteRequest.ProtoReflect.Descriptor instead. -func (*WriteRequest) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{10} -} - -func (x *WriteRequest) GetId() int64 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *WriteRequest) GetOffset() int64 { - if x != nil { - return x.Offset - } - return 0 -} - -func (x *WriteRequest) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -type WriteResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *WriteResponse) Reset() { - *x = WriteResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_util_file_proto_file_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WriteResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WriteResponse) ProtoMessage() {} - -func (x *WriteResponse) ProtoReflect() protoreflect.Message { - mi := &file_util_file_proto_file_proto_msgTypes[11] - 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 WriteResponse.ProtoReflect.Descriptor instead. -func (*WriteResponse) Descriptor() ([]byte, []int) { - return file_util_file_proto_file_proto_rawDescGZIP(), []int{11} -} - -var File_util_file_proto_file_proto protoreflect.FileDescriptor - -var file_util_file_proto_file_proto_rawDesc = []byte{ - 0x0a, 0x1a, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, - 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x45, 0x0a, - 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x72, 0x75, 0x6e, - 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x72, 0x75, 0x6e, - 0x63, 0x61, 0x74, 0x65, 0x22, 0x36, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x1e, 0x0a, 0x0c, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0f, 0x0a, 0x0d, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x0a, - 0x0b, 0x53, 0x74, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5b, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x49, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x22, 0x48, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6f, 0x66, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6f, 0x66, 0x22, 0x37, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x64, 0x22, 0x50, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4a, 0x0a, 0x0c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x22, 0x0f, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0xef, 0x02, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x4f, - 0x70, 0x65, 0x6e, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x45, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, - 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x04, 0x52, 0x65, 0x61, - 0x64, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x48, 0x0a, 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, - 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, - 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x05, 0x43, 0x6c, - 0x6f, 0x73, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_util_file_proto_file_proto_rawDescOnce sync.Once - file_util_file_proto_file_proto_rawDescData = file_util_file_proto_file_proto_rawDesc -) - -func file_util_file_proto_file_proto_rawDescGZIP() []byte { - file_util_file_proto_file_proto_rawDescOnce.Do(func() { - file_util_file_proto_file_proto_rawDescData = protoimpl.X.CompressGZIP(file_util_file_proto_file_proto_rawDescData) - }) - return file_util_file_proto_file_proto_rawDescData -} - -var file_util_file_proto_file_proto_msgTypes = make([]protoimpl.MessageInfo, 12) -var file_util_file_proto_file_proto_goTypes = []interface{}{ - (*OpenRequest)(nil), // 0: go.micro.server.OpenRequest - (*OpenResponse)(nil), // 1: go.micro.server.OpenResponse - (*CloseRequest)(nil), // 2: go.micro.server.CloseRequest - (*CloseResponse)(nil), // 3: go.micro.server.CloseResponse - (*StatRequest)(nil), // 4: go.micro.server.StatRequest - (*StatResponse)(nil), // 5: go.micro.server.StatResponse - (*ReadRequest)(nil), // 6: go.micro.server.ReadRequest - (*ReadResponse)(nil), // 7: go.micro.server.ReadResponse - (*GetRequest)(nil), // 8: go.micro.server.GetRequest - (*GetResponse)(nil), // 9: go.micro.server.GetResponse - (*WriteRequest)(nil), // 10: go.micro.server.WriteRequest - (*WriteResponse)(nil), // 11: go.micro.server.WriteResponse -} -var file_util_file_proto_file_proto_depIdxs = []int32{ - 0, // 0: go.micro.server.File.Open:input_type -> go.micro.server.OpenRequest - 4, // 1: go.micro.server.File.Stat:input_type -> go.micro.server.StatRequest - 6, // 2: go.micro.server.File.Read:input_type -> go.micro.server.ReadRequest - 10, // 3: go.micro.server.File.Write:input_type -> go.micro.server.WriteRequest - 2, // 4: go.micro.server.File.Close:input_type -> go.micro.server.CloseRequest - 1, // 5: go.micro.server.File.Open:output_type -> go.micro.server.OpenResponse - 5, // 6: go.micro.server.File.Stat:output_type -> go.micro.server.StatResponse - 7, // 7: go.micro.server.File.Read:output_type -> go.micro.server.ReadResponse - 11, // 8: go.micro.server.File.Write:output_type -> go.micro.server.WriteResponse - 3, // 9: go.micro.server.File.Close:output_type -> go.micro.server.CloseResponse - 5, // [5:10] is the sub-list for method output_type - 0, // [0:5] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_util_file_proto_file_proto_init() } -func file_util_file_proto_file_proto_init() { - if File_util_file_proto_file_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_util_file_proto_file_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OpenRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OpenResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WriteRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_util_file_proto_file_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WriteResponse); 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_util_file_proto_file_proto_rawDesc, - NumEnums: 0, - NumMessages: 12, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_util_file_proto_file_proto_goTypes, - DependencyIndexes: file_util_file_proto_file_proto_depIdxs, - MessageInfos: file_util_file_proto_file_proto_msgTypes, - }.Build() - File_util_file_proto_file_proto = out.File - file_util_file_proto_file_proto_rawDesc = nil - file_util_file_proto_file_proto_goTypes = nil - file_util_file_proto_file_proto_depIdxs = nil -} diff --git a/util/file/proto/file.pb.micro.go b/util/file/proto/file.pb.micro.go deleted file mode 100644 index 57af68c2..00000000 --- a/util/file/proto/file.pb.micro.go +++ /dev/null @@ -1,161 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: util/file/proto/file.proto - -package go_micro_server - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -import ( - context "context" - api "github.com/unistack-org/micro/v3/api" - client "github.com/unistack-org/micro/v3/client" - server "github.com/unistack-org/micro/v3/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for File service - -func NewFileEndpoints() []*api.Endpoint { - return []*api.Endpoint{} -} - -// Client API for File service - -type FileService interface { - Open(ctx context.Context, in *OpenRequest, opts ...client.CallOption) (*OpenResponse, error) - Stat(ctx context.Context, in *StatRequest, opts ...client.CallOption) (*StatResponse, error) - Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) - Write(ctx context.Context, in *WriteRequest, opts ...client.CallOption) (*WriteResponse, error) - Close(ctx context.Context, in *CloseRequest, opts ...client.CallOption) (*CloseResponse, error) -} - -type fileService struct { - c client.Client - name string -} - -func NewFileService(name string, c client.Client) FileService { - return &fileService{ - c: c, - name: name, - } -} - -func (c *fileService) Open(ctx context.Context, in *OpenRequest, opts ...client.CallOption) (*OpenResponse, error) { - req := c.c.NewRequest(c.name, "File.Open", in) - out := new(OpenResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileService) Stat(ctx context.Context, in *StatRequest, opts ...client.CallOption) (*StatResponse, error) { - req := c.c.NewRequest(c.name, "File.Stat", in) - out := new(StatResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) { - req := c.c.NewRequest(c.name, "File.Read", in) - out := new(ReadResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileService) Write(ctx context.Context, in *WriteRequest, opts ...client.CallOption) (*WriteResponse, error) { - req := c.c.NewRequest(c.name, "File.Write", in) - out := new(WriteResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileService) Close(ctx context.Context, in *CloseRequest, opts ...client.CallOption) (*CloseResponse, error) { - req := c.c.NewRequest(c.name, "File.Close", in) - out := new(CloseResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for File service - -type FileHandler interface { - Open(context.Context, *OpenRequest, *OpenResponse) error - Stat(context.Context, *StatRequest, *StatResponse) error - Read(context.Context, *ReadRequest, *ReadResponse) error - Write(context.Context, *WriteRequest, *WriteResponse) error - Close(context.Context, *CloseRequest, *CloseResponse) error -} - -func RegisterFileHandler(s server.Server, hdlr FileHandler, opts ...server.HandlerOption) error { - type file interface { - Open(ctx context.Context, in *OpenRequest, out *OpenResponse) error - Stat(ctx context.Context, in *StatRequest, out *StatResponse) error - Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error - Write(ctx context.Context, in *WriteRequest, out *WriteResponse) error - Close(ctx context.Context, in *CloseRequest, out *CloseResponse) error - } - type File struct { - file - } - h := &fileHandler{hdlr} - return s.Handle(s.NewHandler(&File{h}, opts...)) -} - -type fileHandler struct { - FileHandler -} - -func (h *fileHandler) Open(ctx context.Context, in *OpenRequest, out *OpenResponse) error { - return h.FileHandler.Open(ctx, in, out) -} - -func (h *fileHandler) Stat(ctx context.Context, in *StatRequest, out *StatResponse) error { - return h.FileHandler.Stat(ctx, in, out) -} - -func (h *fileHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error { - return h.FileHandler.Read(ctx, in, out) -} - -func (h *fileHandler) Write(ctx context.Context, in *WriteRequest, out *WriteResponse) error { - return h.FileHandler.Write(ctx, in, out) -} - -func (h *fileHandler) Close(ctx context.Context, in *CloseRequest, out *CloseResponse) error { - return h.FileHandler.Close(ctx, in, out) -} diff --git a/util/file/proto/file.proto b/util/file/proto/file.proto deleted file mode 100644 index 01395f2c..00000000 --- a/util/file/proto/file.proto +++ /dev/null @@ -1,69 +0,0 @@ -syntax = "proto3"; - -package go.micro.server; - -service File { - rpc Open(OpenRequest) returns(OpenResponse) {}; - rpc Stat(StatRequest) returns(StatResponse) {}; - rpc Read(ReadRequest) returns(ReadResponse) {}; - rpc Write(WriteRequest) returns(WriteResponse) {}; - rpc Close(CloseRequest) returns(CloseResponse) {}; -} - -message OpenRequest { - string filename = 1; - bool truncate = 2; -} - -message OpenResponse { - int64 id = 1; - bool result = 2; -} - -message CloseRequest { - int64 id = 1; -} - -message CloseResponse { -} - -message StatRequest { - string filename = 1; -} - -message StatResponse { - string type = 1; - int64 size = 2; - int64 last_modified = 3; -} - -message ReadRequest { - int64 id = 1; - int64 offset = 2; - int64 size = 3; -} - -message ReadResponse { - int64 size = 1; - bytes data = 2; - bool eof = 3; -} - -message GetRequest { - int64 id = 1; - int64 block_id = 2; -} - -message GetResponse { - int64 block_id = 1; - int64 size = 2; - bytes data = 3; -} - -message WriteRequest { - int64 id = 1; - int64 offset = 2; - bytes data = 3; -} - -message WriteResponse {} \ No newline at end of file diff --git a/util/file/proto/file_grpc.pb.go b/util/file/proto/file_grpc.pb.go deleted file mode 100644 index 2d6a1f4f..00000000 --- a/util/file/proto/file_grpc.pb.go +++ /dev/null @@ -1,234 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package go_micro_server - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// FileClient is the client API for File service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type FileClient interface { - Open(ctx context.Context, in *OpenRequest, opts ...grpc.CallOption) (*OpenResponse, error) - Stat(ctx context.Context, in *StatRequest, opts ...grpc.CallOption) (*StatResponse, error) - Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) - Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) - Close(ctx context.Context, in *CloseRequest, opts ...grpc.CallOption) (*CloseResponse, error) -} - -type fileClient struct { - cc grpc.ClientConnInterface -} - -func NewFileClient(cc grpc.ClientConnInterface) FileClient { - return &fileClient{cc} -} - -func (c *fileClient) Open(ctx context.Context, in *OpenRequest, opts ...grpc.CallOption) (*OpenResponse, error) { - out := new(OpenResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.File/Open", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileClient) Stat(ctx context.Context, in *StatRequest, opts ...grpc.CallOption) (*StatResponse, error) { - out := new(StatResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.File/Stat", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) { - out := new(ReadResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.File/Read", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) { - out := new(WriteResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.File/Write", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fileClient) Close(ctx context.Context, in *CloseRequest, opts ...grpc.CallOption) (*CloseResponse, error) { - out := new(CloseResponse) - err := c.cc.Invoke(ctx, "/go.micro.server.File/Close", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// FileServer is the server API for File service. -// All implementations must embed UnimplementedFileServer -// for forward compatibility -type FileServer interface { - Open(context.Context, *OpenRequest) (*OpenResponse, error) - Stat(context.Context, *StatRequest) (*StatResponse, error) - Read(context.Context, *ReadRequest) (*ReadResponse, error) - Write(context.Context, *WriteRequest) (*WriteResponse, error) - Close(context.Context, *CloseRequest) (*CloseResponse, error) - mustEmbedUnimplementedFileServer() -} - -// UnimplementedFileServer must be embedded to have forward compatible implementations. -type UnimplementedFileServer struct { -} - -func (*UnimplementedFileServer) Open(context.Context, *OpenRequest) (*OpenResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Open not implemented") -} -func (*UnimplementedFileServer) Stat(context.Context, *StatRequest) (*StatResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Stat not implemented") -} -func (*UnimplementedFileServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Read not implemented") -} -func (*UnimplementedFileServer) Write(context.Context, *WriteRequest) (*WriteResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Write not implemented") -} -func (*UnimplementedFileServer) Close(context.Context, *CloseRequest) (*CloseResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") -} -func (*UnimplementedFileServer) mustEmbedUnimplementedFileServer() {} - -func RegisterFileServer(s *grpc.Server, srv FileServer) { - s.RegisterService(&_File_serviceDesc, srv) -} - -func _File_Open_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(OpenRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FileServer).Open(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.File/Open", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FileServer).Open(ctx, req.(*OpenRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _File_Stat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StatRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FileServer).Stat(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.File/Stat", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FileServer).Stat(ctx, req.(*StatRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _File_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReadRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FileServer).Read(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.File/Read", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FileServer).Read(ctx, req.(*ReadRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _File_Write_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(WriteRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FileServer).Write(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.File/Write", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FileServer).Write(ctx, req.(*WriteRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _File_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CloseRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FileServer).Close(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/go.micro.server.File/Close", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FileServer).Close(ctx, req.(*CloseRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _File_serviceDesc = grpc.ServiceDesc{ - ServiceName: "go.micro.server.File", - HandlerType: (*FileServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Open", - Handler: _File_Open_Handler, - }, - { - MethodName: "Stat", - Handler: _File_Stat_Handler, - }, - { - MethodName: "Read", - Handler: _File_Read_Handler, - }, - { - MethodName: "Write", - Handler: _File_Write_Handler, - }, - { - MethodName: "Close", - Handler: _File_Close_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "util/file/proto/file.proto", -} diff --git a/util/http/http.go b/util/http/http.go index 09f4134d..918eea51 100644 --- a/util/http/http.go +++ b/util/http/http.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/unistack-org/micro/v3/metadata" - "github.com/unistack-org/micro/v3/router/registry" "github.com/unistack-org/micro/v3/selector/random" ) @@ -46,9 +45,7 @@ func WriteInternalServerError(w http.ResponseWriter, err error) { } func NewRoundTripper(opts ...Option) http.RoundTripper { - options := Options{ - Router: registry.NewRouter(), - } + options := Options{} for _, o := range opts { o(&options) } diff --git a/util/http/http_test.go b/util/http/http_test.go index 119261fe..391e0797 100644 --- a/util/http/http_test.go +++ b/util/http/http_test.go @@ -1,3 +1,5 @@ +// +build ignore + package http import ( diff --git a/util/kubernetes/client/client.go b/util/kubernetes/client/client.go index 195c0468..fb812969 100644 --- a/util/kubernetes/client/client.go +++ b/util/kubernetes/client/client.go @@ -341,16 +341,20 @@ func NewDeployment(name, version, typ, namespace string) *Deployment { // NewLocalClient returns a client that can be used with `kubectl proxy` func NewLocalClient(hosts ...string) *client { - if len(hosts) == 0 { - hosts[0] = "http://localhost:8001" - } - return &client{ + c := &client{ opts: &api.Options{ Client: http.DefaultClient, - Host: hosts[0], Namespace: "default", }, } + + if len(hosts) == 0 { + c.opts.Host = "http://localhost:8001" + } else { + c.opts.Host = hosts[0] + } + + return c } // NewClusterClient creates a Kubernetes client for use from within a k8s pod. diff --git a/util/pki/pki.go b/util/pki/pki.go index c4ac6f96..fd5f5d4d 100644 --- a/util/pki/pki.go +++ b/util/pki/pki.go @@ -8,8 +8,9 @@ import ( "crypto/rand" "crypto/x509" "encoding/pem" + "fmt" - "github.com/pkg/errors" + "errors" ) // GenerateKey returns an ed25519 key @@ -91,36 +92,36 @@ func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) { } asn1CACrt, err := decodePEM(CACrt) if err != nil { - return nil, errors.Wrap(err, "failed to decode CA Crt PEM") + return nil, fmt.Errorf("failed to decode CA Crt PEM: %w", err) } if len(asn1CACrt) != 1 { - return nil, errors.Errorf("expected 1 CA Crt, got %d", len(asn1CACrt)) + return nil, fmt.Errorf("expected 1 CA Crt, got %d", len(asn1CACrt)) } caCrt, err := x509.ParseCertificate(asn1CACrt[0].Bytes) if err != nil { - return nil, errors.Wrap(err, "ca is not a valid certificate") + return nil, fmt.Errorf("ca is not a valid certificate: %w", err) } asn1CAKey, err := decodePEM(CAKey) if err != nil { - return nil, errors.Wrap(err, "failed to decode CA Key PEM") + return nil, fmt.Errorf("failed to decode CA Key PEM: %w", err) } if len(asn1CAKey) != 1 { - return nil, errors.Errorf("expected 1 CA Key, got %d", len(asn1CACrt)) + return nil, fmt.Errorf("expected 1 CA Key, got %d", len(asn1CACrt)) } caKey, err := x509.ParsePKCS8PrivateKey(asn1CAKey[0].Bytes) if err != nil { - return nil, errors.Wrap(err, "ca key is not a valid private key") + return nil, fmt.Errorf("ca key is not a valid private key: %w", err) } asn1CSR, err := decodePEM(CSR) if err != nil { - return nil, errors.Wrap(err, "failed to decode CSR PEM") + return nil, fmt.Errorf("failed to decode CSR PEM: %w", err) } if len(asn1CSR) != 1 { - return nil, errors.Errorf("expected 1 CSR, got %d", len(asn1CSR)) + return nil, fmt.Errorf("expected 1 CSR, got %d", len(asn1CSR)) } csr, err := x509.ParseCertificateRequest(asn1CSR[0].Bytes) if err != nil { - return nil, errors.Wrap(err, "csr is invalid") + return nil, fmt.Errorf("csr is invalid: %w", err) } template := &x509.Certificate{ SignatureAlgorithm: x509.PureEd25519, @@ -137,11 +138,11 @@ func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) { x509Cert, err := x509.CreateCertificate(rand.Reader, template, caCrt, caCrt.PublicKey, caKey) if err != nil { - return nil, errors.Wrap(err, "Couldn't sign certificate") + return nil, fmt.Errorf("Couldn't sign certificate: %w", err) } out := &bytes.Buffer{} if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil { - return nil, errors.Wrap(err, "couldn't encode cert") + return nil, fmt.Errorf("couldn't encode cert: %w", err) } return out.Bytes(), nil } diff --git a/util/pool/default_test.go b/util/pool/default_test.go index 19b9b5b0..7b9913b9 100644 --- a/util/pool/default_test.go +++ b/util/pool/default_test.go @@ -1,3 +1,5 @@ +// +build ignore + package pool import ( diff --git a/util/registry/util_test.go b/util/registry/util_test.go index 83c3a78d..dadd6232 100644 --- a/util/registry/util_test.go +++ b/util/registry/util_test.go @@ -35,7 +35,7 @@ func TestRemove(t *testing.T) { if i := len(servs); i > 0 { t.Errorf("Expected 0 nodes, got %d: %+v", i, servs) } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { + if len(os.Getenv("INTEGRATION_TESTS")) == 0 { t.Logf("Services %+v", servs) } } @@ -72,7 +72,7 @@ func TestRemoveNodes(t *testing.T) { if i := len(nodes); i != 1 { t.Errorf("Expected only 1 node, got %d: %+v", i, nodes) } - if len(os.Getenv("IN_TRAVIS_CI")) == 0 { + if len(os.Getenv("INTEGRATION_TESTS")) == 0 { t.Logf("Nodes %+v", nodes) } } diff --git a/util/stream/stream.go b/util/stream/stream.go index 4cf6f081..af5f8b8d 100644 --- a/util/stream/stream.go +++ b/util/stream/stream.go @@ -6,7 +6,6 @@ import ( "sync" "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/client/mucp" "github.com/unistack-org/micro/v3/codec" "github.com/unistack-org/micro/v3/metadata" "github.com/unistack-org/micro/v3/server" @@ -77,12 +76,12 @@ func (s *stream) Error() error { // New returns a new encapsulated stream // Proto stream within a server.Stream -func New(service, endpoint string, req interface{}, s Stream) server.Stream { +func New(service, endpoint string, req interface{}, c client.Client, s Stream) server.Stream { return &stream{ Stream: s, request: &request{ context: s.Context(), - Request: mucp.NewClient().NewRequest(service, endpoint, req), + Request: c.NewRequest(service, endpoint, req), }, } } diff --git a/util/sync/manager.go b/util/sync/manager.go index 069b8c13..b43b19b8 100644 --- a/util/sync/manager.go +++ b/util/sync/manager.go @@ -1,9 +1,9 @@ package sync import ( + "fmt" "time" - "github.com/pkg/errors" "github.com/unistack-org/micro/v3/store" ) @@ -32,11 +32,11 @@ func (c *syncStore) processQueue(index int) { for i := 0; i < q.Len(); i++ { r, ok := q.PopFront() if !ok { - panic(errors.Errorf("retrieved an invalid value from the L%d sync queue", index+1)) + panic(fmt.Errorf("retrieved an invalid value from the L%d sync queue", index+1)) } ir, ok := r.(*internalRecord) if !ok { - panic(errors.Errorf("retrieved a non-internal record from the L%d sync queue", index+1)) + panic(fmt.Errorf("retrieved a non-internal record from the L%d sync queue", index+1)) } if !ir.expiresAt.IsZero() && time.Now().After(ir.expiresAt) { continue diff --git a/util/sync/sync.go b/util/sync/sync.go index a8032a52..518fe454 100644 --- a/util/sync/sync.go +++ b/util/sync/sync.go @@ -2,12 +2,12 @@ package sync import ( + "errors" "fmt" "sync" "time" "github.com/ef-ds/deque" - "github.com/pkg/errors" "github.com/unistack-org/micro/v3/store" ) @@ -59,7 +59,7 @@ func (c *syncStore) Init(opts ...store.Option) error { } for _, s := range c.syncOpts.Stores { if err := s.Init(); err != nil { - return errors.Wrapf(err, "Store %s failed to Init()", s.String()) + return fmt.Errorf("Store %s failed to Init(): %w", s.String(), err) } } c.pendingWrites = make([]*deque.Deque, len(c.syncOpts.Stores)-1) diff --git a/util/token/basic/basic_test.go b/util/token/basic/basic_test.go index 17f7b978..13fe2308 100644 --- a/util/token/basic/basic_test.go +++ b/util/token/basic/basic_test.go @@ -1,3 +1,5 @@ +// +build ignore + package basic import (