Compare commits
	
		
			39 Commits
		
	
	
		
			b6d2d459c5
			...
			v3.0.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | f12473f4b1 | ||
|  | 724e2b5830 | ||
|  | 6bdf33c4ee | ||
|  | 84f52fd7ac | ||
|  | 6e30b53280 | ||
|  | a60426c884 | ||
|  | 2998735bf3 | ||
|  | 3a96135df8 | ||
|  | bf8b3aeac7 | ||
|  | 5a52b5929c | ||
|  | 0adb469a85 | ||
|  | 21004341bf | ||
|  | cc26f2b8b1 | ||
|  | 1a6652fe6b | ||
|  | d28f0670d6 | ||
|  | 7bdd619e1b | ||
|  | c62d1d5eb8 | ||
|  | d60d85de5c | ||
|  | 44f281f8d9 | ||
|  | f698feac9c | ||
|  | f55701b374 | ||
|  | 82e8298b73 | ||
|  | fc54503232 | ||
|  | 6f0594eebe | ||
|  | 6b52f859cf | ||
|  | a3d4b8f79b | ||
|  | 7c7df6b35d | ||
|  | e80eab397a | ||
|  | 6cda6ef92e | ||
|  | f9f61d29de | ||
|  | 1ae825032c | ||
|  | f146b52418 | ||
|  | 78a79ca9e1 | ||
|  | 329bc2f265 | ||
|  | 8738ed7757 | ||
|  | 29e8cdbfe9 | ||
|  | 47f356fc5f | ||
|  | 21ffc73c4f | ||
|  | 81a9342b83 | 
							
								
								
									
										5
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -25,4 +25,7 @@ jobs: | |||||||
|       id: tests |       id: tests | ||||||
|       env: |       env: | ||||||
|         IN_TRAVIS_CI: yes |         IN_TRAVIS_CI: yes | ||||||
|       run: go test -v ./... |       run: | | ||||||
|  |         wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar  xvz | ||||||
|  |         cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure & | ||||||
|  |         go test -v ./... | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -7,13 +7,17 @@ jobs: | |||||||
|     name: Test repo |     name: Test repo | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|  |  | ||||||
|     - name: Set up Go 1.13 |     - name: Set up Go 1.13 | ||||||
|       uses: actions/setup-go@v1 |       uses: actions/setup-go@v1 | ||||||
|       with: |       with: | ||||||
|         go-version: 1.13 |         go-version: 1.13 | ||||||
|       id: go |       id: go | ||||||
|  |  | ||||||
|  |     - name: Setup Kind | ||||||
|  |       uses: engineerd/setup-kind@v0.4.0 | ||||||
|  |       with: | ||||||
|  |         version: v0.8.1 | ||||||
|  |  | ||||||
|     - name: Check out code into the Go module directory |     - name: Check out code into the Go module directory | ||||||
|       uses: actions/checkout@v2 |       uses: actions/checkout@v2 | ||||||
|  |  | ||||||
| @@ -26,9 +30,16 @@ jobs: | |||||||
|       env: |       env: | ||||||
|         IN_TRAVIS_CI: yes |         IN_TRAVIS_CI: yes | ||||||
|       run: | |       run: | | ||||||
|         wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar  xvz |         kubectl apply -f runtime/kubernetes/test/test.yaml | ||||||
|  |         sudo mkdir -p /var/run/secrets/kubernetes.io/serviceaccount | ||||||
|  |         sudo chmod 777 /var/run/secrets/kubernetes.io/serviceaccount | ||||||
|  |         wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar -xvz | ||||||
|         cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure & |         cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure & | ||||||
|         go test -v ./... |         wget -q https://github.com/nats-io/nats-streaming-server/releases/download/v0.18.0/nats-streaming-server-v0.18.0-linux-amd64.zip | ||||||
|  |         unzip ./nats-streaming-server-v0.18.0-linux-amd64.zip | ||||||
|  |         export PATH=$PATH:./nats-streaming-server-v0.18.0-linux-amd64 | ||||||
|  |         nats-streaming-server & | ||||||
|  |         go test -tags kubernetes,nats -v ./... | ||||||
|    |    | ||||||
|     - name: Notify of test failure |     - name: Notify of test failure | ||||||
|       if: failure() |       if: failure() | ||||||
|   | |||||||
| @@ -249,7 +249,7 @@ func requestPayload(r *http.Request) ([]byte, error) { | |||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 		return raw.Marshal() | 		return raw.Marshal() | ||||||
| 	case strings.Contains(ct, "application/www-x-form-urlencoded"): | 	case strings.Contains(ct, "application/x-www-form-urlencoded"): | ||||||
| 		r.ParseForm() | 		r.ParseForm() | ||||||
|  |  | ||||||
| 		// generate a new set of values from the form | 		// generate a new set of values from the form | ||||||
|   | |||||||
| @@ -1,268 +0,0 @@ | |||||||
| // Code generated by protoc-gen-go. DO NOT EDIT. |  | ||||||
| // source: api/service/proto/api.proto |  | ||||||
|  |  | ||||||
| package go_micro_api |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	context "context" |  | ||||||
| 	fmt "fmt" |  | ||||||
| 	proto "github.com/golang/protobuf/proto" |  | ||||||
| 	grpc "google.golang.org/grpc" |  | ||||||
| 	codes "google.golang.org/grpc/codes" |  | ||||||
| 	status "google.golang.org/grpc/status" |  | ||||||
| 	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 |  | ||||||
|  |  | ||||||
| type Endpoint struct { |  | ||||||
| 	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"` |  | ||||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` |  | ||||||
| 	XXX_unrecognized     []byte   `json:"-"` |  | ||||||
| 	XXX_sizecache        int32    `json:"-"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Endpoint) Reset()         { *m = Endpoint{} } |  | ||||||
| func (m *Endpoint) String() string { return proto.CompactTextString(m) } |  | ||||||
| func (*Endpoint) ProtoMessage()    {} |  | ||||||
| func (*Endpoint) Descriptor() ([]byte, []int) { |  | ||||||
| 	return fileDescriptor_c4a48b6b680b5c31, []int{0} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Endpoint) XXX_Unmarshal(b []byte) error { |  | ||||||
| 	return xxx_messageInfo_Endpoint.Unmarshal(m, b) |  | ||||||
| } |  | ||||||
| func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { |  | ||||||
| 	return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic) |  | ||||||
| } |  | ||||||
| func (m *Endpoint) XXX_Merge(src proto.Message) { |  | ||||||
| 	xxx_messageInfo_Endpoint.Merge(m, src) |  | ||||||
| } |  | ||||||
| func (m *Endpoint) XXX_Size() int { |  | ||||||
| 	return xxx_messageInfo_Endpoint.Size(m) |  | ||||||
| } |  | ||||||
| func (m *Endpoint) XXX_DiscardUnknown() { |  | ||||||
| 	xxx_messageInfo_Endpoint.DiscardUnknown(m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var xxx_messageInfo_Endpoint proto.InternalMessageInfo |  | ||||||
|  |  | ||||||
| func (m *Endpoint) GetName() string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Name |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Endpoint) GetHost() []string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Host |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Endpoint) GetPath() []string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Path |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Endpoint) GetMethod() []string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Method |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Endpoint) GetStream() bool { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Stream |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type EmptyResponse struct { |  | ||||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` |  | ||||||
| 	XXX_unrecognized     []byte   `json:"-"` |  | ||||||
| 	XXX_sizecache        int32    `json:"-"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *EmptyResponse) Reset()         { *m = EmptyResponse{} } |  | ||||||
| func (m *EmptyResponse) String() string { return proto.CompactTextString(m) } |  | ||||||
| func (*EmptyResponse) ProtoMessage()    {} |  | ||||||
| func (*EmptyResponse) Descriptor() ([]byte, []int) { |  | ||||||
| 	return fileDescriptor_c4a48b6b680b5c31, []int{1} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *EmptyResponse) XXX_Unmarshal(b []byte) error { |  | ||||||
| 	return xxx_messageInfo_EmptyResponse.Unmarshal(m, b) |  | ||||||
| } |  | ||||||
| func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { |  | ||||||
| 	return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic) |  | ||||||
| } |  | ||||||
| func (m *EmptyResponse) XXX_Merge(src proto.Message) { |  | ||||||
| 	xxx_messageInfo_EmptyResponse.Merge(m, src) |  | ||||||
| } |  | ||||||
| func (m *EmptyResponse) XXX_Size() int { |  | ||||||
| 	return xxx_messageInfo_EmptyResponse.Size(m) |  | ||||||
| } |  | ||||||
| func (m *EmptyResponse) XXX_DiscardUnknown() { |  | ||||||
| 	xxx_messageInfo_EmptyResponse.DiscardUnknown(m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo |  | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	proto.RegisterType((*Endpoint)(nil), "go.micro.api.Endpoint") |  | ||||||
| 	proto.RegisterType((*EmptyResponse)(nil), "go.micro.api.EmptyResponse") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func init() { proto.RegisterFile("api/service/proto/api.proto", fileDescriptor_c4a48b6b680b5c31) } |  | ||||||
|  |  | ||||||
| var fileDescriptor_c4a48b6b680b5c31 = []byte{ |  | ||||||
| 	// 212 bytes of a gzipped FileDescriptorProto |  | ||||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0xd0, 0xc1, 0x4a, 0x03, 0x31, |  | ||||||
| 	0x10, 0x80, 0x61, 0xd7, 0xad, 0x65, 0x1d, 0x14, 0x21, 0x87, 0x12, 0xec, 0x65, 0xd9, 0x53, 0x4f, |  | ||||||
| 	0x59, 0xd0, 0x27, 0x28, 0xda, 0x17, 0xd8, 0x37, 0x88, 0xed, 0xd0, 0x9d, 0x43, 0x32, 0x43, 0x32, |  | ||||||
| 	0x14, 0x7c, 0x08, 0xdf, 0x59, 0x12, 0x2b, 0x2c, 0x5e, 0xbd, 0xfd, 0xf3, 0x1d, 0x86, 0x61, 0x60, |  | ||||||
| 	0xeb, 0x85, 0xc6, 0x8c, 0xe9, 0x42, 0x47, 0x1c, 0x25, 0xb1, 0xf2, 0xe8, 0x85, 0x5c, 0x2d, 0xf3, |  | ||||||
| 	0x70, 0x66, 0x17, 0xe8, 0x98, 0xd8, 0x79, 0xa1, 0xe1, 0x02, 0xdd, 0x21, 0x9e, 0x84, 0x29, 0xaa, |  | ||||||
| 	0x31, 0xb0, 0x8a, 0x3e, 0xa0, 0x6d, 0xfa, 0x66, 0x77, 0x3f, 0xd5, 0x2e, 0x36, 0x73, 0x56, 0x7b, |  | ||||||
| 	0xdb, 0xb7, 0xc5, 0x4a, 0x17, 0x13, 0xaf, 0xb3, 0x6d, 0x7f, 0xac, 0xb4, 0xd9, 0xc0, 0x3a, 0xa0, |  | ||||||
| 	0xce, 0x7c, 0xb2, 0xab, 0xaa, 0xd7, 0xa9, 0x78, 0xd6, 0x84, 0x3e, 0xd8, 0xbb, 0xbe, 0xd9, 0x75, |  | ||||||
| 	0xd3, 0x75, 0x1a, 0x9e, 0xe0, 0xf1, 0x10, 0x44, 0x3f, 0x27, 0xcc, 0xc2, 0x31, 0xe3, 0xcb, 0x57, |  | ||||||
| 	0x03, 0xed, 0x5e, 0xc8, 0xec, 0xa1, 0x9b, 0xf0, 0x4c, 0x59, 0x31, 0x99, 0x8d, 0x5b, 0xde, 0xea, |  | ||||||
| 	0x7e, 0x0f, 0x7d, 0xde, 0xfe, 0xf1, 0xe5, 0xa2, 0xe1, 0xc6, 0xbc, 0x01, 0xbc, 0x63, 0xfa, 0xdf, |  | ||||||
| 	0x92, 0x8f, 0x75, 0xfd, 0xd6, 0xeb, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x46, 0x62, 0x67, 0x30, |  | ||||||
| 	0x4c, 0x01, 0x00, 0x00, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. |  | ||||||
| var _ context.Context |  | ||||||
| var _ grpc.ClientConn |  | ||||||
|  |  | ||||||
| // 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.SupportPackageIsVersion4 |  | ||||||
|  |  | ||||||
| // ApiClient is the client API for Api service. |  | ||||||
| // |  | ||||||
| // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#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.ClientConn |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewApiClient(cc *grpc.ClientConn) 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. |  | ||||||
| type ApiServer interface { |  | ||||||
| 	Register(context.Context, *Endpoint) (*EmptyResponse, error) |  | ||||||
| 	Deregister(context.Context, *Endpoint) (*EmptyResponse, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UnimplementedApiServer can be embedded to have forward compatible implementations. |  | ||||||
| type UnimplementedApiServer struct { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (*UnimplementedApiServer) Register(ctx context.Context, req *Endpoint) (*EmptyResponse, error) { |  | ||||||
| 	return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") |  | ||||||
| } |  | ||||||
| func (*UnimplementedApiServer) Deregister(ctx context.Context, req *Endpoint) (*EmptyResponse, error) { |  | ||||||
| 	return nil, status.Errorf(codes.Unimplemented, "method Deregister not implemented") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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", |  | ||||||
| } |  | ||||||
| @@ -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/micro/go-micro/v3/api" |  | ||||||
| 	client "github.com/micro/go-micro/v3/client" |  | ||||||
| 	server "github.com/micro/go-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) |  | ||||||
| } |  | ||||||
| @@ -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 {} |  | ||||||
| @@ -4,6 +4,8 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v3/codec" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestBackoff(t *testing.T) { | func TestBackoff(t *testing.T) { | ||||||
| @@ -32,3 +34,63 @@ func TestBackoff(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type testRequest struct { | ||||||
|  | 	service     string | ||||||
|  | 	method      string | ||||||
|  | 	endpoint    string | ||||||
|  | 	contentType string | ||||||
|  | 	codec       codec.Codec | ||||||
|  | 	body        interface{} | ||||||
|  | 	opts        RequestOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request { | ||||||
|  | 	var opts RequestOptions | ||||||
|  |  | ||||||
|  | 	for _, o := range reqOpts { | ||||||
|  | 		o(&opts) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// set the content-type specified | ||||||
|  | 	if len(opts.ContentType) > 0 { | ||||||
|  | 		contentType = opts.ContentType | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &testRequest{ | ||||||
|  | 		service:     service, | ||||||
|  | 		method:      endpoint, | ||||||
|  | 		endpoint:    endpoint, | ||||||
|  | 		body:        request, | ||||||
|  | 		contentType: contentType, | ||||||
|  | 		opts:        opts, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) ContentType() string { | ||||||
|  | 	return r.contentType | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) Service() string { | ||||||
|  | 	return r.service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) Method() string { | ||||||
|  | 	return r.method | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) Endpoint() string { | ||||||
|  | 	return r.endpoint | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) Body() interface{} { | ||||||
|  | 	return r.body | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) Codec() codec.Writer { | ||||||
|  | 	return r.codec | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *testRequest) Stream() bool { | ||||||
|  | 	return r.opts.Stream | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,66 +0,0 @@ | |||||||
| package client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"hash/fnv" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/v3/metadata" |  | ||||||
| 	cache "github.com/patrickmn/go-cache" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // NewCache returns an initialised cache. |  | ||||||
| func NewCache() *Cache { |  | ||||||
| 	return &Cache{ |  | ||||||
| 		cache: cache.New(cache.NoExpiration, 30*time.Second), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Cache for responses |  | ||||||
| type Cache struct { |  | ||||||
| 	cache *cache.Cache |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Get a response from the cache |  | ||||||
| func (c *Cache) Get(ctx context.Context, req Request) (interface{}, bool) { |  | ||||||
| 	return c.cache.Get(key(ctx, req)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Set a response in the cache |  | ||||||
| func (c *Cache) Set(ctx context.Context, req Request, rsp interface{}, expiry time.Duration) { |  | ||||||
| 	c.cache.Set(key(ctx, req), rsp, expiry) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // List the key value pairs in the cache |  | ||||||
| func (c *Cache) List() map[string]string { |  | ||||||
| 	items := c.cache.Items() |  | ||||||
|  |  | ||||||
| 	rsp := make(map[string]string, len(items)) |  | ||||||
| 	for k, v := range items { |  | ||||||
| 		bytes, _ := json.Marshal(v.Object) |  | ||||||
| 		rsp[k] = string(bytes) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return rsp |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // key returns a hash for the context and request |  | ||||||
| func key(ctx context.Context, req Request) string { |  | ||||||
| 	ns, _ := metadata.Get(ctx, "Micro-Namespace") |  | ||||||
|  |  | ||||||
| 	bytes, _ := json.Marshal(map[string]interface{}{ |  | ||||||
| 		"namespace": ns, |  | ||||||
| 		"request": map[string]interface{}{ |  | ||||||
| 			"service":  req.Service(), |  | ||||||
| 			"endpoint": req.Endpoint(), |  | ||||||
| 			"method":   req.Method(), |  | ||||||
| 			"body":     req.Body(), |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	h := fnv.New64() |  | ||||||
| 	h.Write(bytes) |  | ||||||
| 	return fmt.Sprintf("%x", h.Sum(nil)) |  | ||||||
| } |  | ||||||
| @@ -1,77 +0,0 @@ | |||||||
| package client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/v3/metadata" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestCache(t *testing.T) { |  | ||||||
| 	ctx := context.TODO() |  | ||||||
| 	req := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"} |  | ||||||
|  |  | ||||||
| 	t.Run("CacheMiss", func(t *testing.T) { |  | ||||||
| 		if _, ok := NewCache().Get(ctx, req); ok { |  | ||||||
| 			t.Errorf("Expected to get no result from Get") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("CacheHit", func(t *testing.T) { |  | ||||||
| 		c := NewCache() |  | ||||||
|  |  | ||||||
| 		rsp := "theresponse" |  | ||||||
| 		c.Set(ctx, req, rsp, time.Minute) |  | ||||||
|  |  | ||||||
| 		if res, ok := c.Get(ctx, req); !ok { |  | ||||||
| 			t.Errorf("Expected a result, got nothing") |  | ||||||
| 		} else if res != rsp { |  | ||||||
| 			t.Errorf("Expected '%v' result, got '%v'", rsp, res) |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCacheKey(t *testing.T) { |  | ||||||
| 	ctx := context.TODO() |  | ||||||
|  |  | ||||||
| 	req1 := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"} |  | ||||||
| 	req2 := &testRequest{service: "go.micro.service.foo", method: "Foo.Baz"} |  | ||||||
| 	req3 := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar", body: "customquery"} |  | ||||||
|  |  | ||||||
| 	t.Run("IdenticalRequests", func(t *testing.T) { |  | ||||||
| 		key1 := key(ctx, req1) |  | ||||||
| 		key2 := key(ctx, req1) |  | ||||||
| 		if key1 != key2 { |  | ||||||
| 			t.Errorf("Expected the keys to match for identical requests and context") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("DifferentRequestEndpoints", func(t *testing.T) { |  | ||||||
| 		key1 := key(ctx, req1) |  | ||||||
| 		key2 := key(ctx, req2) |  | ||||||
|  |  | ||||||
| 		if key1 == key2 { |  | ||||||
| 			t.Errorf("Expected the keys to differ for different request endpoints") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("DifferentRequestBody", func(t *testing.T) { |  | ||||||
| 		key1 := key(ctx, req2) |  | ||||||
| 		key2 := key(ctx, req3) |  | ||||||
|  |  | ||||||
| 		if key1 == key2 { |  | ||||||
| 			t.Errorf("Expected the keys to differ for different request bodies") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("DifferentMetadata", func(t *testing.T) { |  | ||||||
| 		mdCtx := metadata.Set(context.TODO(), "Micro-Namespace", "bar") |  | ||||||
| 		key1 := key(mdCtx, req1) |  | ||||||
| 		key2 := key(ctx, req1) |  | ||||||
|  |  | ||||||
| 		if key1 == key2 { |  | ||||||
| 			t.Errorf("Expected the keys to differ for different metadata") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| package client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type clientKey struct{} |  | ||||||
|  |  | ||||||
| func FromContext(ctx context.Context) (Client, bool) { |  | ||||||
| 	c, ok := ctx.Value(clientKey{}).(Client) |  | ||||||
| 	return c, ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewContext(ctx context.Context, c Client) context.Context { |  | ||||||
| 	return context.WithValue(ctx, clientKey{}, c) |  | ||||||
| } |  | ||||||
| @@ -19,16 +19,16 @@ func LookupRoute(ctx context.Context, req Request, opts CallOptions) ([]string, | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// construct the router query | 	// construct the router query | ||||||
| 	query := []router.QueryOption{router.QueryService(req.Service())} | 	query := []router.LookupOption{} | ||||||
|  |  | ||||||
| 	// if a custom network was requested, pass this to the router. By default the router will use it's | 	// if a custom network was requested, pass this to the router. By default the router will use it's | ||||||
| 	// own network, which is set during initialisation. | 	// own network, which is set during initialisation. | ||||||
| 	if len(opts.Network) > 0 { | 	if len(opts.Network) > 0 { | ||||||
| 		query = append(query, router.QueryNetwork(opts.Network)) | 		query = append(query, router.LookupNetwork(opts.Network)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// lookup the routes which can be used to execute the request | 	// lookup the routes which can be used to execute the request | ||||||
| 	routes, err := opts.Router.Lookup(query...) | 	routes, err := opts.Router.Lookup(req.Service(), query...) | ||||||
| 	if err == router.ErrRouteNotFound { | 	if err == router.ErrRouteNotFound { | ||||||
| 		return nil, errors.InternalServerError("go.micro.client", "service %s: %s", req.Service(), err.Error()) | 		return nil, errors.InternalServerError("go.micro.client", "service %s: %s", req.Service(), err.Error()) | ||||||
| 	} else if err != nil { | 	} else if err != nil { | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| // Package mucp provides an mucp client | // Package mucp provides a transport agnostic RPC client | ||||||
| package mucp | package mucp | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -14,7 +14,7 @@ import ( | |||||||
| 	raw "github.com/micro/go-micro/v3/codec/bytes" | 	raw "github.com/micro/go-micro/v3/codec/bytes" | ||||||
| 	"github.com/micro/go-micro/v3/errors" | 	"github.com/micro/go-micro/v3/errors" | ||||||
| 	"github.com/micro/go-micro/v3/metadata" | 	"github.com/micro/go-micro/v3/metadata" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/util/buf" | 	"github.com/micro/go-micro/v3/util/buf" | ||||||
| 	"github.com/micro/go-micro/v3/util/pool" | 	"github.com/micro/go-micro/v3/util/pool" | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ import ( | |||||||
| 	"github.com/micro/go-micro/v3/codec/proto" | 	"github.com/micro/go-micro/v3/codec/proto" | ||||||
| 	"github.com/micro/go-micro/v3/codec/protorpc" | 	"github.com/micro/go-micro/v3/codec/protorpc" | ||||||
| 	"github.com/micro/go-micro/v3/errors" | 	"github.com/micro/go-micro/v3/errors" | ||||||
|  | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/registry" | 	"github.com/micro/go-micro/v3/registry" | ||||||
| 	"github.com/micro/go-micro/v3/transport" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ package mucp | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/micro/go-micro/v3/codec" | 	"github.com/micro/go-micro/v3/codec" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type rpcResponse struct { | type rpcResponse struct { | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/v3/client" | 	"github.com/micro/go-micro/v3/client" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestCallOptions(t *testing.T) { | func TestCallOptions(t *testing.T) { | ||||||
|   | |||||||
| @@ -7,13 +7,13 @@ import ( | |||||||
| 	"github.com/micro/go-micro/v3/broker" | 	"github.com/micro/go-micro/v3/broker" | ||||||
| 	"github.com/micro/go-micro/v3/broker/http" | 	"github.com/micro/go-micro/v3/broker/http" | ||||||
| 	"github.com/micro/go-micro/v3/codec" | 	"github.com/micro/go-micro/v3/codec" | ||||||
|  | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
|  | 	thttp "github.com/micro/go-micro/v3/network/transport/http" | ||||||
| 	"github.com/micro/go-micro/v3/registry" | 	"github.com/micro/go-micro/v3/registry" | ||||||
| 	"github.com/micro/go-micro/v3/router" | 	"github.com/micro/go-micro/v3/router" | ||||||
| 	regRouter "github.com/micro/go-micro/v3/router/registry" | 	regRouter "github.com/micro/go-micro/v3/router/registry" | ||||||
| 	"github.com/micro/go-micro/v3/selector" | 	"github.com/micro/go-micro/v3/selector" | ||||||
| 	"github.com/micro/go-micro/v3/selector/random" | 	"github.com/micro/go-micro/v3/selector/roundrobin" | ||||||
| 	"github.com/micro/go-micro/v3/transport" |  | ||||||
| 	thttp "github.com/micro/go-micro/v3/transport/http" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Options struct { | type Options struct { | ||||||
| @@ -36,9 +36,6 @@ type Options struct { | |||||||
| 	PoolSize int | 	PoolSize int | ||||||
| 	PoolTTL  time.Duration | 	PoolTTL  time.Duration | ||||||
|  |  | ||||||
| 	// Response cache |  | ||||||
| 	Cache *Cache |  | ||||||
|  |  | ||||||
| 	// Middleware for client | 	// Middleware for client | ||||||
| 	Wrappers []Wrapper | 	Wrappers []Wrapper | ||||||
|  |  | ||||||
| @@ -55,8 +52,6 @@ type CallOptions struct { | |||||||
| 	Address []string | 	Address []string | ||||||
| 	// Backoff func | 	// Backoff func | ||||||
| 	Backoff BackoffFunc | 	Backoff BackoffFunc | ||||||
| 	// Duration to cache the response for |  | ||||||
| 	CacheExpiry time.Duration |  | ||||||
| 	// Transport Dial Timeout | 	// Transport Dial Timeout | ||||||
| 	DialTimeout time.Duration | 	DialTimeout time.Duration | ||||||
| 	// Number of Call attempts | 	// Number of Call attempts | ||||||
| @@ -109,7 +104,6 @@ type RequestOptions struct { | |||||||
|  |  | ||||||
| func NewOptions(options ...Option) Options { | func NewOptions(options ...Option) Options { | ||||||
| 	opts := Options{ | 	opts := Options{ | ||||||
| 		Cache:       NewCache(), |  | ||||||
| 		Context:     context.Background(), | 		Context:     context.Background(), | ||||||
| 		ContentType: "application/protobuf", | 		ContentType: "application/protobuf", | ||||||
| 		Codecs:      make(map[string]codec.NewCodec), | 		Codecs:      make(map[string]codec.NewCodec), | ||||||
| @@ -125,7 +119,7 @@ func NewOptions(options ...Option) Options { | |||||||
| 		PoolTTL:   DefaultPoolTTL, | 		PoolTTL:   DefaultPoolTTL, | ||||||
| 		Broker:    http.NewBroker(), | 		Broker:    http.NewBroker(), | ||||||
| 		Router:    regRouter.NewRouter(), | 		Router:    regRouter.NewRouter(), | ||||||
| 		Selector:  random.NewSelector(), | 		Selector:  roundrobin.NewSelector(), | ||||||
| 		Transport: thttp.NewTransport(), | 		Transport: thttp.NewTransport(), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -357,14 +351,6 @@ func WithAuthToken() CallOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // WithCache is a CallOption which sets the duration the response |  | ||||||
| // shoull be cached for |  | ||||||
| func WithCache(c time.Duration) CallOption { |  | ||||||
| 	return func(o *CallOptions) { |  | ||||||
| 		o.CacheExpiry = c |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithNetwork is a CallOption which sets the network attribute | // WithNetwork is a CallOption which sets the network attribute | ||||||
| func WithNetwork(n string) CallOption { | func WithNetwork(n string) CallOption { | ||||||
| 	return func(o *CallOptions) { | 	return func(o *CallOptions) { | ||||||
|   | |||||||
| @@ -1,402 +0,0 @@ | |||||||
| // Code generated by protoc-gen-go. DO NOT EDIT. |  | ||||||
| // source: client/service/proto/client.proto |  | ||||||
|  |  | ||||||
| package go_micro_client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	context "context" |  | ||||||
| 	fmt "fmt" |  | ||||||
| 	proto "github.com/golang/protobuf/proto" |  | ||||||
| 	grpc "google.golang.org/grpc" |  | ||||||
| 	codes "google.golang.org/grpc/codes" |  | ||||||
| 	status "google.golang.org/grpc/status" |  | ||||||
| 	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 |  | ||||||
|  |  | ||||||
| type Request struct { |  | ||||||
| 	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"` |  | ||||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` |  | ||||||
| 	XXX_unrecognized     []byte   `json:"-"` |  | ||||||
| 	XXX_sizecache        int32    `json:"-"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Request) Reset()         { *m = Request{} } |  | ||||||
| func (m *Request) String() string { return proto.CompactTextString(m) } |  | ||||||
| func (*Request) ProtoMessage()    {} |  | ||||||
| func (*Request) Descriptor() ([]byte, []int) { |  | ||||||
| 	return fileDescriptor_27c3d425ddd1a066, []int{0} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Request) XXX_Unmarshal(b []byte) error { |  | ||||||
| 	return xxx_messageInfo_Request.Unmarshal(m, b) |  | ||||||
| } |  | ||||||
| func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { |  | ||||||
| 	return xxx_messageInfo_Request.Marshal(b, m, deterministic) |  | ||||||
| } |  | ||||||
| func (m *Request) XXX_Merge(src proto.Message) { |  | ||||||
| 	xxx_messageInfo_Request.Merge(m, src) |  | ||||||
| } |  | ||||||
| func (m *Request) XXX_Size() int { |  | ||||||
| 	return xxx_messageInfo_Request.Size(m) |  | ||||||
| } |  | ||||||
| func (m *Request) XXX_DiscardUnknown() { |  | ||||||
| 	xxx_messageInfo_Request.DiscardUnknown(m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var xxx_messageInfo_Request proto.InternalMessageInfo |  | ||||||
|  |  | ||||||
| func (m *Request) GetService() string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Service |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Request) GetEndpoint() string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Endpoint |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Request) GetContentType() string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.ContentType |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Request) GetBody() []byte { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Body |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Response struct { |  | ||||||
| 	Body                 []byte   `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` |  | ||||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` |  | ||||||
| 	XXX_unrecognized     []byte   `json:"-"` |  | ||||||
| 	XXX_sizecache        int32    `json:"-"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Response) Reset()         { *m = Response{} } |  | ||||||
| func (m *Response) String() string { return proto.CompactTextString(m) } |  | ||||||
| func (*Response) ProtoMessage()    {} |  | ||||||
| func (*Response) Descriptor() ([]byte, []int) { |  | ||||||
| 	return fileDescriptor_27c3d425ddd1a066, []int{1} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Response) XXX_Unmarshal(b []byte) error { |  | ||||||
| 	return xxx_messageInfo_Response.Unmarshal(m, b) |  | ||||||
| } |  | ||||||
| func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { |  | ||||||
| 	return xxx_messageInfo_Response.Marshal(b, m, deterministic) |  | ||||||
| } |  | ||||||
| func (m *Response) XXX_Merge(src proto.Message) { |  | ||||||
| 	xxx_messageInfo_Response.Merge(m, src) |  | ||||||
| } |  | ||||||
| func (m *Response) XXX_Size() int { |  | ||||||
| 	return xxx_messageInfo_Response.Size(m) |  | ||||||
| } |  | ||||||
| func (m *Response) XXX_DiscardUnknown() { |  | ||||||
| 	xxx_messageInfo_Response.DiscardUnknown(m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var xxx_messageInfo_Response proto.InternalMessageInfo |  | ||||||
|  |  | ||||||
| func (m *Response) GetBody() []byte { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Body |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Message struct { |  | ||||||
| 	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"` |  | ||||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` |  | ||||||
| 	XXX_unrecognized     []byte   `json:"-"` |  | ||||||
| 	XXX_sizecache        int32    `json:"-"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) Reset()         { *m = Message{} } |  | ||||||
| func (m *Message) String() string { return proto.CompactTextString(m) } |  | ||||||
| func (*Message) ProtoMessage()    {} |  | ||||||
| func (*Message) Descriptor() ([]byte, []int) { |  | ||||||
| 	return fileDescriptor_27c3d425ddd1a066, []int{2} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) XXX_Unmarshal(b []byte) error { |  | ||||||
| 	return xxx_messageInfo_Message.Unmarshal(m, b) |  | ||||||
| } |  | ||||||
| func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { |  | ||||||
| 	return xxx_messageInfo_Message.Marshal(b, m, deterministic) |  | ||||||
| } |  | ||||||
| func (m *Message) XXX_Merge(src proto.Message) { |  | ||||||
| 	xxx_messageInfo_Message.Merge(m, src) |  | ||||||
| } |  | ||||||
| func (m *Message) XXX_Size() int { |  | ||||||
| 	return xxx_messageInfo_Message.Size(m) |  | ||||||
| } |  | ||||||
| func (m *Message) XXX_DiscardUnknown() { |  | ||||||
| 	xxx_messageInfo_Message.DiscardUnknown(m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var xxx_messageInfo_Message proto.InternalMessageInfo |  | ||||||
|  |  | ||||||
| func (m *Message) GetTopic() string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Topic |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) GetContentType() string { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.ContentType |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) GetBody() []byte { |  | ||||||
| 	if m != nil { |  | ||||||
| 		return m.Body |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	proto.RegisterType((*Request)(nil), "go.micro.client.Request") |  | ||||||
| 	proto.RegisterType((*Response)(nil), "go.micro.client.Response") |  | ||||||
| 	proto.RegisterType((*Message)(nil), "go.micro.client.Message") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func init() { proto.RegisterFile("client/service/proto/client.proto", fileDescriptor_27c3d425ddd1a066) } |  | ||||||
|  |  | ||||||
| var fileDescriptor_27c3d425ddd1a066 = []byte{ |  | ||||||
| 	// 267 bytes of a gzipped FileDescriptorProto |  | ||||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0xc1, 0x4b, 0xc3, 0x30, |  | ||||||
| 	0x14, 0xc6, 0x97, 0x6d, 0xb6, 0xf3, 0x39, 0x10, 0x1e, 0x1e, 0x62, 0x0f, 0xb2, 0xf5, 0xd4, 0x53, |  | ||||||
| 	0x2b, 0x7a, 0x16, 0x0f, 0x3d, 0x0b, 0x52, 0xc5, 0xab, 0xb4, 0xd9, 0x63, 0x06, 0xba, 0x24, 0x36, |  | ||||||
| 	0xd9, 0xa0, 0x7f, 0xa4, 0xff, 0x93, 0x90, 0x46, 0x27, 0xba, 0x5d, 0xbc, 0xe5, 0xfb, 0x7e, 0xe4, |  | ||||||
| 	0x7b, 0x2f, 0x5f, 0x60, 0x29, 0x5a, 0x49, 0xca, 0x15, 0x96, 0xba, 0x9d, 0x14, 0x54, 0x98, 0x4e, |  | ||||||
| 	0x3b, 0x5d, 0x0c, 0x66, 0xee, 0x05, 0x9e, 0xaf, 0x75, 0xbe, 0x91, 0xa2, 0xd3, 0xf9, 0x60, 0xa7, |  | ||||||
| 	0x3b, 0x88, 0x2b, 0x7a, 0xdf, 0x92, 0x75, 0xc8, 0x21, 0x0e, 0x37, 0x39, 0x5b, 0xb0, 0xec, 0xb4, |  | ||||||
| 	0xfa, 0x92, 0x98, 0xc0, 0x8c, 0xd4, 0xca, 0x68, 0xa9, 0x1c, 0x1f, 0x7b, 0xf4, 0xad, 0x71, 0x09, |  | ||||||
| 	0x73, 0xa1, 0x95, 0x23, 0xe5, 0x5e, 0x5d, 0x6f, 0x88, 0x4f, 0x3c, 0x3f, 0x0b, 0xde, 0x73, 0x6f, |  | ||||||
| 	0x08, 0x11, 0xa6, 0x8d, 0x5e, 0xf5, 0x7c, 0xba, 0x60, 0xd9, 0xbc, 0xf2, 0xe7, 0xf4, 0x0a, 0x66, |  | ||||||
| 	0x15, 0x59, 0xa3, 0x95, 0xdd, 0x73, 0xf6, 0x83, 0xbf, 0x40, 0xfc, 0x40, 0xd6, 0xd6, 0x6b, 0xc2, |  | ||||||
| 	0x0b, 0x38, 0x71, 0xda, 0x48, 0x11, 0xb6, 0x1a, 0xc4, 0x9f, 0xb9, 0xe3, 0xe3, 0x73, 0x27, 0xfb, |  | ||||||
| 	0xdc, 0x9b, 0x0f, 0x06, 0x51, 0xe9, 0x9f, 0x8e, 0x77, 0x30, 0x2d, 0xeb, 0xb6, 0x45, 0x9e, 0xff, |  | ||||||
| 	0x2a, 0x25, 0x0f, 0x8d, 0x24, 0x97, 0x07, 0xc8, 0xb0, 0x73, 0x3a, 0xc2, 0x12, 0xa2, 0x27, 0xd7, |  | ||||||
| 	0x51, 0xbd, 0xf9, 0x67, 0x40, 0xc6, 0xae, 0x19, 0xde, 0x43, 0xfc, 0xb8, 0x6d, 0x5a, 0x69, 0xdf, |  | ||||||
| 	0x0e, 0xa4, 0x84, 0x02, 0x92, 0xa3, 0x24, 0x1d, 0x35, 0x91, 0xff, 0xd7, 0xdb, 0xcf, 0x00, 0x00, |  | ||||||
| 	0x00, 0xff, 0xff, 0xd6, 0x3f, 0xc3, 0xa1, 0xfc, 0x01, 0x00, 0x00, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. |  | ||||||
| var _ context.Context |  | ||||||
| var _ grpc.ClientConn |  | ||||||
|  |  | ||||||
| // 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.SupportPackageIsVersion4 |  | ||||||
|  |  | ||||||
| // ClientClient is the client API for Client service. |  | ||||||
| // |  | ||||||
| // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#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.ClientConn |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewClientClient(cc *grpc.ClientConn) 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. |  | ||||||
| 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) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UnimplementedClientServer can be embedded to have forward compatible implementations. |  | ||||||
| type UnimplementedClientServer struct { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (*UnimplementedClientServer) Call(ctx context.Context, req *Request) (*Response, error) { |  | ||||||
| 	return nil, status.Errorf(codes.Unimplemented, "method Call not implemented") |  | ||||||
| } |  | ||||||
| func (*UnimplementedClientServer) Stream(srv Client_StreamServer) error { |  | ||||||
| 	return status.Errorf(codes.Unimplemented, "method Stream not implemented") |  | ||||||
| } |  | ||||||
| func (*UnimplementedClientServer) Publish(ctx context.Context, req *Message) (*Message, error) { |  | ||||||
| 	return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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", |  | ||||||
| } |  | ||||||
| @@ -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/micro/go-micro/v3/api" |  | ||||||
| 	client "github.com/micro/go-micro/v3/client" |  | ||||||
| 	server "github.com/micro/go-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) |  | ||||||
| } |  | ||||||
| @@ -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; |  | ||||||
| } |  | ||||||
| @@ -1,65 +0,0 @@ | |||||||
| package client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/micro/go-micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type testRequest struct { |  | ||||||
| 	service     string |  | ||||||
| 	method      string |  | ||||||
| 	endpoint    string |  | ||||||
| 	contentType string |  | ||||||
| 	codec       codec.Codec |  | ||||||
| 	body        interface{} |  | ||||||
| 	opts        RequestOptions |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request { |  | ||||||
| 	var opts RequestOptions |  | ||||||
|  |  | ||||||
| 	for _, o := range reqOpts { |  | ||||||
| 		o(&opts) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// set the content-type specified |  | ||||||
| 	if len(opts.ContentType) > 0 { |  | ||||||
| 		contentType = opts.ContentType |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &testRequest{ |  | ||||||
| 		service:     service, |  | ||||||
| 		method:      endpoint, |  | ||||||
| 		endpoint:    endpoint, |  | ||||||
| 		body:        request, |  | ||||||
| 		contentType: contentType, |  | ||||||
| 		opts:        opts, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) ContentType() string { |  | ||||||
| 	return r.contentType |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) Service() string { |  | ||||||
| 	return r.service |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) Method() string { |  | ||||||
| 	return r.method |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) Endpoint() string { |  | ||||||
| 	return r.endpoint |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) Body() interface{} { |  | ||||||
| 	return r.body |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) Codec() codec.Writer { |  | ||||||
| 	return r.codec |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) Stream() bool { |  | ||||||
| 	return r.opts.Stream |  | ||||||
| } |  | ||||||
| @@ -1,7 +1,2 @@ | |||||||
| // Package debug provides micro debug packages | // Package debug provides interfaces for service debugging | ||||||
| package debug | package debug | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	// DefaultName is the name of debug service |  | ||||||
| 	DefaultName = "go.micro.debug" |  | ||||||
| ) |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| // Package events contains interfaces for managing events within distributed systems | // Package events is for event streaming and storage | ||||||
| package events | package events | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -14,18 +14,21 @@ var ( | |||||||
| 	ErrEncodingMessage = errors.New("Error encoding message") | 	ErrEncodingMessage = errors.New("Error encoding message") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Stream of events | // Stream is an event streaming interface | ||||||
| type Stream interface { | type Stream interface { | ||||||
| 	Publish(topic string, opts ...PublishOption) error | 	Publish(topic string, msg interface{}, opts ...PublishOption) error | ||||||
| 	Subscribe(opts ...SubscribeOption) (<-chan Event, error) | 	Subscribe(topic string, opts ...SubscribeOption) (<-chan Event, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Store of events | // Store is an event store interface | ||||||
| type Store interface { | type Store interface { | ||||||
| 	Read(opts ...ReadOption) ([]*Event, error) | 	Read(topic string, opts ...ReadOption) ([]*Event, error) | ||||||
| 	Write(event *Event, opts ...WriteOption) error | 	Write(event *Event, opts ...WriteOption) error | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type AckFunc func() error | ||||||
|  | type NackFunc func() error | ||||||
|  |  | ||||||
| // Event is the object returned by the broker when you subscribe to a topic | // Event is the object returned by the broker when you subscribe to a topic | ||||||
| type Event struct { | type Event struct { | ||||||
| 	// ID to uniquely identify the event | 	// ID to uniquely identify the event | ||||||
| @@ -34,13 +37,34 @@ type Event struct { | |||||||
| 	Topic string | 	Topic string | ||||||
| 	// Timestamp of the event | 	// Timestamp of the event | ||||||
| 	Timestamp time.Time | 	Timestamp time.Time | ||||||
| 	// Metadata contains the encoded event was indexed by | 	// Metadata contains the values the event was indexed by | ||||||
| 	Metadata map[string]string | 	Metadata map[string]string | ||||||
| 	// Payload contains the encoded message | 	// Payload contains the encoded message | ||||||
| 	Payload []byte | 	Payload []byte | ||||||
|  |  | ||||||
|  | 	ackFunc  AckFunc | ||||||
|  | 	nackFunc NackFunc | ||||||
| } | } | ||||||
|  |  | ||||||
| // Unmarshal the events message into an object | // Unmarshal the events message into an object | ||||||
| func (e *Event) Unmarshal(v interface{}) error { | func (e *Event) Unmarshal(v interface{}) error { | ||||||
| 	return json.Unmarshal(e.Payload, v) | 	return json.Unmarshal(e.Payload, v) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Ack acknowledges successful processing of the event in ManualAck mode | ||||||
|  | func (e *Event) Ack() error { | ||||||
|  | 	return e.ackFunc() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Event) SetAckFunc(f AckFunc) { | ||||||
|  | 	e.ackFunc = f | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Nack negatively acknowledges processing of the event (i.e. failure) in ManualAck mode | ||||||
|  | func (e *Event) Nack() error { | ||||||
|  | 	return e.nackFunc() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Event) SetNackFunc(f NackFunc) { | ||||||
|  | 	e.nackFunc = f | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,185 +0,0 @@ | |||||||
| package memory |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"sync" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/google/uuid" |  | ||||||
| 	"github.com/micro/go-micro/v3/events" |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| 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("") |  | ||||||
| 		assert.Equalf(t, err, events.ErrMissingTopic, "Publishing to a blank topic should return an error") |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	// TestFirehose will publish a message to the test topic. The subscriber will subscribe to the |  | ||||||
| 	// firehose topic (indicated by a lack of the topic option). |  | ||||||
| 	t.Run("TestFirehose", func(t *testing.T) { |  | ||||||
| 		payload := &testPayload{Message: "HelloWorld"} |  | ||||||
| 		metadata := map[string]string{"foo": "bar"} |  | ||||||
|  |  | ||||||
| 		// create the subscriber |  | ||||||
| 		evChan, err := stream.Subscribe() |  | ||||||
| 		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", |  | ||||||
| 			events.WithPayload(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() |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	// 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(events.WithTopic("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", |  | ||||||
| 			events.WithPayload(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(events.WithTopic(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, |  | ||||||
| 			events.WithPayload(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( |  | ||||||
| 			events.WithTopic(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() |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @@ -1,200 +0,0 @@ | |||||||
| package nats |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"net" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"sync" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/google/uuid" |  | ||||||
| 	"github.com/micro/go-micro/v3/events" |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type testPayload struct { |  | ||||||
| 	Message string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestStream(t *testing.T) { |  | ||||||
| 	_, 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("") |  | ||||||
| 		assert.Equalf(t, err, events.ErrMissingTopic, "Publishing to a blank topic should return an error") |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	// TestFirehose will publish a message to the test topic. The subscriber will subscribe to the |  | ||||||
| 	// firehose topic (indicated by a lack of the topic option). |  | ||||||
| 	t.Run("TestFirehose", func(t *testing.T) { |  | ||||||
| 		payload := &testPayload{Message: "HelloWorld"} |  | ||||||
| 		metadata := map[string]string{"foo": "bar"} |  | ||||||
|  |  | ||||||
| 		// create the subscriber |  | ||||||
| 		evChan, err := stream.Subscribe() |  | ||||||
| 		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", |  | ||||||
| 			events.WithPayload(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() |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	// 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(events.WithTopic("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", |  | ||||||
| 			events.WithPayload(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(events.WithTopic(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, |  | ||||||
| 			events.WithPayload(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( |  | ||||||
| 			events.WithTopic(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() |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @@ -6,9 +6,6 @@ import "time" | |||||||
| type PublishOptions struct { | type PublishOptions struct { | ||||||
| 	// Metadata contains any keys which can be used to query the data, for example a customer id | 	// Metadata contains any keys which can be used to query the data, for example a customer id | ||||||
| 	Metadata map[string]string | 	Metadata map[string]string | ||||||
| 	// Payload contains any additonal data which is relevent to the event but does not need to be |  | ||||||
| 	// indexed such as structured data |  | ||||||
| 	Payload interface{} |  | ||||||
| 	// Timestamp to set for the event, if the timestamp is a zero value, the current time will be used | 	// Timestamp to set for the event, if the timestamp is a zero value, the current time will be used | ||||||
| 	Timestamp time.Time | 	Timestamp time.Time | ||||||
| } | } | ||||||
| @@ -23,13 +20,6 @@ func WithMetadata(md map[string]string) PublishOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // WithPayload sets the payload field on PublishOptions |  | ||||||
| func WithPayload(p interface{}) PublishOption { |  | ||||||
| 	return func(o *PublishOptions) { |  | ||||||
| 		o.Payload = p |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithTimestamp sets the timestamp field on PublishOptions | // WithTimestamp sets the timestamp field on PublishOptions | ||||||
| func WithTimestamp(t time.Time) PublishOption { | func WithTimestamp(t time.Time) PublishOption { | ||||||
| 	return func(o *PublishOptions) { | 	return func(o *PublishOptions) { | ||||||
| @@ -42,12 +32,20 @@ type SubscribeOptions struct { | |||||||
| 	// Queue is the name of the subscribers queue, if two subscribers have the same queue the message | 	// Queue is the name of the subscribers queue, if two subscribers have the same queue the message | ||||||
| 	// should only be published to one of them | 	// should only be published to one of them | ||||||
| 	Queue string | 	Queue string | ||||||
| 	// Topic to subscribe to, if left blank the consumer will be subscribed to the firehouse topic which |  | ||||||
| 	// recieves all events |  | ||||||
| 	Topic string |  | ||||||
| 	// StartAtTime is the time from which the messages should be consumed from. If not provided then | 	// StartAtTime is the time from which the messages should be consumed from. If not provided then | ||||||
| 	// the messages will be consumed starting from the moment the Subscription starts. | 	// the messages will be consumed starting from the moment the Subscription starts. | ||||||
| 	StartAtTime time.Time | 	StartAtTime time.Time | ||||||
|  | 	// AutoAck if true (default true), automatically acknowledges every message so it will not be redelivered. | ||||||
|  | 	// If false specifies that each message need ts to be manually acknowledged by the subscriber. | ||||||
|  | 	// If processing is successful the message should be ack'ed to remove the message from the stream. | ||||||
|  | 	// If processing is unsuccessful the message should be nack'ed (negative acknowledgement) which will mean it will | ||||||
|  | 	// remain on the stream to be processed again. | ||||||
|  | 	AutoAck bool | ||||||
|  | 	AckWait time.Duration | ||||||
|  | 	// RetryLimit indicates number of times a message is retried | ||||||
|  | 	RetryLimit int | ||||||
|  | 	// CustomRetries indicates whether to use RetryLimit | ||||||
|  | 	CustomRetries bool | ||||||
| } | } | ||||||
|  |  | ||||||
| // SubscribeOption sets attributes on SubscribeOptions | // SubscribeOption sets attributes on SubscribeOptions | ||||||
| @@ -60,13 +58,6 @@ func WithQueue(q string) SubscribeOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // WithTopic sets the topic to subscribe to |  | ||||||
| func WithTopic(t string) SubscribeOption { |  | ||||||
| 	return func(o *SubscribeOptions) { |  | ||||||
| 		o.Topic = t |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithStartAtTime sets the StartAtTime field on SubscribeOptions to the value provided | // WithStartAtTime sets the StartAtTime field on SubscribeOptions to the value provided | ||||||
| func WithStartAtTime(t time.Time) SubscribeOption { | func WithStartAtTime(t time.Time) SubscribeOption { | ||||||
| 	return func(o *SubscribeOptions) { | 	return func(o *SubscribeOptions) { | ||||||
| @@ -74,6 +65,31 @@ func WithStartAtTime(t time.Time) SubscribeOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithAutoAck sets the AutoAck field on SubscribeOptions and an ackWait duration after which if no ack is received | ||||||
|  | // the message is requeued in case auto ack is turned off | ||||||
|  | func WithAutoAck(ack bool, ackWait time.Duration) SubscribeOption { | ||||||
|  | 	return func(o *SubscribeOptions) { | ||||||
|  | 		o.AutoAck = ack | ||||||
|  | 		o.AckWait = ackWait | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithRetryLimit sets the RetryLimit field on SubscribeOptions. | ||||||
|  | // Set to -1 for infinite retries (default) | ||||||
|  | func WithRetryLimit(retries int) SubscribeOption { | ||||||
|  | 	return func(o *SubscribeOptions) { | ||||||
|  | 		o.RetryLimit = retries | ||||||
|  | 		o.CustomRetries = true | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s SubscribeOptions) GetRetryLimit() int { | ||||||
|  | 	if !s.CustomRetries { | ||||||
|  | 		return -1 | ||||||
|  | 	} | ||||||
|  | 	return s.RetryLimit | ||||||
|  | } | ||||||
|  |  | ||||||
| // WriteOptions contains all the options which can be provided when writing an event to a store | // WriteOptions contains all the options which can be provided when writing an event to a store | ||||||
| type WriteOptions struct { | type WriteOptions struct { | ||||||
| 	// TTL is the duration the event should be recorded for, a zero value TTL indicates the event should | 	// TTL is the duration the event should be recorded for, a zero value TTL indicates the event should | ||||||
| @@ -93,47 +109,24 @@ func WithTTL(d time.Duration) WriteOption { | |||||||
|  |  | ||||||
| // ReadOptions contains all the options which can be provided when reading events from a store | // ReadOptions contains all the options which can be provided when reading events from a store | ||||||
| type ReadOptions struct { | type ReadOptions struct { | ||||||
| 	// Topic to read events from, if no topic is provided events from all topics will be returned |  | ||||||
| 	Topic string |  | ||||||
| 	// Query to filter the results using. The store will query the metadata provided when the event |  | ||||||
| 	// was written to the store |  | ||||||
| 	Query map[string]string |  | ||||||
| 	// Limit the number of results to return | 	// Limit the number of results to return | ||||||
| 	Limit int | 	Limit uint | ||||||
| 	// Offset the results by this number, useful for paginated queries | 	// Offset the results by this number, useful for paginated queries | ||||||
| 	Offset int | 	Offset uint | ||||||
| } | } | ||||||
|  |  | ||||||
| // ReadOption sets attributes on ReadOptions | // ReadOption sets attributes on ReadOptions | ||||||
| type ReadOption func(o *ReadOptions) | type ReadOption func(o *ReadOptions) | ||||||
|  |  | ||||||
| // ReadTopic sets the topic attribute on ReadOptions |  | ||||||
| func ReadTopic(t string) ReadOption { |  | ||||||
| 	return func(o *ReadOptions) { |  | ||||||
| 		o.Topic = t |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ReadFilter sets a key and value in the query |  | ||||||
| func ReadFilter(key, value string) ReadOption { |  | ||||||
| 	return func(o *ReadOptions) { |  | ||||||
| 		if o.Query == nil { |  | ||||||
| 			o.Query = map[string]string{key: value} |  | ||||||
| 		} else { |  | ||||||
| 			o.Query[key] = value |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ReadLimit sets the limit attribute on ReadOptions | // ReadLimit sets the limit attribute on ReadOptions | ||||||
| func ReadLimit(l int) ReadOption { | func ReadLimit(l uint) ReadOption { | ||||||
| 	return func(o *ReadOptions) { | 	return func(o *ReadOptions) { | ||||||
| 		o.Limit = 1 | 		o.Limit = 1 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // ReadOffset sets the offset attribute on ReadOptions | // ReadOffset sets the offset attribute on ReadOptions | ||||||
| func ReadOffset(l int) ReadOption { | func ReadOffset(l uint) ReadOption { | ||||||
| 	return func(o *ReadOptions) { | 	return func(o *ReadOptions) { | ||||||
| 		o.Offset = 1 | 		o.Offset = 1 | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								events/store/options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								events/store/options.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | package store | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v3/store" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Options struct { | ||||||
|  | 	Store store.Store | ||||||
|  | 	TTL   time.Duration | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Option func(o *Options) | ||||||
|  |  | ||||||
|  | // WithStore sets the underlying store to use | ||||||
|  | func WithStore(s store.Store) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Store = s | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithTTL sets the default TTL | ||||||
|  | func WithTTL(ttl time.Duration) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.TTL = ttl | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								events/store/store.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								events/store/store.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | package store | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v3/events" | ||||||
|  | 	gostore "github.com/micro/go-micro/v3/store" | ||||||
|  | 	"github.com/micro/go-micro/v3/store/memory" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const joinKey = "/" | ||||||
|  |  | ||||||
|  | // NewStore returns an initialized events store | ||||||
|  | func NewStore(opts ...Option) events.Store { | ||||||
|  | 	// parse the options | ||||||
|  | 	var options Options | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	if options.TTL.Seconds() == 0 { | ||||||
|  | 		options.TTL = time.Hour * 24 | ||||||
|  | 	} | ||||||
|  | 	if options.Store == nil { | ||||||
|  | 		options.Store = memory.NewStore() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// return the store | ||||||
|  | 	return &evStore{options} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type evStore struct { | ||||||
|  | 	opts Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Read events for a topic | ||||||
|  | func (s *evStore) Read(topic string, opts ...events.ReadOption) ([]*events.Event, error) { | ||||||
|  | 	// validate the topic | ||||||
|  | 	if len(topic) == 0 { | ||||||
|  | 		return nil, events.ErrMissingTopic | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// parse the options | ||||||
|  | 	options := events.ReadOptions{ | ||||||
|  | 		Offset: 0, | ||||||
|  | 		Limit:  250, | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// execute the request | ||||||
|  | 	recs, err := s.opts.Store.Read(topic+joinKey, | ||||||
|  | 		gostore.ReadPrefix(), | ||||||
|  | 		gostore.ReadLimit(options.Limit), | ||||||
|  | 		gostore.ReadOffset(options.Offset), | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "Error reading from store") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// unmarshal the result | ||||||
|  | 	result := make([]*events.Event, len(recs)) | ||||||
|  | 	for i, r := range recs { | ||||||
|  | 		var e events.Event | ||||||
|  | 		if err := json.Unmarshal(r.Value, &e); err != nil { | ||||||
|  | 			return nil, errors.Wrap(err, "Invalid event returned from stroe") | ||||||
|  | 		} | ||||||
|  | 		result[i] = &e | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Write an event to the store | ||||||
|  | func (s *evStore) Write(event *events.Event, opts ...events.WriteOption) error { | ||||||
|  | 	// parse the options | ||||||
|  | 	options := events.WriteOptions{ | ||||||
|  | 		TTL: s.opts.TTL, | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// construct the store record | ||||||
|  | 	bytes, err := json.Marshal(event) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.Wrap(err, "Error mashaling event to JSON") | ||||||
|  | 	} | ||||||
|  | 	record := &gostore.Record{ | ||||||
|  | 		Key:    event.Topic + joinKey + event.ID, | ||||||
|  | 		Value:  bytes, | ||||||
|  | 		Expiry: options.TTL, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// write the record to the store | ||||||
|  | 	if err := s.opts.Store.Write(record); err != nil { | ||||||
|  | 		return errors.Wrap(err, "Error writing to the store") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								events/store/store_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								events/store/store_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | package store | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/google/uuid" | ||||||
|  | 	"github.com/micro/go-micro/v3/events" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestStore(t *testing.T) { | ||||||
|  | 	store := NewStore() | ||||||
|  |  | ||||||
|  | 	testData := []events.Event{ | ||||||
|  | 		{ID: uuid.New().String(), Topic: "foo"}, | ||||||
|  | 		{ID: uuid.New().String(), Topic: "foo"}, | ||||||
|  | 		{ID: uuid.New().String(), Topic: "bar"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// write the records to the store | ||||||
|  | 	t.Run("Write", func(t *testing.T) { | ||||||
|  | 		for _, event := range testData { | ||||||
|  | 			err := store.Write(&event) | ||||||
|  | 			assert.Nilf(t, err, "Writing an event should not return an error") | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// should not be able to read events from a blank topic | ||||||
|  | 	t.Run("ReadMissingTopic", func(t *testing.T) { | ||||||
|  | 		evs, err := store.Read("") | ||||||
|  | 		assert.Equal(t, err, events.ErrMissingTopic, "Reading a blank topic should return an error") | ||||||
|  | 		assert.Nil(t, evs, "No events should be returned") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// should only get the events from the topic requested | ||||||
|  | 	t.Run("ReadTopic", func(t *testing.T) { | ||||||
|  | 		evs, err := store.Read("foo") | ||||||
|  | 		assert.Nilf(t, err, "No error should be returned") | ||||||
|  | 		assert.Len(t, evs, 2, "Only the events for this topic should be returned") | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// limits should be honoured | ||||||
|  | 	t.Run("ReadTopicLimit", func(t *testing.T) { | ||||||
|  | 		evs, err := store.Read("foo", events.ReadLimit(1)) | ||||||
|  | 		assert.Nilf(t, err, "No error should be returned") | ||||||
|  | 		assert.Len(t, evs, 1, "The result should include no more than the read limit") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
| @@ -32,6 +32,12 @@ type subscriber struct { | |||||||
| 	Queue   string | 	Queue   string | ||||||
| 	Topic   string | 	Topic   string | ||||||
| 	Channel chan events.Event | 	Channel chan events.Event | ||||||
|  | 
 | ||||||
|  | 	sync.RWMutex | ||||||
|  | 	retryMap   map[string]int | ||||||
|  | 	retryLimit int | ||||||
|  | 	autoAck    bool | ||||||
|  | 	ackWait    time.Duration | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type mem struct { | type mem struct { | ||||||
| @@ -41,7 +47,7 @@ type mem struct { | |||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *mem) Publish(topic string, opts ...events.PublishOption) error { | func (m *mem) Publish(topic string, msg interface{}, opts ...events.PublishOption) error { | ||||||
| 	// validate the topic | 	// validate the topic | ||||||
| 	if len(topic) == 0 { | 	if len(topic) == 0 { | ||||||
| 		return events.ErrMissingTopic | 		return events.ErrMissingTopic | ||||||
| @@ -57,10 +63,10 @@ func (m *mem) Publish(topic string, opts ...events.PublishOption) error { | |||||||
| 
 | 
 | ||||||
| 	// encode the message if it's not already encoded | 	// encode the message if it's not already encoded | ||||||
| 	var payload []byte | 	var payload []byte | ||||||
| 	if p, ok := options.Payload.([]byte); ok { | 	if p, ok := msg.([]byte); ok { | ||||||
| 		payload = p | 		payload = p | ||||||
| 	} else { | 	} else { | ||||||
| 		p, err := json.Marshal(options.Payload) | 		p, err := json.Marshal(msg) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return events.ErrEncodingMessage | 			return events.ErrEncodingMessage | ||||||
| 		} | 		} | ||||||
| @@ -94,20 +100,38 @@ func (m *mem) Publish(topic string, opts ...events.PublishOption) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *mem) Subscribe(opts ...events.SubscribeOption) (<-chan events.Event, error) { | 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 | 	// parse the options | ||||||
| 	options := events.SubscribeOptions{ | 	options := events.SubscribeOptions{ | ||||||
| 		Queue: uuid.New().String(), | 		Queue:   uuid.New().String(), | ||||||
|  | 		AutoAck: true, | ||||||
| 	} | 	} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  | 	// TODO RetryLimit | ||||||
| 
 | 
 | ||||||
| 	// setup the subscriber | 	// setup the subscriber | ||||||
| 	sub := &subscriber{ | 	sub := &subscriber{ | ||||||
| 		Channel: make(chan events.Event), | 		Channel:    make(chan events.Event), | ||||||
| 		Topic:   options.Topic, | 		Topic:      topic, | ||||||
| 		Queue:   options.Queue, | 		Queue:      options.Queue, | ||||||
|  | 		retryMap:   map[string]int{}, | ||||||
|  | 		autoAck:    true, | ||||||
|  | 		retryLimit: options.GetRetryLimit(), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !options.AutoAck { | ||||||
|  | 		if options.AckWait == 0 { | ||||||
|  | 			return nil, fmt.Errorf("invalid AckWait passed, should be positive integer") | ||||||
|  | 		} | ||||||
|  | 		sub.autoAck = options.AutoAck | ||||||
|  | 		sub.ackWait = options.AckWait | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// register the subscriber | 	// register the subscriber | ||||||
| @@ -124,16 +148,11 @@ func (m *mem) Subscribe(opts ...events.SubscribeOption) (<-chan events.Event, er | |||||||
| 	return sub.Channel, nil | 	return sub.Channel, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // lookupPreviousEvents finds events for a subscriber which occured before a given time and sends | // lookupPreviousEvents finds events for a subscriber which occurred before a given time and sends | ||||||
| // them into the subscribers channel | // them into the subscribers channel | ||||||
| func (m *mem) lookupPreviousEvents(sub *subscriber, startTime time.Time) { | func (m *mem) lookupPreviousEvents(sub *subscriber, startTime time.Time) { | ||||||
| 	var prefix string |  | ||||||
| 	if len(sub.Topic) > 0 { |  | ||||||
| 		prefix = sub.Topic + "/" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// lookup all events which match the topic (a blank topic will return all results) | 	// lookup all events which match the topic (a blank topic will return all results) | ||||||
| 	recs, err := m.store.Read(prefix, store.ReadPrefix()) | 	recs, err := m.store.Read(sub.Topic+"/", store.ReadPrefix()) | ||||||
| 	if err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 	if err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
| 		logger.Errorf("Error looking up previous events: %v", err) | 		logger.Errorf("Error looking up previous events: %v", err) | ||||||
| 		return | 		return | ||||||
| @@ -150,8 +169,7 @@ func (m *mem) lookupPreviousEvents(sub *subscriber, startTime time.Time) { | |||||||
| 		if ev.Timestamp.Unix() < startTime.Unix() { | 		if ev.Timestamp.Unix() < startTime.Unix() { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 		sendEvent(&ev, sub) | ||||||
| 		sub.Channel <- ev |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -173,9 +191,60 @@ func (m *mem) handleEvent(ev *events.Event) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// send the message to each channel async (since one channel might be blocked) | 	// send the message to each channel async (since one channel might be blocked) | ||||||
| 	for _, sub := range subs { | 	for _, sub := range filteredSubs { | ||||||
| 		go func(s *subscriber) { | 		sendEvent(ev, sub) | ||||||
| 			s.Channel <- *ev | 	} | ||||||
| 		}(sub) | } | ||||||
|  | 
 | ||||||
|  | func sendEvent(ev *events.Event, sub *subscriber) { | ||||||
|  | 	go func(s *subscriber) { | ||||||
|  | 		evCopy := *ev | ||||||
|  | 		if s.autoAck { | ||||||
|  | 			s.Channel <- evCopy | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		evCopy.SetAckFunc(ackFunc(s, evCopy)) | ||||||
|  | 		evCopy.SetNackFunc(nackFunc(s, evCopy)) | ||||||
|  | 		s.retryMap[evCopy.ID] = 0 | ||||||
|  | 		tick := time.NewTicker(s.ackWait) | ||||||
|  | 		defer tick.Stop() | ||||||
|  | 		for range tick.C { | ||||||
|  | 			s.Lock() | ||||||
|  | 			count, ok := s.retryMap[evCopy.ID] | ||||||
|  | 			s.Unlock() | ||||||
|  | 			if !ok { | ||||||
|  | 				// success | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if s.retryLimit > -1 && count > s.retryLimit { | ||||||
|  | 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
|  | 					logger.Errorf("Message retry limit reached, discarding: %v %d %d", evCopy.ID, count, s.retryLimit) | ||||||
|  | 				} | ||||||
|  | 				s.Lock() | ||||||
|  | 				delete(s.retryMap, evCopy.ID) | ||||||
|  | 				s.Unlock() | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			s.Channel <- evCopy | ||||||
|  | 			s.Lock() | ||||||
|  | 			s.retryMap[evCopy.ID] = count + 1 | ||||||
|  | 			s.Unlock() | ||||||
|  | 		} | ||||||
|  | 	}(sub) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ackFunc(s *subscriber, evCopy events.Event) func() error { | ||||||
|  | 	return func() error { | ||||||
|  | 		s.Lock() | ||||||
|  | 		delete(s.retryMap, evCopy.ID) | ||||||
|  | 		s.Unlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func nackFunc(s *subscriber, evCopy events.Event) func() error { | ||||||
|  | 	return func() error { | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
							
								
								
									
										1
									
								
								events/stream/memory/memory_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								events/stream/memory/memory_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | package memory | ||||||
| @@ -16,7 +16,6 @@ import ( | |||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	defaultClusterID = "micro" | 	defaultClusterID = "micro" | ||||||
| 	eventsTopic      = "events" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // NewStream returns an initialized nats stream or an error if the connection to the nats | // NewStream returns an initialized nats stream or an error if the connection to the nats | ||||||
| @@ -59,7 +58,7 @@ type stream struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Publish a message to a topic | // Publish a message to a topic | ||||||
| func (s *stream) Publish(topic string, opts ...events.PublishOption) error { | func (s *stream) Publish(topic string, msg interface{}, opts ...events.PublishOption) error { | ||||||
| 	// validate the topic | 	// validate the topic | ||||||
| 	if len(topic) == 0 { | 	if len(topic) == 0 { | ||||||
| 		return events.ErrMissingTopic | 		return events.ErrMissingTopic | ||||||
| @@ -75,10 +74,10 @@ func (s *stream) Publish(topic string, opts ...events.PublishOption) error { | |||||||
| 
 | 
 | ||||||
| 	// encode the message if it's not already encoded | 	// encode the message if it's not already encoded | ||||||
| 	var payload []byte | 	var payload []byte | ||||||
| 	if p, ok := options.Payload.([]byte); ok { | 	if p, ok := msg.([]byte); ok { | ||||||
| 		payload = p | 		payload = p | ||||||
| 	} else { | 	} else { | ||||||
| 		p, err := json.Marshal(options.Payload) | 		p, err := json.Marshal(msg) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return events.ErrEncodingMessage | 			return events.ErrEncodingMessage | ||||||
| 		} | 		} | ||||||
| @@ -100,11 +99,6 @@ func (s *stream) Publish(topic string, opts ...events.PublishOption) error { | |||||||
| 		return errors.Wrap(err, "Error encoding event") | 		return errors.Wrap(err, "Error encoding event") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// publish the event to the events channel |  | ||||||
| 	if _, err := s.conn.PublishAsync(eventsTopic, bytes, nil); err != nil { |  | ||||||
| 		return errors.Wrap(err, "Error publishing message to events") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// publish the event to the topic's channel | 	// publish the event to the topic's channel | ||||||
| 	if _, err := s.conn.PublishAsync(event.Topic, bytes, nil); err != nil { | 	if _, err := s.conn.PublishAsync(event.Topic, bytes, nil); err != nil { | ||||||
| 		return errors.Wrap(err, "Error publishing message to topic") | 		return errors.Wrap(err, "Error publishing message to topic") | ||||||
| @@ -114,11 +108,16 @@ func (s *stream) Publish(topic string, opts ...events.PublishOption) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Subscribe to a topic | // Subscribe to a topic | ||||||
| func (s *stream) Subscribe(opts ...events.SubscribeOption) (<-chan events.Event, error) { | 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 | 	// parse the options | ||||||
| 	options := events.SubscribeOptions{ | 	options := events.SubscribeOptions{ | ||||||
| 		Topic: eventsTopic, | 		Queue:   uuid.New().String(), | ||||||
| 		Queue: uuid.New().String(), | 		AutoAck: true, | ||||||
| 	} | 	} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| @@ -127,19 +126,43 @@ func (s *stream) Subscribe(opts ...events.SubscribeOption) (<-chan events.Event, | |||||||
| 	// setup the subscriber | 	// setup the subscriber | ||||||
| 	c := make(chan events.Event) | 	c := make(chan events.Event) | ||||||
| 	handleMsg := func(m *stan.Msg) { | 	handleMsg := func(m *stan.Msg) { | ||||||
|  | 		// poison message handling | ||||||
|  | 		if options.GetRetryLimit() > -1 && m.Redelivered && int(m.RedeliveryCount) > options.GetRetryLimit() { | ||||||
|  | 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
|  | 				logger.Errorf("Message retry limit reached, discarding: %v", m.Sequence) | ||||||
|  | 			} | ||||||
|  | 			m.Ack() // ignoring error | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		// decode the message | 		// decode the message | ||||||
| 		var evt events.Event | 		var evt events.Event | ||||||
| 		if err := json.Unmarshal(m.Data, &evt); err != nil { | 		if err := json.Unmarshal(m.Data, &evt); err != nil { | ||||||
| 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
| 				logger.Errorf("Error decoding message: %v", err) | 				logger.Errorf("Error decoding message: %v", err) | ||||||
| 			} | 			} | ||||||
| 			// not ackknowledging the message is the way to indicate an error occured | 			// not acknowledging the message is the way to indicate an error occurred | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if !options.AutoAck { | ||||||
|  | 			// set up the ack funcs | ||||||
|  | 			evt.SetAckFunc(func() error { | ||||||
|  | 				return m.Ack() | ||||||
|  | 			}) | ||||||
|  | 			evt.SetNackFunc(func() error { | ||||||
|  | 				// noop. not acknowledging the message is the way to indicate an error occurred | ||||||
|  | 				// we have to wait for the ack wait to kick in before the message is resent | ||||||
|  | 				return nil | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		// push onto the channel and wait for the consumer to take the event off before we acknowledge it. | 		// push onto the channel and wait for the consumer to take the event off before we acknowledge it. | ||||||
| 		c <- evt | 		c <- evt | ||||||
| 
 | 
 | ||||||
|  | 		if !options.AutoAck { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
| 		if err := m.Ack(); err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 		if err := m.Ack(); err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
| 			logger.Errorf("Error acknowledging message: %v", err) | 			logger.Errorf("Error acknowledging message: %v", err) | ||||||
| 		} | 		} | ||||||
| @@ -147,15 +170,18 @@ func (s *stream) Subscribe(opts ...events.SubscribeOption) (<-chan events.Event, | |||||||
| 
 | 
 | ||||||
| 	// setup the options | 	// setup the options | ||||||
| 	subOpts := []stan.SubscriptionOption{ | 	subOpts := []stan.SubscriptionOption{ | ||||||
| 		stan.DurableName(options.Topic), | 		stan.DurableName(topic), | ||||||
| 		stan.SetManualAckMode(), | 		stan.SetManualAckMode(), | ||||||
| 	} | 	} | ||||||
| 	if options.StartAtTime.Unix() > 0 { | 	if options.StartAtTime.Unix() > 0 { | ||||||
| 		stan.StartAtTime(options.StartAtTime) | 		subOpts = append(subOpts, stan.StartAtTime(options.StartAtTime)) | ||||||
|  | 	} | ||||||
|  | 	if options.AckWait > 0 { | ||||||
|  | 		subOpts = append(subOpts, stan.AckWait(options.AckWait)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// connect the subscriber | 	// connect the subscriber | ||||||
| 	_, err := s.conn.QueueSubscribe(options.Topic, options.Queue, handleMsg, subOpts...) | 	_, err := s.conn.QueueSubscribe(topic, options.Queue, handleMsg, subOpts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, errors.Wrap(err, "Error subscribing to topic") | 		return nil, errors.Wrap(err, "Error subscribing to topic") | ||||||
| 	} | 	} | ||||||
							
								
								
									
										3
									
								
								events/stream/nats/nats_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								events/stream/nats/nats_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | // +build nats | ||||||
|  |  | ||||||
|  | package nats | ||||||
							
								
								
									
										253
									
								
								events/stream/test/stream_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								events/stream/test/stream_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,253 @@ | |||||||
|  | // +build nats | ||||||
|  |  | ||||||
|  | package test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v3/events" | ||||||
|  | 	"github.com/micro/go-micro/v3/events/stream/memory" | ||||||
|  | 	"github.com/micro/go-micro/v3/events/stream/nats" | ||||||
|  |  | ||||||
|  | 	"github.com/google/uuid" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type testPayload struct { | ||||||
|  | 	Message string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type testCase struct { | ||||||
|  | 	str  events.Stream | ||||||
|  | 	name string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStream(t *testing.T) { | ||||||
|  | 	tcs := []testCase{} | ||||||
|  |  | ||||||
|  | 	// NATS specific setup | ||||||
|  | 	stream, err := nats.NewStream(nats.ClusterID("test-cluster")) | ||||||
|  | 	assert.Nilf(t, err, "NewStream should not return an error") | ||||||
|  | 	assert.NotNilf(t, stream, "NewStream should return a stream object") | ||||||
|  | 	tcs = append(tcs, testCase{str: stream, name: "nats"}) | ||||||
|  |  | ||||||
|  | 	stream, err = memory.NewStream() | ||||||
|  | 	assert.Nilf(t, err, "NewStream should not return an error") | ||||||
|  | 	assert.NotNilf(t, stream, "NewStream should return a stream object") | ||||||
|  | 	tcs = append(tcs, testCase{str: stream, name: "memory"}) | ||||||
|  |  | ||||||
|  | 	for _, tc := range tcs { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			runTestStream(t, tc.str) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func runTestStream(t *testing.T, stream events.Stream) { | ||||||
|  | 	// 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.Second * 1) | ||||||
|  |  | ||||||
|  | 			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() | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("AckingNacking", func(t *testing.T) { | ||||||
|  | 		ch, err := stream.Subscribe("foobarAck", events.WithAutoAck(false, 5*time.Second)) | ||||||
|  | 		assert.NoError(t, err, "Unexpected error subscribing") | ||||||
|  | 		assert.NoError(t, stream.Publish("foobarAck", map[string]string{"foo": "message 1"})) | ||||||
|  | 		assert.NoError(t, stream.Publish("foobarAck", map[string]string{"foo": "message 2"})) | ||||||
|  |  | ||||||
|  | 		ev := <-ch | ||||||
|  | 		ev.Ack() | ||||||
|  | 		ev = <-ch | ||||||
|  | 		nacked := ev.ID | ||||||
|  | 		ev.Nack() | ||||||
|  | 		select { | ||||||
|  | 		case ev = <-ch: | ||||||
|  | 			assert.Equal(t, ev.ID, nacked, "Nacked message should have been received again") | ||||||
|  | 			assert.NoError(t, ev.Ack()) | ||||||
|  | 		case <-time.After(7 * time.Second): | ||||||
|  | 			t.Fatalf("Timed out waiting for message to be put back on queue") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("Retries", func(t *testing.T) { | ||||||
|  | 		ch, err := stream.Subscribe("foobarRetries", events.WithAutoAck(false, 5*time.Second), events.WithRetryLimit(1)) | ||||||
|  | 		assert.NoError(t, err, "Unexpected error subscribing") | ||||||
|  | 		assert.NoError(t, stream.Publish("foobarRetries", map[string]string{"foo": "message 1"})) | ||||||
|  |  | ||||||
|  | 		ev := <-ch | ||||||
|  | 		id := ev.ID | ||||||
|  | 		ev.Nack() | ||||||
|  | 		ev = <-ch | ||||||
|  | 		assert.Equal(t, id, ev.ID, "Nacked message should have been received again") | ||||||
|  | 		ev.Nack() | ||||||
|  | 		select { | ||||||
|  | 		case ev = <-ch: | ||||||
|  | 			t.Fatalf("Unexpected event received") | ||||||
|  | 		case <-time.After(7 * time.Second): | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("InfiniteRetries", func(t *testing.T) { | ||||||
|  | 		ch, err := stream.Subscribe("foobarRetriesInf", events.WithAutoAck(false, 2*time.Second)) | ||||||
|  | 		assert.NoError(t, err, "Unexpected error subscribing") | ||||||
|  | 		assert.NoError(t, stream.Publish("foobarRetriesInf", map[string]string{"foo": "message 1"})) | ||||||
|  |  | ||||||
|  | 		count := 0 | ||||||
|  | 		id := "" | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case ev := <-ch: | ||||||
|  | 				if id != "" { | ||||||
|  | 					assert.Equal(t, id, ev.ID, "Nacked message should have been received again") | ||||||
|  | 				} | ||||||
|  | 				id = ev.ID | ||||||
|  | 			case <-time.After(3 * time.Second): | ||||||
|  | 				t.Fatalf("Unexpected event received") | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			count++ | ||||||
|  | 			if count == 11 { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("twoSubs", func(t *testing.T) { | ||||||
|  | 		ch1, err := stream.Subscribe("foobarTwoSubs1", events.WithAutoAck(false, 5*time.Second)) | ||||||
|  | 		assert.NoError(t, err, "Unexpected error subscribing to topic 1") | ||||||
|  | 		ch2, err := stream.Subscribe("foobarTwoSubs2", events.WithAutoAck(false, 5*time.Second)) | ||||||
|  | 		assert.NoError(t, err, "Unexpected error subscribing to topic 2") | ||||||
|  |  | ||||||
|  | 		assert.NoError(t, stream.Publish("foobarTwoSubs2", map[string]string{"foo": "message 1"})) | ||||||
|  | 		assert.NoError(t, stream.Publish("foobarTwoSubs1", map[string]string{"foo": "message 1"})) | ||||||
|  |  | ||||||
|  | 		wg := sync.WaitGroup{} | ||||||
|  | 		wg.Add(2) | ||||||
|  | 		go func() { | ||||||
|  | 			ev := <-ch1 | ||||||
|  | 			assert.Equal(t, "foobarTwoSubs1", ev.Topic, "Received message from unexpected topic") | ||||||
|  | 			wg.Done() | ||||||
|  | 		}() | ||||||
|  | 		go func() { | ||||||
|  | 			ev := <-ch2 | ||||||
|  | 			assert.Equal(t, "foobarTwoSubs2", ev.Topic, "Received message from unexpected topic") | ||||||
|  | 			wg.Done() | ||||||
|  | 		}() | ||||||
|  | 		wg.Wait() | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
								
							| @@ -19,7 +19,7 @@ require ( | |||||||
| 	github.com/dgrijalva/jwt-go v3.2.0+incompatible | 	github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||||
| 	github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1 | 	github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1 | ||||||
| 	github.com/evanphx/json-patch/v5 v5.0.0 | 	github.com/evanphx/json-patch/v5 v5.0.0 | ||||||
| 	github.com/fsnotify/fsnotify v1.4.7 | 	github.com/fsnotify/fsnotify v1.4.9 | ||||||
| 	github.com/fsouza/go-dockerclient v1.6.0 | 	github.com/fsouza/go-dockerclient v1.6.0 | ||||||
| 	github.com/ghodss/yaml v1.0.0 | 	github.com/ghodss/yaml v1.0.0 | ||||||
| 	github.com/go-acme/lego/v3 v3.4.0 | 	github.com/go-acme/lego/v3 v3.4.0 | ||||||
| @@ -41,14 +41,12 @@ require ( | |||||||
| 	github.com/kr/pretty v0.2.0 | 	github.com/kr/pretty v0.2.0 | ||||||
| 	github.com/kr/text v0.2.0 // indirect | 	github.com/kr/text v0.2.0 // indirect | ||||||
| 	github.com/lib/pq v1.7.0 | 	github.com/lib/pq v1.7.0 | ||||||
| 	github.com/lucas-clemente/quic-go v0.14.1 |  | ||||||
| 	github.com/miekg/dns v1.1.27 | 	github.com/miekg/dns v1.1.27 | ||||||
| 	github.com/mitchellh/hashstructure v1.0.0 | 	github.com/mitchellh/hashstructure v1.0.0 | ||||||
| 	github.com/nats-io/nats-streaming-server v0.18.0 // indirect | 	github.com/nats-io/nats-streaming-server v0.18.0 // indirect | ||||||
| 	github.com/nats-io/nats.go v1.10.0 | 	github.com/nats-io/nats.go v1.10.0 | ||||||
| 	github.com/nats-io/stan.go v0.7.0 | 	github.com/nats-io/stan.go v0.7.0 | ||||||
| 	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect | 	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect | ||||||
| 	github.com/onsi/ginkgo v1.12.0 // indirect |  | ||||||
| 	github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c | 	github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c | ||||||
| 	github.com/patrickmn/go-cache v2.1.0+incompatible | 	github.com/patrickmn/go-cache v2.1.0+incompatible | ||||||
| 	github.com/pkg/errors v0.9.1 | 	github.com/pkg/errors v0.9.1 | ||||||
| @@ -62,14 +60,14 @@ require ( | |||||||
| 	go.etcd.io/bbolt v1.3.5 | 	go.etcd.io/bbolt v1.3.5 | ||||||
| 	go.uber.org/zap v1.13.0 | 	go.uber.org/zap v1.13.0 | ||||||
| 	golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 | 	golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 | ||||||
| 	golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 | 	golang.org/x/net v0.0.0-20200707034311-ab3426394381 | ||||||
| 	golang.org/x/tools v0.0.0-20200117065230-39095c1d176c // indirect | 	golang.org/x/tools v0.0.0-20200117065230-39095c1d176c // indirect | ||||||
| 	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 | 	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 | ||||||
| 	google.golang.org/grpc v1.27.0 | 	google.golang.org/grpc v1.27.0 | ||||||
| 	google.golang.org/protobuf v1.25.0 | 	google.golang.org/protobuf v1.25.0 | ||||||
| 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect | 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect | ||||||
| 	gopkg.in/square/go-jose.v2 v2.4.1 // indirect | 	gopkg.in/square/go-jose.v2 v2.4.1 // indirect | ||||||
| 	gopkg.in/yaml.v2 v2.2.8 // indirect | 	gopkg.in/yaml.v2 v2.3.0 // indirect | ||||||
| 	sigs.k8s.io/yaml v1.1.0 // indirect | 	sigs.k8s.io/yaml v1.1.0 // indirect | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								go.sum
									
									
									
									
									
								
							| @@ -38,8 +38,6 @@ github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:i | |||||||
| github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= | 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= | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= | ||||||
| github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= | github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= | ||||||
| github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75 h1:3ILjVyslFbc4jl1w5TWuvvslFD/nDfR2H8tVaMVLrEY= |  | ||||||
| github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= |  | ||||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||||
| github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||||
| @@ -71,8 +69,6 @@ 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/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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= | ||||||
| github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | 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/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/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= | ||||||
| github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= | github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= | ||||||
| @@ -130,6 +126,8 @@ github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSY | |||||||
| github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= | ||||||
| github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | 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.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 h1:f7j+AX94143JL1H3TiqSMkM4EcLDI0De1qD4GGn3Hig= | ||||||
| github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= | 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 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= | ||||||
| @@ -171,7 +169,6 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb | |||||||
| github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= | 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.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | ||||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= |  | ||||||
| github.com/golang/protobuf v1.3.1/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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||||
| @@ -276,7 +273,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv | |||||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= | github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= | ||||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||||
| github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= | ||||||
| github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= |  | ||||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||||
| github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= | 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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||||||
| @@ -290,13 +286,6 @@ github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= | |||||||
| github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= | github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= | ||||||
| github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= | 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/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= | ||||||
| github.com/lucas-clemente/quic-go v0.14.1 h1:c1aKoBZKOPA+49q96B1wGkibyPP0AxYh45WuAoq+87E= |  | ||||||
| github.com/lucas-clemente/quic-go v0.14.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU= |  | ||||||
| github.com/marten-seemann/chacha20 v0.2.0 h1:f40vqzzx+3GdOmzQoItkLX5WLvHgPgyYqFFIO5Gh4hQ= |  | ||||||
| github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE= |  | ||||||
| github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= |  | ||||||
| github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks= |  | ||||||
| github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= |  | ||||||
| github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | 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.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-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= | ||||||
| @@ -306,7 +295,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 | |||||||
| github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | 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 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= | ||||||
| github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | ||||||
| github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= |  | ||||||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | 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/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 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= | ||||||
| @@ -346,14 +334,8 @@ github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2 | |||||||
| github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= | github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= | ||||||
| github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= | github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= | ||||||
| github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||||||
| github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= |  | ||||||
| github.com/onsi/ginkgo v1.7.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.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= |  | ||||||
| github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= |  | ||||||
| github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= |  | ||||||
| github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | ||||||
| github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= |  | ||||||
| github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= |  | ||||||
| github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= | 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 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= | ||||||
| github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= | github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= | ||||||
| @@ -385,21 +367,18 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP | |||||||
| github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= | 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 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.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= | ||||||
| github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= |  | ||||||
| github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= | ||||||
| github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U= | github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U= | ||||||
| github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= | github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= | ||||||
| github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | ||||||
| github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | ||||||
| github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= |  | ||||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | 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 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= | ||||||
| github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||||
| github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | ||||||
| github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | ||||||
| github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= |  | ||||||
| github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= | 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 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= | ||||||
| github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= | ||||||
| @@ -408,7 +387,6 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R | |||||||
| github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= | ||||||
| github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= | ||||||
| github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= |  | ||||||
| github.com/prometheus/procfs v0.0.5/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.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= | ||||||
| github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= | github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= | ||||||
| @@ -417,11 +395,9 @@ github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKc | |||||||
| github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= | 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/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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||||
| github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= |  | ||||||
| github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | 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/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= | ||||||
| github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | ||||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= |  | ||||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | ||||||
| github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | 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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= | ||||||
| @@ -436,7 +412,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ | |||||||
| github.com/stretchr/objx v0.1.1/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= | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= |  | ||||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | 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 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= | ||||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||||
| @@ -487,11 +462,11 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE | |||||||
| golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | 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-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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |  | ||||||
| golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/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-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 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= | ||||||
| golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| @@ -525,7 +500,6 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r | |||||||
| golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
| @@ -537,8 +511,8 @@ 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-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-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-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE= | golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= | ||||||
| golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | 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-20180821212333-d2e6202438be/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-20181106182150-f42d05182288/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| @@ -571,10 +545,9 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w | |||||||
| golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/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-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-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-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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/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-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| @@ -610,7 +583,6 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn | |||||||
| golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/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-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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||||
| golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY= |  | ||||||
| golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/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-20200117065230-39095c1d176c h1:FodBYPZKH5tAN2O60HlglMwXGAeV/4k+NKbli79M/2c= | golang.org/x/tools v0.0.0-20200117065230-39095c1d176c h1:FodBYPZKH5tAN2O60HlglMwXGAeV/4k+NKbli79M/2c= | ||||||
| golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||||
| @@ -631,7 +603,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 | |||||||
| google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= | 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/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-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-20190307195333-5fe7a883aa19/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-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||||
| google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||||
| @@ -639,7 +610,6 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn | |||||||
| google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | 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-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= | ||||||
| google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 h1:aQktFqmDE2yjveXJlVIfslDFmFnUXSqG0i6KRcJAeMc= |  | ||||||
| google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= | 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/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||||
| @@ -650,7 +620,6 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ | |||||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||||
| google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= |  | ||||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
| @@ -671,7 +640,6 @@ 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= | gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= | ||||||
| gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= | gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= | ||||||
| gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | ||||||
| gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= |  | ||||||
| gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | ||||||
| gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= | gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= | ||||||
| gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | ||||||
| @@ -680,11 +648,10 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD | |||||||
| gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | ||||||
| gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= |  | ||||||
| gopkg.in/yaml.v2 v2.2.4/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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= | ||||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 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 h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= | ||||||
| gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= | ||||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
|   | |||||||
							
								
								
									
										53
									
								
								metrics/logging/reporter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								metrics/logging/reporter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | package logging | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v3/logger" | ||||||
|  | 	"github.com/micro/go-micro/v3/metrics" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	defaultLoggingLevel = logger.TraceLevel | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Reporter is an implementation of metrics.Reporter: | ||||||
|  | type Reporter struct { | ||||||
|  | 	options metrics.Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New returns a configured logging reporter: | ||||||
|  | func New(opts ...metrics.Option) *Reporter { | ||||||
|  | 	logger.Logf(logger.InfoLevel, "Metrics/Logging - metrics will be logged (at %s level)", defaultLoggingLevel.String()) | ||||||
|  |  | ||||||
|  | 	return &Reporter{ | ||||||
|  | 		options: metrics.NewOptions(opts...), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Count implements the metrics.Reporter interface Count method: | ||||||
|  | func (r *Reporter) Count(metricName string, value int64, tags metrics.Tags) error { | ||||||
|  | 	logger.Logf(defaultLoggingLevel, "Count metric: (%s: %d) %s", metricName, value, tags) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Gauge implements the metrics.Reporter interface Gauge method: | ||||||
|  | func (r *Reporter) Gauge(metricName string, value float64, tags metrics.Tags) error { | ||||||
|  | 	logger.Logf(defaultLoggingLevel, "Gauge metric: (%s: %f) %s", metricName, value, tags) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Timing implements the metrics.Reporter interface Timing method: | ||||||
|  | func (r *Reporter) Timing(metricName string, value time.Duration, tags metrics.Tags) error { | ||||||
|  | 	logger.Logf(defaultLoggingLevel, "Timing metric: (%s: %s) %s", metricName, value.String(), tags) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // convertTags turns Tags into prometheus labels: | ||||||
|  | func convertTags(tags metrics.Tags) map[string]interface{} { | ||||||
|  | 	labels := make(map[string]interface{}) | ||||||
|  | 	for key, value := range tags { | ||||||
|  | 		labels[key] = value | ||||||
|  | 	} | ||||||
|  | 	return labels | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								metrics/logging/reporter_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								metrics/logging/reporter_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | package logging | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v3/metrics" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestLoggingReporter(t *testing.T) { | ||||||
|  |  | ||||||
|  | 	// Make a Reporter: | ||||||
|  | 	reporter := New(metrics.Path("/prometheus"), metrics.DefaultTags(map[string]string{"service": "prometheus-test"})) | ||||||
|  | 	assert.NotNil(t, reporter) | ||||||
|  | 	assert.Equal(t, "prometheus-test", reporter.options.DefaultTags["service"]) | ||||||
|  | 	assert.Equal(t, ":9000", reporter.options.Address) | ||||||
|  | 	assert.Equal(t, "/prometheus", reporter.options.Path) | ||||||
|  |  | ||||||
|  | 	// Check that our implementation is valid: | ||||||
|  | 	assert.Implements(t, new(metrics.Reporter), reporter) | ||||||
|  |  | ||||||
|  | 	// Test tag conversion: | ||||||
|  | 	tags := metrics.Tags{ | ||||||
|  | 		"tag1": "false", | ||||||
|  | 		"tag2": "true", | ||||||
|  | 	} | ||||||
|  | 	convertedTags := convertTags(tags) | ||||||
|  | 	assert.Equal(t, "false", convertedTags["tag1"]) | ||||||
|  | 	assert.Equal(t, "true", convertedTags["tag2"]) | ||||||
|  |  | ||||||
|  | 	// Test submitting metrics through the interface methods: | ||||||
|  | 	assert.NoError(t, reporter.Count("test.counter.1", 6, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Count("test.counter.2", 19, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Count("test.counter.1", 5, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Gauge("test.gauge.1", 99, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Gauge("test.gauge.2", 55, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Gauge("test.gauge.1", 98, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Timing("test.timing.1", time.Second, tags)) | ||||||
|  | 	assert.NoError(t, reporter.Timing("test.timing.2", time.Minute, tags)) | ||||||
|  | } | ||||||
| @@ -3,7 +3,6 @@ package noop | |||||||
| import ( | import ( | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	log "github.com/micro/go-micro/v3/logger" |  | ||||||
| 	"github.com/micro/go-micro/v3/metrics" | 	"github.com/micro/go-micro/v3/metrics" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -14,8 +13,6 @@ type Reporter struct { | |||||||
|  |  | ||||||
| // New returns a configured noop reporter: | // New returns a configured noop reporter: | ||||||
| func New(opts ...metrics.Option) *Reporter { | func New(opts ...metrics.Option) *Reporter { | ||||||
| 	log.Info("Metrics/NoOp - not doing anything") |  | ||||||
|  |  | ||||||
| 	return &Reporter{ | 	return &Reporter{ | ||||||
| 		options: metrics.NewOptions(opts...), | 		options: metrics.NewOptions(opts...), | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ var ( | |||||||
| 	defaultPrometheusListenAddress = ":9000" | 	defaultPrometheusListenAddress = ":9000" | ||||||
| 	// This is the endpoint where the Prometheus metrics will be made available ("/metrics" is the default with Prometheus): | 	// This is the endpoint where the Prometheus metrics will be made available ("/metrics" is the default with Prometheus): | ||||||
| 	defaultPath = "/metrics" | 	defaultPath = "/metrics" | ||||||
| 	// timingObjectives is the default spread of stats we maintain for timings / histograms: | 	// defaultPercentiles is the default spread of percentiles/quantiles we maintain for timings / histogram metrics: | ||||||
| 	defaultTimingObjectives = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0} | 	defaultPercentiles = []float64{0, 0.5, 0.75, 0.90, 0.95, 0.98, 0.99, 1} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Option powers the configuration for metrics implementations: | // Option powers the configuration for metrics implementations: | ||||||
| @@ -14,19 +14,19 @@ type Option func(*Options) | |||||||
|  |  | ||||||
| // Options for metrics implementations: | // Options for metrics implementations: | ||||||
| type Options struct { | type Options struct { | ||||||
| 	Address          string | 	Address     string | ||||||
| 	Path             string | 	DefaultTags Tags | ||||||
| 	DefaultTags      Tags | 	Path        string | ||||||
| 	TimingObjectives map[float64]float64 | 	Percentiles []float64 | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewOptions prepares a set of options: | // NewOptions prepares a set of options: | ||||||
| func NewOptions(opt ...Option) Options { | func NewOptions(opt ...Option) Options { | ||||||
| 	opts := Options{ | 	opts := Options{ | ||||||
| 		Address:          defaultPrometheusListenAddress, | 		Address:     defaultPrometheusListenAddress, | ||||||
| 		DefaultTags:      make(Tags), | 		DefaultTags: make(Tags), | ||||||
| 		Path:             defaultPath, | 		Path:        defaultPath, | ||||||
| 		TimingObjectives: defaultTimingObjectives, | 		Percentiles: defaultPercentiles, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range opt { | 	for _, o := range opt { | ||||||
| @@ -57,9 +57,9 @@ func DefaultTags(value Tags) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // TimingObjectives defines the desired spread of statistics for histogram / timing metrics: | // Percentiles defines the desired spread of statistics for histogram / timing metrics: | ||||||
| func TimingObjectives(value map[float64]float64) Option { | func Percentiles(value []float64) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.TimingObjectives = value | 		o.Percentiles = value | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,10 +9,16 @@ import ( | |||||||
| func TestOptions(t *testing.T) { | func TestOptions(t *testing.T) { | ||||||
|  |  | ||||||
| 	// Make some new options: | 	// Make some new options: | ||||||
| 	options := NewOptions(Path("/prometheus"), DefaultTags(map[string]string{"service": "prometheus-test"})) | 	options := NewOptions( | ||||||
|  | 		Address(":9999"), | ||||||
|  | 		DefaultTags(map[string]string{"service": "prometheus-test"}), | ||||||
|  | 		Path("/prometheus"), | ||||||
|  | 		Percentiles([]float64{0.11, 0.22, 0.33}), | ||||||
|  | 	) | ||||||
|  |  | ||||||
| 	// Check that the defaults and overrides were accepted: | 	// Check that the defaults and overrides were accepted: | ||||||
| 	assert.Equal(t, ":9000", options.Address) | 	assert.Equal(t, ":9999", options.Address) | ||||||
| 	assert.Equal(t, "/prometheus", options.Path) |  | ||||||
| 	assert.Equal(t, "prometheus-test", options.DefaultTags["service"]) | 	assert.Equal(t, "prometheus-test", options.DefaultTags["service"]) | ||||||
|  | 	assert.Equal(t, "/prometheus", options.Path) | ||||||
|  | 	assert.Equal(t, []float64{0.11, 0.22, 0.33}, options.Percentiles) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,13 +19,22 @@ type metricFamily struct { | |||||||
|  |  | ||||||
| // newMetricFamily returns a new metricFamily (useful in case we want to change the structure later): | // newMetricFamily returns a new metricFamily (useful in case we want to change the structure later): | ||||||
| func (r *Reporter) newMetricFamily() metricFamily { | func (r *Reporter) newMetricFamily() metricFamily { | ||||||
|  |  | ||||||
|  | 	// Take quantile thresholds from our pre-defined list: | ||||||
|  | 	timingObjectives := make(map[float64]float64) | ||||||
|  | 	for _, percentile := range r.options.Percentiles { | ||||||
|  | 		if quantileThreshold, ok := quantileThresholds[percentile]; ok { | ||||||
|  | 			timingObjectives[percentile] = quantileThreshold | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return metricFamily{ | 	return metricFamily{ | ||||||
| 		counters:           make(map[string]*prometheus.CounterVec), | 		counters:           make(map[string]*prometheus.CounterVec), | ||||||
| 		gauges:             make(map[string]*prometheus.GaugeVec), | 		gauges:             make(map[string]*prometheus.GaugeVec), | ||||||
| 		timings:            make(map[string]*prometheus.SummaryVec), | 		timings:            make(map[string]*prometheus.SummaryVec), | ||||||
| 		defaultLabels:      r.convertTags(r.options.DefaultTags), | 		defaultLabels:      r.convertTags(r.options.DefaultTags), | ||||||
| 		prometheusRegistry: r.prometheusRegistry, | 		prometheusRegistry: r.prometheusRegistry, | ||||||
| 		timingObjectives:   r.options.TimingObjectives, | 		timingObjectives:   timingObjectives, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,12 @@ import ( | |||||||
| 	"github.com/prometheus/client_golang/prometheus/promhttp" | 	"github.com/prometheus/client_golang/prometheus/promhttp" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// quantileThresholds maps quantiles / percentiles to error thresholds (required by the Prometheus client). | ||||||
|  | 	// Must be from our pre-defined set [0.0, 0.5, 0.75, 0.90, 0.95, 0.98, 0.99, 1]: | ||||||
|  | 	quantileThresholds = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0} | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Reporter is an implementation of metrics.Reporter: | // Reporter is an implementation of metrics.Reporter: | ||||||
| type Reporter struct { | type Reporter struct { | ||||||
| 	options            metrics.Options | 	options            metrics.Options | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package prometheus | package prometheus | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -12,11 +14,11 @@ import ( | |||||||
| func TestPrometheusReporter(t *testing.T) { | func TestPrometheusReporter(t *testing.T) { | ||||||
|  |  | ||||||
| 	// Make a Reporter: | 	// Make a Reporter: | ||||||
| 	reporter, err := New(metrics.Path("/prometheus"), metrics.DefaultTags(map[string]string{"service": "prometheus-test"})) | 	reporter, err := New(metrics.Address(":9999"), metrics.Path("/prometheus"), metrics.DefaultTags(map[string]string{"service": "prometheus-test"})) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NotNil(t, reporter) | 	assert.NotNil(t, reporter) | ||||||
| 	assert.Equal(t, "prometheus-test", reporter.options.DefaultTags["service"]) | 	assert.Equal(t, "prometheus-test", reporter.options.DefaultTags["service"]) | ||||||
| 	assert.Equal(t, ":9000", reporter.options.Address) | 	assert.Equal(t, ":9999", reporter.options.Address) | ||||||
| 	assert.Equal(t, "/prometheus", reporter.options.Path) | 	assert.Equal(t, "/prometheus", reporter.options.Path) | ||||||
|  |  | ||||||
| 	// Check that our implementation is valid: | 	// Check that our implementation is valid: | ||||||
| @@ -69,5 +71,19 @@ func TestPrometheusReporter(t *testing.T) { | |||||||
| 	assert.Len(t, reporter.metrics.timings, 2) | 	assert.Len(t, reporter.metrics.timings, 2) | ||||||
|  |  | ||||||
| 	// Test reading back the metrics: | 	// Test reading back the metrics: | ||||||
| 	// This could be done by hitting the /metrics endpoint | 	rsp, err := http.Get("http://localhost:9999/prometheus") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, http.StatusOK, rsp.StatusCode) | ||||||
|  |  | ||||||
|  | 	// Read the response body and check for our metric: | ||||||
|  | 	bodyBytes, err := ioutil.ReadAll(rsp.Body) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	// Check for appropriately aggregated metrics: | ||||||
|  | 	assert.Contains(t, string(bodyBytes), `test_counter_1{service="prometheus-test",tag1="false",tag2="true"} 11`) | ||||||
|  | 	assert.Contains(t, string(bodyBytes), `test_counter_2{service="prometheus-test",tag1="false",tag2="true"} 19`) | ||||||
|  | 	assert.Contains(t, string(bodyBytes), `test_gauge_1{service="prometheus-test",tag1="false",tag2="true"} 98`) | ||||||
|  | 	assert.Contains(t, string(bodyBytes), `test_gauge_2{service="prometheus-test",tag1="false",tag2="true"} 55`) | ||||||
|  | 	assert.Contains(t, string(bodyBytes), `test_timing_1{service="prometheus-test",tag1="false",tag2="true",quantile="0"} 1`) | ||||||
|  | 	assert.Contains(t, string(bodyBytes), `test_timing_2{service="prometheus-test",tag1="false",tag2="true",quantile="0"} 60`) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,8 +34,6 @@ type Entity interface { | |||||||
| 	Value() interface{} | 	Value() interface{} | ||||||
| 	// Attributes of the entity | 	// Attributes of the entity | ||||||
| 	Attributes() map[string]interface{} | 	Attributes() map[string]interface{} | ||||||
| 	// Read a value as a concrete type |  | ||||||
| 	Read(v interface{}) error |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type Options struct { | type Options struct { | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								model/sql/entity.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								model/sql/entity.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | package sql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/google/uuid" | ||||||
|  | 	"github.com/micro/go-micro/v3/codec" | ||||||
|  | 	"github.com/micro/go-micro/v3/model" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type sqlEntity struct { | ||||||
|  | 	id         string | ||||||
|  | 	name       string | ||||||
|  | 	value      interface{} | ||||||
|  | 	codec      codec.Marshaler | ||||||
|  | 	attributes map[string]interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlEntity) Attributes() map[string]interface{} { | ||||||
|  | 	return m.attributes | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlEntity) Id() string { | ||||||
|  | 	return m.id | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlEntity) Name() string { | ||||||
|  | 	return m.name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlEntity) Value() interface{} { | ||||||
|  | 	return m.value | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newEntity(name string, value interface{}, codec codec.Marshaler) model.Entity { | ||||||
|  | 	return &sqlEntity{ | ||||||
|  | 		id:         uuid.New().String(), | ||||||
|  | 		name:       name, | ||||||
|  | 		value:      value, | ||||||
|  | 		codec:      codec, | ||||||
|  | 		attributes: make(map[string]interface{}), | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								model/sql/sql.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								model/sql/sql.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | // Package sql is the micro data model implementation | ||||||
|  | package sql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/micro/go-micro/v3/codec/json" | ||||||
|  | 	"github.com/micro/go-micro/v3/model" | ||||||
|  | 	"github.com/micro/go-micro/v3/store" | ||||||
|  | 	"github.com/micro/go-micro/v3/store/memory" | ||||||
|  | 	memsync "github.com/micro/go-micro/v3/sync/memory" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type sqlModel struct { | ||||||
|  | 	options model.Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlModel) Init(opts ...model.Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&m.options) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlModel) NewEntity(name string, value interface{}) model.Entity { | ||||||
|  | 	// TODO: potentially pluralise name for tables | ||||||
|  | 	return newEntity(name, value, m.options.Codec) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *sqlModel) 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 *sqlModel) 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 *sqlModel) 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 *sqlModel) 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 *sqlModel) String() string { | ||||||
|  | 	return "sql" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewModel(opts ...model.Option) model.Model { | ||||||
|  | 	options := model.Options{ | ||||||
|  | 		Codec: new(json.Marshaler), | ||||||
|  | 		Sync:  memsync.NewSync(), | ||||||
|  | 		Store: memory.NewStore(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &sqlModel{ | ||||||
|  | 		options: options, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -16,16 +16,16 @@ import ( | |||||||
| 	"github.com/micro/go-micro/v3/logger" | 	"github.com/micro/go-micro/v3/logger" | ||||||
| 	"github.com/micro/go-micro/v3/network" | 	"github.com/micro/go-micro/v3/network" | ||||||
| 	pb "github.com/micro/go-micro/v3/network/mucp/proto" | 	pb "github.com/micro/go-micro/v3/network/mucp/proto" | ||||||
|  | 	"github.com/micro/go-micro/v3/network/resolver/dns" | ||||||
|  | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
|  | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
|  | 	bun "github.com/micro/go-micro/v3/network/tunnel/broker" | ||||||
|  | 	tun "github.com/micro/go-micro/v3/network/tunnel/transport" | ||||||
| 	"github.com/micro/go-micro/v3/proxy" | 	"github.com/micro/go-micro/v3/proxy" | ||||||
| 	"github.com/micro/go-micro/v3/registry/noop" | 	"github.com/micro/go-micro/v3/registry/noop" | ||||||
| 	"github.com/micro/go-micro/v3/resolver/dns" |  | ||||||
| 	"github.com/micro/go-micro/v3/router" | 	"github.com/micro/go-micro/v3/router" | ||||||
| 	"github.com/micro/go-micro/v3/server" | 	"github.com/micro/go-micro/v3/server" | ||||||
| 	smucp "github.com/micro/go-micro/v3/server/mucp" | 	smucp "github.com/micro/go-micro/v3/server/mucp" | ||||||
| 	"github.com/micro/go-micro/v3/transport" |  | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" |  | ||||||
| 	bun "github.com/micro/go-micro/v3/tunnel/broker" |  | ||||||
| 	tun "github.com/micro/go-micro/v3/tunnel/transport" |  | ||||||
| 	"github.com/micro/go-micro/v3/util/backoff" | 	"github.com/micro/go-micro/v3/util/backoff" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -961,12 +961,11 @@ func (n *mucpNetwork) processNetChan(listener tunnel.Listener) { | |||||||
| 						route.Metric = d | 						route.Metric = d | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					q := []router.QueryOption{ | 					q := []router.LookupOption{ | ||||||
| 						router.QueryService(route.Service), | 						router.LookupLink(route.Link), | ||||||
| 						router.QueryLink(route.Link), |  | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					routes, err := n.router.Table().Query(q...) | 					routes, err := n.router.Lookup(route.Service, q...) | ||||||
| 					if err != nil && err != router.ErrRouteNotFound { | 					if err != nil && err != router.ErrRouteNotFound { | ||||||
| 						if logger.V(logger.DebugLevel, logger.DefaultLogger) { | 						if logger.V(logger.DebugLevel, logger.DefaultLogger) { | ||||||
| 							logger.Debugf("Network node %s failed listing best routes for %s: %v", n.id, route.Service, err) | 							logger.Debugf("Network node %s failed listing best routes for %s: %v", n.id, route.Service, err) | ||||||
| @@ -1079,16 +1078,15 @@ func (n *mucpNetwork) processNetChan(listener tunnel.Listener) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // pruneRoutes prunes routes return by given query | // pruneRoutes prunes routes return by given query | ||||||
| func (n *mucpNetwork) pruneRoutes(q ...router.QueryOption) error { | func (n *mucpNetwork) pruneRoutes(q ...router.LookupOption) error { | ||||||
| 	routes, err := n.router.Table().Query(q...) | 	routes, err := n.router.Table().Read() | ||||||
| 	if err != nil && err != router.ErrRouteNotFound { | 	if err != nil && err != router.ErrRouteNotFound { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, route := range routes { | 	// filter and delete the routes in question | ||||||
| 		if err := n.router.Table().Delete(route); err != nil && err != router.ErrRouteNotFound { | 	for _, route := range router.Filter(routes, router.NewLookup(q...)) { | ||||||
| 			return err | 		n.router.Table().Delete(route) | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| @@ -1097,18 +1095,18 @@ func (n *mucpNetwork) pruneRoutes(q ...router.QueryOption) error { | |||||||
| // pruneNodeRoutes prunes routes that were either originated by or routable via given node | // pruneNodeRoutes prunes routes that were either originated by or routable via given node | ||||||
| func (n *mucpNetwork) prunePeerRoutes(peer *node) error { | func (n *mucpNetwork) prunePeerRoutes(peer *node) error { | ||||||
| 	// lookup all routes originated by router | 	// lookup all routes originated by router | ||||||
| 	q := []router.QueryOption{ | 	q := []router.LookupOption{ | ||||||
| 		router.QueryRouter(peer.id), | 		router.LookupRouter(peer.id), | ||||||
| 		router.QueryLink("*"), | 		router.LookupLink("*"), | ||||||
| 	} | 	} | ||||||
| 	if err := n.pruneRoutes(q...); err != nil { | 	if err := n.pruneRoutes(q...); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// lookup all routes routable via gw | 	// lookup all routes routable via gw | ||||||
| 	q = []router.QueryOption{ | 	q = []router.LookupOption{ | ||||||
| 		router.QueryGateway(peer.address), | 		router.LookupGateway(peer.address), | ||||||
| 		router.QueryLink("*"), | 		router.LookupLink("*"), | ||||||
| 	} | 	} | ||||||
| 	if err := n.pruneRoutes(q...); err != nil { | 	if err := n.pruneRoutes(q...); err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -1260,7 +1258,7 @@ func (n *mucpNetwork) manage() { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// get a list of all routes | 			// get a list of all routes | ||||||
| 			routes, err := n.options.Router.Table().List() | 			routes, err := n.options.Router.Table().Read() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				if logger.V(logger.DebugLevel, logger.DefaultLogger) { | 				if logger.V(logger.DebugLevel, logger.DefaultLogger) { | ||||||
| 					logger.Debugf("Network failed listing routes when pruning peers: %v", err) | 					logger.Debugf("Network failed listing routes when pruning peers: %v", err) | ||||||
| @@ -1291,7 +1289,7 @@ func (n *mucpNetwork) manage() { | |||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				// otherwise delete all the routes originated by it | 				// otherwise delete all the routes originated by it | ||||||
| 				if err := n.pruneRoutes(router.QueryRouter(route.Router)); err != nil { | 				if err := n.pruneRoutes(router.LookupRouter(route.Router)); err != nil { | ||||||
| 					if logger.V(logger.DebugLevel, logger.DefaultLogger) { | 					if logger.V(logger.DebugLevel, logger.DefaultLogger) { | ||||||
| 						logger.Debugf("Network failed deleting routes by %s: %v", route.Router, err) | 						logger.Debugf("Network failed deleting routes by %s: %v", route.Router, err) | ||||||
| 					} | 					} | ||||||
| @@ -1346,7 +1344,7 @@ func (n *mucpNetwork) manage() { | |||||||
| // based on the advertisement strategy encoded in protobuf | // based on the advertisement strategy encoded in protobuf | ||||||
| // It returns error if the routes failed to be retrieved from the routing table | // It returns error if the routes failed to be retrieved from the routing table | ||||||
| func (n *mucpNetwork) getProtoRoutes() ([]*pb.Route, error) { | func (n *mucpNetwork) getProtoRoutes() ([]*pb.Route, error) { | ||||||
| 	routes, err := n.router.Table().List() | 	routes, err := n.router.Table().Read() | ||||||
| 	if err != nil && err != router.ErrRouteNotFound { | 	if err != nil && err != router.ErrRouteNotFound { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -2,12 +2,12 @@ package network | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
|  | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
|  | 	tmucp "github.com/micro/go-micro/v3/network/tunnel/mucp" | ||||||
| 	"github.com/micro/go-micro/v3/proxy" | 	"github.com/micro/go-micro/v3/proxy" | ||||||
| 	"github.com/micro/go-micro/v3/proxy/mucp" | 	"github.com/micro/go-micro/v3/proxy/mucp" | ||||||
| 	"github.com/micro/go-micro/v3/router" | 	"github.com/micro/go-micro/v3/router" | ||||||
| 	regRouter "github.com/micro/go-micro/v3/router/registry" | 	regRouter "github.com/micro/go-micro/v3/router/registry" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" |  | ||||||
| 	tmucp "github.com/micro/go-micro/v3/tunnel/mucp" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"net" | 	"net" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/resolver" | 	"github.com/micro/go-micro/v3/network/resolver" | ||||||
| 	"github.com/miekg/dns" | 	"github.com/miekg/dns" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net" | 	"net" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/resolver" | 	"github.com/micro/go-micro/v3/network/resolver" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Resolver is a DNS network resolve | // Resolver is a DNS network resolve | ||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/resolver" | 	"github.com/micro/go-micro/v3/network/resolver" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Resolver is a HTTP network resolver | // Resolver is a HTTP network resolver | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| package noop | package noop | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/micro/go-micro/v3/resolver" | 	"github.com/micro/go-micro/v3/network/resolver" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Resolver struct{} | type Resolver struct{} | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| package registry | package registry | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/micro/go-micro/v3/resolver" | 	"github.com/micro/go-micro/v3/network/resolver" | ||||||
| 	"github.com/micro/go-micro/v3/registry" | 	"github.com/micro/go-micro/v3/registry" | ||||||
| 	"github.com/micro/go-micro/v3/registry/mdns" | 	"github.com/micro/go-micro/v3/registry/mdns" | ||||||
| ) | ) | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| package static | package static | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/micro/go-micro/v3/resolver" | 	"github.com/micro/go-micro/v3/network/resolver" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Resolver returns a static list of nodes. In the event the node list | // Resolver returns a static list of nodes. In the event the node list | ||||||
| @@ -6,7 +6,7 @@ import ( | |||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
| 	"net" | 	"net" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	maddr "github.com/micro/go-micro/v3/util/addr" | 	maddr "github.com/micro/go-micro/v3/util/addr" | ||||||
| 	mnet "github.com/micro/go-micro/v3/util/net" | 	mnet "github.com/micro/go-micro/v3/util/net" | ||||||
| 	mls "github.com/micro/go-micro/v3/util/tls" | 	mls "github.com/micro/go-micro/v3/util/tls" | ||||||
| @@ -14,7 +14,7 @@ import ( | |||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
| 	"google.golang.org/grpc/credentials" | 	"google.golang.org/grpc/credentials" | ||||||
| 
 | 
 | ||||||
| 	pb "github.com/micro/go-micro/v3/transport/grpc/proto" | 	pb "github.com/micro/go-micro/v3/network/transport/grpc/proto" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type grpcTransport struct { | type grpcTransport struct { | ||||||
| @@ -4,7 +4,7 @@ import ( | |||||||
| 	"net" | 	"net" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func expectedPort(t *testing.T, expected string, lsn transport.Listener) { | func expectedPort(t *testing.T, expected string, lsn transport.Listener) { | ||||||
| @@ -5,8 +5,8 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/errors" | 	"github.com/micro/go-micro/v3/errors" | ||||||
| 	"github.com/micro/go-micro/v3/logger" | 	"github.com/micro/go-micro/v3/logger" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	pb "github.com/micro/go-micro/v3/transport/grpc/proto" | 	pb "github.com/micro/go-micro/v3/network/transport/grpc/proto" | ||||||
| 	"google.golang.org/grpc/peer" | 	"google.golang.org/grpc/peer" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| package grpc | package grpc | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	pb "github.com/micro/go-micro/v3/transport/grpc/proto" | 	pb "github.com/micro/go-micro/v3/network/transport/grpc/proto" | ||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	maddr "github.com/micro/go-micro/v3/util/addr" | 	maddr "github.com/micro/go-micro/v3/util/addr" | ||||||
| 	"github.com/micro/go-micro/v3/util/buf" | 	"github.com/micro/go-micro/v3/util/buf" | ||||||
| 	mnet "github.com/micro/go-micro/v3/util/net" | 	mnet "github.com/micro/go-micro/v3/util/net" | ||||||
| @@ -4,7 +4,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func call(b *testing.B, c int) { | func call(b *testing.B, c int) { | ||||||
| @@ -6,7 +6,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func expectedPort(t *testing.T, expected string, lsn transport.Listener) { | func expectedPort(t *testing.T, expected string, lsn transport.Listener) { | ||||||
| @@ -4,7 +4,7 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Handle registers the handler for the given pattern. | // Handle registers the handler for the given pattern. | ||||||
| @@ -10,7 +10,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	maddr "github.com/micro/go-micro/v3/util/addr" | 	maddr "github.com/micro/go-micro/v3/util/addr" | ||||||
| 	mnet "github.com/micro/go-micro/v3/util/net" | 	mnet "github.com/micro/go-micro/v3/util/net" | ||||||
| ) | ) | ||||||
| @@ -4,7 +4,7 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMemoryTransport(t *testing.T) { | func TestMemoryTransport(t *testing.T) { | ||||||
| @@ -5,9 +5,9 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/broker" | 	"github.com/micro/go-micro/v3/broker" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel/mucp" | 	"github.com/micro/go-micro/v3/network/tunnel/mucp" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunBroker struct { | type tunBroker struct { | ||||||
| @@ -6,7 +6,7 @@ import ( | |||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| 	"crypto/sha256" | 	"crypto/sha256" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| 	"github.com/oxtoacart/bpool" | 	"github.com/oxtoacart/bpool" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/micro/go-micro/v3/logger" | 	"github.com/micro/go-micro/v3/logger" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type link struct { | type link struct { | ||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/logger" | 	"github.com/micro/go-micro/v3/logger" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunListener struct { | type tunListener struct { | ||||||
| @@ -9,8 +9,8 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/micro/go-micro/v3/logger" | 	"github.com/micro/go-micro/v3/logger" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| @@ -6,8 +6,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func testBrokenTunAccept(t *testing.T, tun tunnel.Tunnel, wait chan bool, wg *sync.WaitGroup) { | func testBrokenTunAccept(t *testing.T, tun tunnel.Tunnel, wait chan bool, wg *sync.WaitGroup) { | ||||||
| @@ -8,8 +8,8 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/logger" | 	"github.com/micro/go-micro/v3/logger" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // session is our pseudo session for transport.Socket | // session is our pseudo session for transport.Socket | ||||||
| @@ -4,8 +4,8 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/transport/quic" | 	"github.com/micro/go-micro/v3/network/transport/grpc" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| @@ -140,6 +140,6 @@ func DefaultOptions() Options { | |||||||
| 		Id:        uuid.New().String(), | 		Id:        uuid.New().String(), | ||||||
| 		Address:   DefaultAddress, | 		Address:   DefaultAddress, | ||||||
| 		Token:     DefaultToken, | 		Token:     DefaultToken, | ||||||
| 		Transport: quic.NewTransport(), | 		Transport: grpc.NewTransport(), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| package transport | package transport | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunListener struct { | type tunListener struct { | ||||||
| @@ -4,9 +4,9 @@ package transport | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel" | 	"github.com/micro/go-micro/v3/network/tunnel" | ||||||
| 	"github.com/micro/go-micro/v3/tunnel/mucp" | 	"github.com/micro/go-micro/v3/network/tunnel/mucp" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunTransport struct { | type tunTransport struct { | ||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/micro/go-micro/v3/transport" | 	"github.com/micro/go-micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @@ -94,10 +94,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server | |||||||
| 		logger.Tracef("Proxy received request for %s %s", service, endpoint) | 		logger.Tracef("Proxy received request for %s %s", service, endpoint) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// no retries with the proxy | 	var opts []client.CallOption | ||||||
| 	opts := []client.CallOption{ |  | ||||||
| 		client.WithRetries(0), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// call a specific backend | 	// call a specific backend | ||||||
| 	if len(p.Endpoint) > 0 { | 	if len(p.Endpoint) > 0 { | ||||||
|   | |||||||
| @@ -208,7 +208,7 @@ func (p *Proxy) getRoute(ctx context.Context, service string) ([]router.Route, e | |||||||
|  |  | ||||||
| func (p *Proxy) cacheRoutes(service string) ([]router.Route, error) { | func (p *Proxy) cacheRoutes(service string) ([]router.Route, error) { | ||||||
| 	// lookup the routes in the router | 	// lookup the routes in the router | ||||||
| 	results, err := p.Router.Lookup(router.QueryService(service), router.QueryNetwork("*")) | 	results, err := p.Router.Lookup(service, router.LookupNetwork("*")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		// assumption that we're ok with stale routes | 		// assumption that we're ok with stale routes | ||||||
| 		logger.Debugf("Failed to lookup route for %s: %v", service, err) | 		logger.Debugf("Failed to lookup route for %s: %v", service, err) | ||||||
|   | |||||||
| @@ -17,19 +17,17 @@ func NewRouter(opts ...router.Option) router.Router { | |||||||
| 	if len(options.Network) == 0 { | 	if len(options.Network) == 0 { | ||||||
| 		options.Network = "micro" | 		options.Network = "micro" | ||||||
| 	} | 	} | ||||||
| 	return &dns{options, &table{options}} | 	return &dns{options} | ||||||
| } | } | ||||||
|  |  | ||||||
| type dns struct { | type dns struct { | ||||||
| 	options router.Options | 	options router.Options | ||||||
| 	table   *table |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (d *dns) Init(opts ...router.Option) error { | func (d *dns) Init(opts ...router.Option) error { | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&d.options) | 		o(&d.options) | ||||||
| 	} | 	} | ||||||
| 	d.table.options = d.options |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -38,50 +36,16 @@ func (d *dns) Options() router.Options { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (d *dns) Table() router.Table { | func (d *dns) Table() router.Table { | ||||||
| 	return d.table | 	return nil | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *dns) Lookup(opts ...router.QueryOption) ([]router.Route, error) { |  | ||||||
| 	return d.table.Query(opts...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *dns) Watch(opts ...router.WatchOption) (router.Watcher, error) { |  | ||||||
| 	return nil, nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (d *dns) Close() error { | func (d *dns) Close() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (d *dns) String() string { | func (d *dns) Lookup(service string, opts ...router.LookupOption) ([]router.Route, error) { | ||||||
| 	return "dns" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type table struct { |  | ||||||
| 	options router.Options |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Create(router.Route) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Delete(router.Route) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Update(router.Route) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) List() ([]router.Route, error) { |  | ||||||
| 	return nil, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Query(opts ...router.QueryOption) ([]router.Route, error) { |  | ||||||
| 	options := router.NewQuery(opts...) |  | ||||||
|  |  | ||||||
| 	// check to see if we have the port provided in the service, e.g. go-micro-srv-foo:8000 | 	// check to see if we have the port provided in the service, e.g. go-micro-srv-foo:8000 | ||||||
| 	host, port, err := net.SplitHostPort(options.Service) | 	host, port, err := net.SplitHostPort(service) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		// lookup the service using A records | 		// lookup the service using A records | ||||||
| 		ips, err := net.LookupHost(host) | 		ips, err := net.LookupHost(host) | ||||||
| @@ -95,7 +59,7 @@ func (t *table) Query(opts ...router.QueryOption) ([]router.Route, error) { | |||||||
| 		result := make([]router.Route, len(ips)) | 		result := make([]router.Route, len(ips)) | ||||||
| 		for i, ip := range ips { | 		for i, ip := range ips { | ||||||
| 			result[i] = router.Route{ | 			result[i] = router.Route{ | ||||||
| 				Service: options.Service, | 				Service: service, | ||||||
| 				Address: fmt.Sprintf("%s:%d", ip, uint16(p)), | 				Address: fmt.Sprintf("%s:%d", ip, uint16(p)), | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -104,7 +68,7 @@ func (t *table) Query(opts ...router.QueryOption) ([]router.Route, error) { | |||||||
|  |  | ||||||
| 	// we didn't get the port so we'll lookup the service using SRV records. If we can't lookup the | 	// we didn't get the port so we'll lookup the service using SRV records. If we can't lookup the | ||||||
| 	// service using the SRV record, we return the error. | 	// service using the SRV record, we return the error. | ||||||
| 	_, nodes, err := net.LookupSRV(options.Service, "tcp", t.options.Network) | 	_, nodes, err := net.LookupSRV(service, "tcp", d.options.Network) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -113,10 +77,18 @@ func (t *table) Query(opts ...router.QueryOption) ([]router.Route, error) { | |||||||
| 	result := make([]router.Route, len(nodes)) | 	result := make([]router.Route, len(nodes)) | ||||||
| 	for i, n := range nodes { | 	for i, n := range nodes { | ||||||
| 		result[i] = router.Route{ | 		result[i] = router.Route{ | ||||||
| 			Service: options.Service, | 			Service: service, | ||||||
| 			Address: fmt.Sprintf("%s:%d", n.Target, n.Port), | 			Address: fmt.Sprintf("%s:%d", n.Target, n.Port), | ||||||
| 			Network: t.options.Network, | 			Network: d.options.Network, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return result, nil | 	return result, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (d *dns) Watch(opts ...router.WatchOption) (router.Watcher, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *dns) String() string { | ||||||
|  | 	return "dns" | ||||||
|  | } | ||||||
|   | |||||||
| @@ -42,19 +42,19 @@ func (m *mdnsRouter) Table() router.Table { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mdnsRouter) Lookup(opts ...router.QueryOption) ([]router.Route, error) { | func (m *mdnsRouter) Lookup(service string, opts ...router.LookupOption) ([]router.Route, error) { | ||||||
| 	options := router.NewQuery(opts...) | 	options := router.NewLookup(opts...) | ||||||
|  |  | ||||||
| 	// check to see if we have the port provided in the service, e.g. go-micro-srv-foo:8000 | 	// check to see if we have the port provided in the service, e.g. go-micro-srv-foo:8000 | ||||||
| 	service, port, err := net.SplitHostPort(options.Service) | 	srv, port, err := net.SplitHostPort(service) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		service = options.Service | 		srv = service | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// query for the host | 	// query for the host | ||||||
| 	entries := make(chan *mdns.ServiceEntry) | 	entries := make(chan *mdns.ServiceEntry) | ||||||
|  |  | ||||||
| 	p := mdns.DefaultParams(service) | 	p := mdns.DefaultParams(srv) | ||||||
| 	p.Timeout = time.Millisecond * 100 | 	p.Timeout = time.Millisecond * 100 | ||||||
| 	p.Entries = entries | 	p.Entries = entries | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,8 +22,8 @@ type Options struct { | |||||||
| 	Registry registry.Registry | 	Registry registry.Registry | ||||||
| 	// Context for additional options | 	// Context for additional options | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Precache routes | 	// Cache routes | ||||||
| 	Precache bool | 	Cache bool | ||||||
| } | } | ||||||
|  |  | ||||||
| // Id sets Router Id | // Id sets Router Id | ||||||
| @@ -61,10 +61,10 @@ func Registry(r registry.Registry) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Precache the routes | // Cache the routes | ||||||
| func Precache() Option { | func Cache() Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Precache = true | 		o.Cache = true | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -77,3 +77,16 @@ func DefaultOptions() Options { | |||||||
| 		Context:  context.Background(), | 		Context:  context.Background(), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type ReadOptions struct { | ||||||
|  | 	Service string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ReadOption func(o *ReadOptions) | ||||||
|  |  | ||||||
|  | // ReadService sets the service to read from the table | ||||||
|  | func ReadService(s string) ReadOption { | ||||||
|  | 	return func(o *ReadOptions) { | ||||||
|  | 		o.Service = s | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										117
									
								
								router/query.go
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								router/query.go
									
									
									
									
									
								
							| @@ -1,13 +1,11 @@ | |||||||
| package router | package router | ||||||
|  |  | ||||||
| // QueryOption sets routing table query options | // LookupOption sets routing table query options | ||||||
| type QueryOption func(*QueryOptions) | type LookupOption func(*LookupOptions) | ||||||
|  |  | ||||||
| // QueryOptions are routing table query options | // LookupOptions are routing table query options | ||||||
| // TODO replace with Filter(Route) bool | // TODO replace with Filter(Route) bool | ||||||
| type QueryOptions struct { | type LookupOptions struct { | ||||||
| 	// Service is destination service name |  | ||||||
| 	Service string |  | ||||||
| 	// Address of the service | 	// Address of the service | ||||||
| 	Address string | 	Address string | ||||||
| 	// Gateway is route gateway | 	// Gateway is route gateway | ||||||
| @@ -20,53 +18,45 @@ type QueryOptions struct { | |||||||
| 	Link string | 	Link string | ||||||
| } | } | ||||||
|  |  | ||||||
| // QueryService sets service to query | // LookupAddress sets service to query | ||||||
| func QueryService(s string) QueryOption { | func LookupAddress(a string) LookupOption { | ||||||
| 	return func(o *QueryOptions) { | 	return func(o *LookupOptions) { | ||||||
| 		o.Service = s |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // QueryAddress sets service to query |  | ||||||
| func QueryAddress(a string) QueryOption { |  | ||||||
| 	return func(o *QueryOptions) { |  | ||||||
| 		o.Address = a | 		o.Address = a | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // QueryGateway sets gateway address to query | // LookupGateway sets gateway address to query | ||||||
| func QueryGateway(g string) QueryOption { | func LookupGateway(g string) LookupOption { | ||||||
| 	return func(o *QueryOptions) { | 	return func(o *LookupOptions) { | ||||||
| 		o.Gateway = g | 		o.Gateway = g | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // QueryNetwork sets network name to query | // LookupNetwork sets network name to query | ||||||
| func QueryNetwork(n string) QueryOption { | func LookupNetwork(n string) LookupOption { | ||||||
| 	return func(o *QueryOptions) { | 	return func(o *LookupOptions) { | ||||||
| 		o.Network = n | 		o.Network = n | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // QueryRouter sets router id to query | // LookupRouter sets router id to query | ||||||
| func QueryRouter(r string) QueryOption { | func LookupRouter(r string) LookupOption { | ||||||
| 	return func(o *QueryOptions) { | 	return func(o *LookupOptions) { | ||||||
| 		o.Router = r | 		o.Router = r | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // QueryLink sets the link to query | // LookupLink sets the link to query | ||||||
| func QueryLink(link string) QueryOption { | func LookupLink(link string) LookupOption { | ||||||
| 	return func(o *QueryOptions) { | 	return func(o *LookupOptions) { | ||||||
| 		o.Link = link | 		o.Link = link | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewQuery creates new query and returns it | // NewLookup creates new query and returns it | ||||||
| func NewQuery(opts ...QueryOption) QueryOptions { | func NewLookup(opts ...LookupOption) LookupOptions { | ||||||
| 	// default options | 	// default options | ||||||
| 	qopts := QueryOptions{ | 	qopts := LookupOptions{ | ||||||
| 		Service: "*", |  | ||||||
| 		Address: "*", | 		Address: "*", | ||||||
| 		Gateway: "*", | 		Gateway: "*", | ||||||
| 		Network: "*", | 		Network: "*", | ||||||
| @@ -80,3 +70,66 @@ func NewQuery(opts ...QueryOption) QueryOptions { | |||||||
|  |  | ||||||
| 	return qopts | 	return qopts | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // isMatch checks if the route matches given query options | ||||||
|  | func isMatch(route Route, address, gateway, network, rtr, link string) bool { | ||||||
|  | 	// matches the values provided | ||||||
|  | 	match := func(a, b string) bool { | ||||||
|  | 		if a == "*" || b == "*" || a == b { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// a simple struct to hold our values | ||||||
|  | 	type compare struct { | ||||||
|  | 		a string | ||||||
|  | 		b string | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// compare the following values | ||||||
|  | 	values := []compare{ | ||||||
|  | 		{gateway, route.Gateway}, | ||||||
|  | 		{network, route.Network}, | ||||||
|  | 		{rtr, route.Router}, | ||||||
|  | 		{address, route.Address}, | ||||||
|  | 		{link, route.Link}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, v := range values { | ||||||
|  | 		// attempt to match each value | ||||||
|  | 		if !match(v.a, v.b) { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // filterRoutes finds all the routes for given network and router and returns them | ||||||
|  | func Filter(routes []Route, opts LookupOptions) []Route { | ||||||
|  | 	address := opts.Address | ||||||
|  | 	gateway := opts.Gateway | ||||||
|  | 	network := opts.Network | ||||||
|  | 	rtr := opts.Router | ||||||
|  | 	link := opts.Link | ||||||
|  |  | ||||||
|  | 	// routeMap stores the routes we're going to advertise | ||||||
|  | 	routeMap := make(map[string][]Route) | ||||||
|  |  | ||||||
|  | 	for _, route := range routes { | ||||||
|  | 		if isMatch(route, address, gateway, network, rtr, link) { | ||||||
|  | 			// add matchihg route to the routeMap | ||||||
|  | 			routeKey := route.Service + "@" + route.Network | ||||||
|  | 			routeMap[routeKey] = append(routeMap[routeKey], route) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var results []Route | ||||||
|  |  | ||||||
|  | 	for _, route := range routeMap { | ||||||
|  | 		results = append(results, route...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return results | ||||||
|  | } | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ func NewRouter(opts ...router.Option) router.Router { | |||||||
|  |  | ||||||
| 	// create the new table, passing the fetchRoute method in as a fallback if | 	// create the new table, passing the fetchRoute method in as a fallback if | ||||||
| 	// the table doesn't contain the result for a query. | 	// the table doesn't contain the result for a query. | ||||||
| 	r.table = newTable(r.lookup) | 	r.table = newTable() | ||||||
|  |  | ||||||
| 	// start the router | 	// start the router | ||||||
| 	r.start() | 	r.start() | ||||||
| @@ -136,7 +136,7 @@ func (r *rtr) createRoutes(service *registry.Service, network string) []router.R | |||||||
| 			Network:  network, | 			Network:  network, | ||||||
| 			Router:   r.options.Id, | 			Router:   r.options.Id, | ||||||
| 			Link:     router.DefaultLink, | 			Link:     router.DefaultLink, | ||||||
| 			Metric:   router.DefaultLocalMetric, | 			Metric:   router.DefaultMetric, | ||||||
| 			Metadata: node.Metadata, | 			Metadata: node.Metadata, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @@ -241,8 +241,41 @@ func (r *rtr) loadRoutes(reg registry.Registry) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Close the router | ||||||
|  | func (r *rtr) Close() error { | ||||||
|  | 	r.Lock() | ||||||
|  | 	defer r.Unlock() | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case <-r.exit: | ||||||
|  | 		return nil | ||||||
|  | 	default: | ||||||
|  | 		if !r.running { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		close(r.exit) | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r.running = false | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // lookup retrieves all the routes for a given service and creates them in the routing table | // lookup retrieves all the routes for a given service and creates them in the routing table | ||||||
| func (r *rtr) lookup(service string) ([]router.Route, error) { | func (r *rtr) Lookup(service string, opts ...router.LookupOption) ([]router.Route, error) { | ||||||
|  | 	q := router.NewLookup(opts...) | ||||||
|  |  | ||||||
|  | 	// if we find the routes filter and return them | ||||||
|  | 	routes, err := r.table.Read(router.ReadService(service)) | ||||||
|  | 	if err == nil { | ||||||
|  | 		routes = router.Filter(routes, q) | ||||||
|  | 		if len(routes) == 0 { | ||||||
|  | 			return nil, router.ErrRouteNotFound | ||||||
|  | 		} | ||||||
|  | 		return routes, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// lookup the route | ||||||
| 	logger.Tracef("Fetching route for %s domain: %v", service, registry.WildcardDomain) | 	logger.Tracef("Fetching route for %s domain: %v", service, registry.WildcardDomain) | ||||||
|  |  | ||||||
| 	services, err := r.options.Registry.GetService(service, registry.GetDomain(registry.WildcardDomain)) | 	services, err := r.options.Registry.GetService(service, registry.GetDomain(registry.WildcardDomain)) | ||||||
| @@ -254,8 +287,6 @@ func (r *rtr) lookup(service string) ([]router.Route, error) { | |||||||
| 		return nil, fmt.Errorf("failed getting services: %v", err) | 		return nil, fmt.Errorf("failed getting services: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var routes []router.Route |  | ||||||
|  |  | ||||||
| 	for _, srv := range services { | 	for _, srv := range services { | ||||||
| 		domain := getDomain(srv) | 		domain := getDomain(srv) | ||||||
| 		// TODO: should we continue to send the event indicating we created a route? | 		// TODO: should we continue to send the event indicating we created a route? | ||||||
| @@ -263,6 +294,17 @@ func (r *rtr) lookup(service string) ([]router.Route, error) { | |||||||
| 		routes = append(routes, r.createRoutes(srv, domain)...) | 		routes = append(routes, r.createRoutes(srv, domain)...) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// if we're supposed to cache then save the routes | ||||||
|  | 	if r.options.Cache { | ||||||
|  | 		for _, route := range routes { | ||||||
|  | 			r.table.Create(route) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	routes = router.Filter(routes, q) | ||||||
|  | 	if len(routes) == 0 { | ||||||
|  | 		return nil, router.ErrRouteNotFound | ||||||
|  | 	} | ||||||
| 	return routes, nil | 	return routes, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -324,13 +366,6 @@ func (r *rtr) start() error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if r.options.Precache { |  | ||||||
| 		// add all local service routes into the routing table |  | ||||||
| 		if err := r.loadRoutes(r.options.Registry); err != nil { |  | ||||||
| 			return fmt.Errorf("failed loading registry routes: %s", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// add default gateway into routing table | 	// add default gateway into routing table | ||||||
| 	if r.options.Gateway != "" { | 	if r.options.Gateway != "" { | ||||||
| 		// note, the only non-default value is the gateway | 		// note, the only non-default value is the gateway | ||||||
| @@ -341,7 +376,7 @@ func (r *rtr) start() error { | |||||||
| 			Network: "*", | 			Network: "*", | ||||||
| 			Router:  r.options.Id, | 			Router:  r.options.Id, | ||||||
| 			Link:    router.DefaultLink, | 			Link:    router.DefaultLink, | ||||||
| 			Metric:  router.DefaultLocalMetric, | 			Metric:  router.DefaultMetric, | ||||||
| 		} | 		} | ||||||
| 		if err := r.table.Create(route); err != nil { | 		if err := r.table.Create(route); err != nil { | ||||||
| 			return fmt.Errorf("failed adding default gateway route: %s", err) | 			return fmt.Errorf("failed adding default gateway route: %s", err) | ||||||
| @@ -350,25 +385,59 @@ func (r *rtr) start() error { | |||||||
|  |  | ||||||
| 	// create error and exit channels | 	// create error and exit channels | ||||||
| 	r.exit = make(chan bool) | 	r.exit = make(chan bool) | ||||||
|  | 	r.running = true | ||||||
|  |  | ||||||
| 	// periodically refresh all the routes | 	// only cache if told to do so | ||||||
|  | 	if !r.options.Cache { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// create a refresh notify channel | ||||||
|  | 	refresh := make(chan bool, 1) | ||||||
|  |  | ||||||
|  | 	// fires the refresh for loading routes | ||||||
|  | 	refreshRoutes := func() { | ||||||
|  | 		select { | ||||||
|  | 		case refresh <- true: | ||||||
|  | 		default: | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// refresh all the routes in the event of a failure watching the registry | ||||||
| 	go func() { | 	go func() { | ||||||
| 		t1 := time.NewTicker(RefreshInterval) | 		var lastRefresh time.Time | ||||||
| 		defer t1.Stop() |  | ||||||
|  |  | ||||||
| 		t2 := time.NewTicker(PruneInterval) | 		// load a refresh | ||||||
| 		defer t2.Stop() | 		refreshRoutes() | ||||||
|  |  | ||||||
| 		for { | 		for { | ||||||
| 			select { | 			select { | ||||||
| 			case <-r.exit: | 			case <-r.exit: | ||||||
| 				return | 				return | ||||||
| 			case <-t2.C: | 			case <-refresh: | ||||||
| 				r.table.pruneRoutes(RefreshInterval) | 				// don't refresh if we've done so in the past minute | ||||||
| 			case <-t1.C: | 				if !lastRefresh.IsZero() && time.Since(lastRefresh) < time.Minute { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// load new routes | ||||||
| 				if err := r.loadRoutes(r.options.Registry); err != nil { | 				if err := r.loadRoutes(r.options.Registry); err != nil { | ||||||
| 					logger.Debugf("failed refreshing registry routes: %s", err) | 					logger.Debugf("failed refreshing registry routes: %s", err) | ||||||
|  | 					// in this don't prune | ||||||
|  | 					continue | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | 				// first time so nothing to prune | ||||||
|  | 				if !lastRefresh.IsZero() { | ||||||
|  | 					// prune any routes since last refresh since we've | ||||||
|  | 					// updated basically everything we care about | ||||||
|  | 					r.table.pruneRoutes(time.Since(lastRefresh)) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// update the refresh time | ||||||
|  | 				lastRefresh = time.Now() | ||||||
|  | 			case <-time.After(RefreshInterval): | ||||||
|  | 				refreshRoutes() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| @@ -386,6 +455,8 @@ func (r *rtr) start() error { | |||||||
| 						logger.Debugf("failed creating registry watcher: %v", err) | 						logger.Debugf("failed creating registry watcher: %v", err) | ||||||
| 					} | 					} | ||||||
| 					time.Sleep(time.Second) | 					time.Sleep(time.Second) | ||||||
|  | 					// in the event of an error reload routes | ||||||
|  | 					refreshRoutes() | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| @@ -395,46 +466,21 @@ func (r *rtr) start() error { | |||||||
| 						logger.Debugf("Error watching the registry: %v", err) | 						logger.Debugf("Error watching the registry: %v", err) | ||||||
| 					} | 					} | ||||||
| 					time.Sleep(time.Second) | 					time.Sleep(time.Second) | ||||||
|  | 					// in the event of an error reload routes | ||||||
|  | 					refreshRoutes() | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	r.running = true |  | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Lookup routes in the routing table |  | ||||||
| func (r *rtr) Lookup(q ...router.QueryOption) ([]router.Route, error) { |  | ||||||
| 	return r.Table().Query(q...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Watch routes | // Watch routes | ||||||
| func (r *rtr) Watch(opts ...router.WatchOption) (router.Watcher, error) { | func (r *rtr) Watch(opts ...router.WatchOption) (router.Watcher, error) { | ||||||
| 	return r.table.Watch(opts...) | 	return r.table.Watch(opts...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Close the router |  | ||||||
| func (r *rtr) Close() error { |  | ||||||
| 	r.Lock() |  | ||||||
| 	defer r.Unlock() |  | ||||||
|  |  | ||||||
| 	select { |  | ||||||
| 	case <-r.exit: |  | ||||||
| 		return nil |  | ||||||
| 	default: |  | ||||||
| 		if !r.running { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 		close(r.exit) |  | ||||||
|  |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r.running = false |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // String prints debugging information about router | // String prints debugging information about router | ||||||
| func (r *rtr) String() string { | func (r *rtr) String() string { | ||||||
| 	return "registry" | 	return "registry" | ||||||
|   | |||||||
| @@ -12,8 +12,6 @@ import ( | |||||||
| // table is an in-memory routing table | // table is an in-memory routing table | ||||||
| type table struct { | type table struct { | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| 	// lookup for a service |  | ||||||
| 	lookup func(string) ([]router.Route, error) |  | ||||||
| 	// routes stores service routes | 	// routes stores service routes | ||||||
| 	routes map[string]map[uint64]*route | 	routes map[string]map[uint64]*route | ||||||
| 	// watchers stores table watchers | 	// watchers stores table watchers | ||||||
| @@ -26,9 +24,8 @@ type route struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // newtable creates a new routing table and returns it | // newtable creates a new routing table and returns it | ||||||
| func newTable(lookup func(string) ([]router.Route, error), opts ...router.Option) *table { | func newTable() *table { | ||||||
| 	return &table{ | 	return &table{ | ||||||
| 		lookup:   lookup, |  | ||||||
| 		routes:   make(map[string]map[uint64]*route), | 		routes:   make(map[string]map[uint64]*route), | ||||||
| 		watchers: make(map[string]*tableWatcher), | 		watchers: make(map[string]*tableWatcher), | ||||||
| 	} | 	} | ||||||
| @@ -201,153 +198,40 @@ func (t *table) Update(r router.Route) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // List returns a list of all routes in the table | // Read entries from the table | ||||||
| func (t *table) List() ([]router.Route, error) { | func (t *table) Read(opts ...router.ReadOption) ([]router.Route, error) { | ||||||
|  | 	var options router.ReadOptions | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	t.RLock() | 	t.RLock() | ||||||
| 	defer t.RUnlock() | 	defer t.RUnlock() | ||||||
|  |  | ||||||
| 	var routes []router.Route | 	var routes []router.Route | ||||||
| 	for _, rmap := range t.routes { |  | ||||||
| 		for _, route := range rmap { | 	// get the routes based on options passed | ||||||
| 			routes = append(routes, route.route) | 	if len(options.Service) > 0 { | ||||||
|  | 		routeMap, ok := t.routes[options.Service] | ||||||
|  | 		if !ok { | ||||||
|  | 			return nil, router.ErrRouteNotFound | ||||||
|  | 		} | ||||||
|  | 		for _, rt := range routeMap { | ||||||
|  | 			routes = append(routes, rt.route) | ||||||
|  | 		} | ||||||
|  | 		return routes, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// otherwise get all routes | ||||||
|  | 	for _, serviceRoutes := range t.routes { | ||||||
|  | 		for _, rt := range serviceRoutes { | ||||||
|  | 			routes = append(routes, rt.route) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return routes, nil | 	return routes, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // isMatch checks if the route matches given query options |  | ||||||
| func isMatch(route router.Route, address, gateway, network, rtr, link string) bool { |  | ||||||
| 	// matches the values provided |  | ||||||
| 	match := func(a, b string) bool { |  | ||||||
| 		if a == "*" || b == "*" || a == b { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// a simple struct to hold our values |  | ||||||
| 	type compare struct { |  | ||||||
| 		a string |  | ||||||
| 		b string |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// compare the following values |  | ||||||
| 	values := []compare{ |  | ||||||
| 		{gateway, route.Gateway}, |  | ||||||
| 		{network, route.Network}, |  | ||||||
| 		{rtr, route.Router}, |  | ||||||
| 		{address, route.Address}, |  | ||||||
| 		{link, route.Link}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, v := range values { |  | ||||||
| 		// attempt to match each value |  | ||||||
| 		if !match(v.a, v.b) { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // filterRoutes finds all the routes for given network and router and returns them |  | ||||||
| func filterRoutes(routes map[uint64]*route, opts router.QueryOptions) []router.Route { |  | ||||||
| 	address := opts.Address |  | ||||||
| 	gateway := opts.Gateway |  | ||||||
| 	network := opts.Network |  | ||||||
| 	rtr := opts.Router |  | ||||||
| 	link := opts.Link |  | ||||||
|  |  | ||||||
| 	// routeMap stores the routes we're going to advertise |  | ||||||
| 	routeMap := make(map[string][]router.Route) |  | ||||||
|  |  | ||||||
| 	for _, rt := range routes { |  | ||||||
| 		// get the actual route |  | ||||||
| 		route := rt.route |  | ||||||
|  |  | ||||||
| 		if isMatch(route, address, gateway, network, rtr, link) { |  | ||||||
| 			// add matchihg route to the routeMap |  | ||||||
| 			routeKey := route.Service + "@" + route.Network |  | ||||||
| 			routeMap[routeKey] = append(routeMap[routeKey], route) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var results []router.Route |  | ||||||
|  |  | ||||||
| 	for _, route := range routeMap { |  | ||||||
| 		results = append(results, route...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return results |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Lookup queries routing table and returns all routes that match the lookup query |  | ||||||
| func (t *table) Query(q ...router.QueryOption) ([]router.Route, error) { |  | ||||||
| 	// create new query options |  | ||||||
| 	opts := router.NewQuery(q...) |  | ||||||
|  |  | ||||||
| 	// create a cwslicelist of query results |  | ||||||
| 	results := make([]router.Route, 0, len(t.routes)) |  | ||||||
|  |  | ||||||
| 	// readAndFilter routes for this service under read lock. |  | ||||||
| 	readAndFilter := func(q router.QueryOptions) ([]router.Route, bool) { |  | ||||||
| 		t.RLock() |  | ||||||
| 		defer t.RUnlock() |  | ||||||
|  |  | ||||||
| 		routes, ok := t.routes[q.Service] |  | ||||||
| 		if !ok || len(routes) == 0 { |  | ||||||
| 			return nil, false |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return filterRoutes(routes, q), true |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.Service != "*" { |  | ||||||
| 		// try and load services from the cache |  | ||||||
| 		if routes, ok := readAndFilter(opts); ok { |  | ||||||
| 			return routes, nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// lookup the route and try again |  | ||||||
| 		// TODO: move this logic out of the hot path |  | ||||||
| 		// being hammered on queries will require multiple lookups |  | ||||||
| 		routes, err := t.lookup(opts.Service) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// cache the routes |  | ||||||
| 		for _, rt := range routes { |  | ||||||
| 			t.Create(rt) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// try again |  | ||||||
| 		if routes, ok := readAndFilter(opts); ok { |  | ||||||
| 			return routes, nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return nil, router.ErrRouteNotFound |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// search through all destinations |  | ||||||
| 	t.RLock() |  | ||||||
|  |  | ||||||
| 	for _, routes := range t.routes { |  | ||||||
| 		// filter the routes |  | ||||||
| 		found := filterRoutes(routes, opts) |  | ||||||
| 		// ensure we don't append zero length routes |  | ||||||
| 		if len(found) == 0 { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		results = append(results, found...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	t.RUnlock() |  | ||||||
|  |  | ||||||
| 	return results, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Watch returns routing table entry watcher | // Watch returns routing table entry watcher | ||||||
| func (t *table) Watch(opts ...router.WatchOption) (router.Watcher, error) { | func (t *table) Watch(opts ...router.WatchOption) (router.Watcher, error) { | ||||||
| 	// by default watch everything | 	// by default watch everything | ||||||
|   | |||||||
| @@ -1,15 +1,13 @@ | |||||||
| package registry | package registry | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/v3/router" | 	"github.com/micro/go-micro/v3/router" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func testSetup() (*table, router.Route) { | func testSetup() (*table, router.Route) { | ||||||
| 	routr := NewRouter().(*rtr) | 	table := newTable() | ||||||
| 	table := newTable(routr.lookup) |  | ||||||
|  |  | ||||||
| 	route := router.Route{ | 	route := router.Route{ | ||||||
| 		Service: "dest.svc", | 		Service: "dest.svc", | ||||||
| @@ -101,7 +99,7 @@ func TestList(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	routes, err := table.List() | 	routes, err := table.Read() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("error listing routes: %s", err) | 		t.Fatalf("error listing routes: %s", err) | ||||||
| 	} | 	} | ||||||
| @@ -114,235 +112,20 @@ func TestList(t *testing.T) { | |||||||
| func TestQuery(t *testing.T) { | func TestQuery(t *testing.T) { | ||||||
| 	table, route := testSetup() | 	table, route := testSetup() | ||||||
|  |  | ||||||
| 	svc := []string{"svc1", "svc2", "svc3", "svc1"} | 	if err := table.Create(route); err != nil { | ||||||
| 	net := []string{"net1", "net2", "net1", "net3"} | 		t.Fatalf("error adding route: %s", err) | ||||||
| 	gw := []string{"gw1", "gw2", "gw3", "gw3"} |  | ||||||
| 	rtr := []string{"rtr1", "rt2", "rt3", "rtr3"} |  | ||||||
|  |  | ||||||
| 	for i := 0; i < len(svc); i++ { |  | ||||||
| 		route.Service = svc[i] |  | ||||||
| 		route.Network = net[i] |  | ||||||
| 		route.Gateway = gw[i] |  | ||||||
| 		route.Router = rtr[i] |  | ||||||
| 		route.Link = router.DefaultLink |  | ||||||
|  |  | ||||||
| 		if err := table.Create(route); err != nil { |  | ||||||
| 			t.Fatalf("error adding route: %s", err) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// return all routes | 	rt, err := table.Read(router.ReadService(route.Service)) | ||||||
| 	routes, err := table.Query() |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("error looking up routes: %s", err) | 		t.Fatal("Expected a route got err", err) | ||||||
| 	} else if len(routes) == 0 { |  | ||||||
| 		t.Fatalf("error looking up routes: not found") |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// query routes particular network | 	if len(rt) != 1 { | ||||||
| 	network := "net1" | 		t.Fatalf("Expected one route got %d", len(rt)) | ||||||
|  |  | ||||||
| 	routes, err = table.Query(router.QueryNetwork(network)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(routes) != 2 { | 	if rt[0].Hash() != route.Hash() { | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes)) | 		t.Fatal("Mismatched routes received") | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, route := range routes { |  | ||||||
| 		if route.Network != network { |  | ||||||
| 			t.Fatalf("incorrect route returned. Expected network: %s, found: %s", network, route.Network) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// query routes for particular gateway |  | ||||||
| 	gateway := "gw1" |  | ||||||
|  |  | ||||||
| 	routes, err = table.Query(router.QueryGateway(gateway)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) != 1 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if routes[0].Gateway != gateway { |  | ||||||
| 		t.Fatalf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// query routes for particular router |  | ||||||
| 	rt := "rtr1" |  | ||||||
|  |  | ||||||
| 	routes, err = table.Query(router.QueryRouter(rt)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) != 1 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if routes[0].Router != rt { |  | ||||||
| 		t.Fatalf("incorrect route returned. Expected router: %s, found: %s", rt, routes[0].Router) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// query particular gateway and network |  | ||||||
| 	query := []router.QueryOption{ |  | ||||||
| 		router.QueryGateway(gateway), |  | ||||||
| 		router.QueryNetwork(network), |  | ||||||
| 		router.QueryRouter(rt), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	routes, err = table.Query(query...) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) != 1 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if routes[0].Gateway != gateway { |  | ||||||
| 		t.Fatalf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if routes[0].Network != network { |  | ||||||
| 		t.Fatalf("incorrect network returned. Expected network: %s, found: %s", network, routes[0].Network) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if routes[0].Router != rt { |  | ||||||
| 		t.Fatalf("incorrect route returned. Expected router: %s, found: %s", rt, routes[0].Router) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// non-existen route query |  | ||||||
| 	routes, err = table.Query(router.QueryService("foobar")) |  | ||||||
| 	if err != router.ErrRouteNotFound { |  | ||||||
| 		t.Fatalf("error looking up routes. Expected: %s, found: %s", router.ErrRouteNotFound, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) != 0 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 0, len(routes)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// query NO routes |  | ||||||
| 	query = []router.QueryOption{ |  | ||||||
| 		router.QueryGateway(gateway), |  | ||||||
| 		router.QueryNetwork(network), |  | ||||||
| 		router.QueryLink("network"), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	routes, err = table.Query(query...) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) > 0 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 0, len(routes)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// insert local routes to query |  | ||||||
| 	for i := 0; i < 2; i++ { |  | ||||||
| 		route.Link = "foobar" |  | ||||||
| 		route.Address = fmt.Sprintf("local.route.address-%d", i) |  | ||||||
| 		if err := table.Create(route); err != nil { |  | ||||||
| 			t.Fatalf("error adding route: %s", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// query local routes |  | ||||||
| 	query = []router.QueryOption{ |  | ||||||
| 		router.QueryGateway("*"), |  | ||||||
| 		router.QueryNetwork("*"), |  | ||||||
| 		router.QueryLink("foobar"), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	routes, err = table.Query(query...) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) != 2 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// add two different routes for svcX with different metric |  | ||||||
| 	for i := 0; i < 2; i++ { |  | ||||||
| 		route.Service = "svcX" |  | ||||||
| 		route.Address = fmt.Sprintf("svcX.route.address-%d", i) |  | ||||||
| 		route.Metric = int64(100 + i) |  | ||||||
| 		route.Link = router.DefaultLink |  | ||||||
| 		if err := table.Create(route); err != nil { |  | ||||||
| 			t.Fatalf("error adding route: %s", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	query = []router.QueryOption{ |  | ||||||
| 		router.QueryService("svcX"), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	routes, err = table.Query(query...) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up routes: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(routes) != 2 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes)) |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestFallback(t *testing.T) { |  | ||||||
|  |  | ||||||
| 	r := &rtr{ |  | ||||||
| 		options: router.DefaultOptions(), |  | ||||||
| 	} |  | ||||||
| 	route := router.Route{ |  | ||||||
| 		Service: "go.micro.service.foo", |  | ||||||
| 		Router:  r.options.Id, |  | ||||||
| 		Link:    router.DefaultLink, |  | ||||||
| 		Metric:  router.DefaultLocalMetric, |  | ||||||
| 	} |  | ||||||
| 	r.table = newTable(func(s string) ([]router.Route, error) { |  | ||||||
| 		return []router.Route{route}, nil |  | ||||||
| 	}) |  | ||||||
| 	r.start() |  | ||||||
|  |  | ||||||
| 	rts, err := r.Lookup(router.QueryService("go.micro.service.foo")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up service %s", err) |  | ||||||
| 	} |  | ||||||
| 	if len(rts) != 1 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned %d", len(rts)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// deleting from the table but the next query should invoke the fallback that we passed during new table creation |  | ||||||
| 	if err := r.table.Delete(route); err != nil { |  | ||||||
| 		t.Fatalf("error deleting route %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	rts, err = r.Lookup(router.QueryService("go.micro.service.foo")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("error looking up service %s", err) |  | ||||||
| 	} |  | ||||||
| 	if len(rts) != 1 { |  | ||||||
| 		t.Fatalf("incorrect number of routes returned %d", len(rts)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFallbackError(t *testing.T) { |  | ||||||
| 	r := &rtr{ |  | ||||||
| 		options: router.DefaultOptions(), |  | ||||||
| 	} |  | ||||||
| 	r.table = newTable(func(s string) ([]router.Route, error) { |  | ||||||
| 		return nil, fmt.Errorf("ERROR") |  | ||||||
| 	}) |  | ||||||
| 	r.start() |  | ||||||
| 	_, err := r.Lookup(router.QueryService("go.micro.service.foo")) |  | ||||||
| 	if err == nil { |  | ||||||
| 		t.Fatalf("expected error looking up service but none returned") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,40 +0,0 @@ | |||||||
| package router |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"hash/fnv" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	// DefaultLink is default network link |  | ||||||
| 	DefaultLink = "local" |  | ||||||
| 	// DefaultLocalMetric is default route cost for a local route |  | ||||||
| 	DefaultLocalMetric int64 = 1 |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Route is network route |  | ||||||
| type Route struct { |  | ||||||
| 	// Service is destination service name |  | ||||||
| 	Service string |  | ||||||
| 	// Address is service node address |  | ||||||
| 	Address string |  | ||||||
| 	// Gateway is route gateway |  | ||||||
| 	Gateway string |  | ||||||
| 	// Network is network address |  | ||||||
| 	Network string |  | ||||||
| 	// Router is router id |  | ||||||
| 	Router string |  | ||||||
| 	// Link is network link |  | ||||||
| 	Link string |  | ||||||
| 	// Metric is the route cost metric |  | ||||||
| 	Metric int64 |  | ||||||
| 	// Metadata for the route |  | ||||||
| 	Metadata map[string]string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Hash returns route hash sum. |  | ||||||
| func (r *Route) Hash() uint64 { |  | ||||||
| 	h := fnv.New64() |  | ||||||
| 	h.Reset() |  | ||||||
| 	h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Router + r.Link)) |  | ||||||
| 	return h.Sum64() |  | ||||||
| } |  | ||||||
| @@ -3,9 +3,14 @@ package router | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"hash/fnv" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|  | 	// DefaultLink is default network link | ||||||
|  | 	DefaultLink = "local" | ||||||
|  | 	// DefaultLocalMetric is default route cost for a local route | ||||||
|  | 	DefaultMetric int64 = 1 | ||||||
| 	// DefaultNetwork is default micro network | 	// DefaultNetwork is default micro network | ||||||
| 	DefaultNetwork = "micro" | 	DefaultNetwork = "micro" | ||||||
| 	// ErrRouteNotFound is returned when no route was found in the routing table | 	// ErrRouteNotFound is returned when no route was found in the routing table | ||||||
| @@ -23,7 +28,7 @@ type Router interface { | |||||||
| 	// The routing table | 	// The routing table | ||||||
| 	Table() Table | 	Table() Table | ||||||
| 	// Lookup queries routes in the routing table | 	// Lookup queries routes in the routing table | ||||||
| 	Lookup(...QueryOption) ([]Route, error) | 	Lookup(service string, opts ...LookupOption) ([]Route, error) | ||||||
| 	// Watch returns a watcher which tracks updates to the routing table | 	// Watch returns a watcher which tracks updates to the routing table | ||||||
| 	Watch(opts ...WatchOption) (Watcher, error) | 	Watch(opts ...WatchOption) (Watcher, error) | ||||||
| 	// Close the router | 	// Close the router | ||||||
| @@ -40,10 +45,8 @@ type Table interface { | |||||||
| 	Delete(Route) error | 	Delete(Route) error | ||||||
| 	// Update route in the routing table | 	// Update route in the routing table | ||||||
| 	Update(Route) error | 	Update(Route) error | ||||||
| 	// List all routes in the table | 	// Read is for querying the table | ||||||
| 	List() ([]Route, error) | 	Read(...ReadOption) ([]Route, error) | ||||||
| 	// Query routes in the routing table |  | ||||||
| 	Query(...QueryOption) ([]Route, error) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Option used by the router | // Option used by the router | ||||||
| @@ -60,3 +63,31 @@ const ( | |||||||
| 	// Error means the router has encountered error | 	// Error means the router has encountered error | ||||||
| 	Error | 	Error | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Route is a network route | ||||||
|  | type Route struct { | ||||||
|  | 	// Service is destination service name | ||||||
|  | 	Service string | ||||||
|  | 	// Address is service node address | ||||||
|  | 	Address string | ||||||
|  | 	// Gateway is route gateway | ||||||
|  | 	Gateway string | ||||||
|  | 	// Network is network address | ||||||
|  | 	Network string | ||||||
|  | 	// Router is router id | ||||||
|  | 	Router string | ||||||
|  | 	// Link is network link | ||||||
|  | 	Link string | ||||||
|  | 	// Metric is the route cost metric | ||||||
|  | 	Metric int64 | ||||||
|  | 	// Metadata for the route | ||||||
|  | 	Metadata map[string]string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Hash returns route hash sum. | ||||||
|  | func (r *Route) Hash() uint64 { | ||||||
|  | 	h := fnv.New64() | ||||||
|  | 	h.Reset() | ||||||
|  | 	h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Router + r.Link)) | ||||||
|  | 	return h.Sum64() | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,21 +1,29 @@ | |||||||
|  | // Package static is a static router which returns the service name as the address + port | ||||||
| package static | package static | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net" | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/v3/router" | 	"github.com/micro/go-micro/v3/router" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// DefaulPort is the port to append where nothing is set | ||||||
|  | 	DefaultPort = 8080 | ||||||
|  | ) | ||||||
|  |  | ||||||
| // NewRouter returns an initialized static router | // NewRouter returns an initialized static router | ||||||
| func NewRouter(opts ...router.Option) router.Router { | func NewRouter(opts ...router.Option) router.Router { | ||||||
| 	options := router.DefaultOptions() | 	options := router.DefaultOptions() | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
| 	return &static{options, new(table)} | 	return &static{options} | ||||||
| } | } | ||||||
|  |  | ||||||
| type static struct { | type static struct { | ||||||
| 	options router.Options | 	options router.Options | ||||||
| 	table   router.Table |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *static) Init(opts ...router.Option) error { | func (s *static) Init(opts ...router.Option) error { | ||||||
| @@ -33,8 +41,26 @@ func (s *static) Table() router.Table { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *static) Lookup(opts ...router.QueryOption) ([]router.Route, error) { | func (s *static) Lookup(service string, opts ...router.LookupOption) ([]router.Route, error) { | ||||||
| 	return s.table.Query(opts...) | 	options := router.NewLookup(opts...) | ||||||
|  |  | ||||||
|  | 	_, _, err := net.SplitHostPort(service) | ||||||
|  | 	if err == nil { | ||||||
|  | 		// use the address | ||||||
|  | 		options.Address = service | ||||||
|  | 	} else { | ||||||
|  | 		options.Address = fmt.Sprintf("%s:%d", service, DefaultPort) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return []router.Route{ | ||||||
|  | 		router.Route{ | ||||||
|  | 			Service: service, | ||||||
|  | 			Address: options.Address, | ||||||
|  | 			Gateway: options.Gateway, | ||||||
|  | 			Network: options.Network, | ||||||
|  | 			Router:  options.Router, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *static) Watch(opts ...router.WatchOption) (router.Watcher, error) { | func (s *static) Watch(opts ...router.WatchOption) (router.Watcher, error) { | ||||||
| @@ -48,35 +74,3 @@ func (s *static) Close() error { | |||||||
| func (s *static) String() string { | func (s *static) String() string { | ||||||
| 	return "static" | 	return "static" | ||||||
| } | } | ||||||
|  |  | ||||||
| type table struct{} |  | ||||||
|  |  | ||||||
| func (t *table) Create(router.Route) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Delete(router.Route) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Update(router.Route) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) List() ([]router.Route, error) { |  | ||||||
| 	return nil, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *table) Query(opts ...router.QueryOption) ([]router.Route, error) { |  | ||||||
| 	options := router.NewQuery(opts...) |  | ||||||
|  |  | ||||||
| 	return []router.Route{ |  | ||||||
| 		router.Route{ |  | ||||||
| 			Address: options.Service, |  | ||||||
| 			Service: options.Address, |  | ||||||
| 			Gateway: options.Gateway, |  | ||||||
| 			Network: options.Network, |  | ||||||
| 			Router:  options.Router, |  | ||||||
| 		}, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -435,7 +435,7 @@ func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) er | |||||||
| 		if exist, err := k.namespaceExists(namespace); err == nil && !exist { | 		if exist, err := k.namespaceExists(namespace); err == nil && !exist { | ||||||
| 			if err := k.createNamespace(namespace); err != nil { | 			if err := k.createNamespace(namespace); err != nil { | ||||||
| 				if logger.V(logger.WarnLevel, logger.DefaultLogger) { | 				if logger.V(logger.WarnLevel, logger.DefaultLogger) { | ||||||
| 					logger.Warnf("Error creating namespacr %v: %v", namespace, err) | 					logger.Warnf("Error creating namespace %v: %v", namespace, err) | ||||||
| 				} | 				} | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| @@ -712,3 +712,35 @@ func credentialsName(service *runtime.Service) string { | |||||||
| 	name := fmt.Sprintf("%v-%v-credentials", service.Name, service.Version) | 	name := fmt.Sprintf("%v-%v-credentials", service.Name, service.Version) | ||||||
| 	return client.SerializeResourceName(name) | 	return client.SerializeResourceName(name) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (k *kubernetes) CreateNamespace(ns string) error { | ||||||
|  | 	err := k.client.Create(&client.Resource{ | ||||||
|  | 		Kind: "namespace", | ||||||
|  | 		Value: client.Namespace{ | ||||||
|  | 			Metadata: &client.Metadata{ | ||||||
|  | 				Name: ns, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
|  | 			logger.Errorf("Error creating namespace %v: %v", ns, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (k *kubernetes) DeleteNamespace(ns string) error { | ||||||
|  | 	err := k.client.Delete(&client.Resource{ | ||||||
|  | 		Kind: "namespace", | ||||||
|  | 		Name: ns, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if err != nil { | ||||||
|  | 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||||
|  | 				logger.Errorf("Error deleting namespace %v: %v", ns, err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								runtime/kubernetes/kubernetes_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								runtime/kubernetes/kubernetes_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | // +build kubernetes | ||||||
|  |  | ||||||
|  | package kubernetes | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func setupClient(t *testing.T) { | ||||||
|  | 	files := []string{"token", "ca.crt"} | ||||||
|  | 	for _, f := range files { | ||||||
|  | 		cmd := exec.Command("kubectl", "get", "secrets", "-o", | ||||||
|  | 			fmt.Sprintf(`jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='micro-runtime')].data.%s}"`, | ||||||
|  | 				strings.ReplaceAll(f, ".", "\\."))) | ||||||
|  | 		if outp, err := cmd.Output(); err != nil { | ||||||
|  | 			t.Fatalf("Failed to set k8s token %s", err) | ||||||
|  | 		} else { | ||||||
|  | 			outq := outp[1 : len(outp)-1] | ||||||
|  | 			decoded, err := base64.StdEncoding.DecodeString(string(outq)) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("Failed to set k8s token %s '%s'", err, outq) | ||||||
|  | 			} | ||||||
|  | 			if err := ioutil.WriteFile("/var/run/secrets/kubernetes.io/serviceaccount/"+f, decoded, 0755); err != nil { | ||||||
|  | 				t.Fatalf("Error setting up k8s %s", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | 	outp, err := exec.Command("kubectl", "config", "view", "-o", `jsonpath='{.clusters[?(@.name=="kind-kind")].cluster.server}'`).Output() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Cannot find server for kind %s", err) | ||||||
|  | 	} | ||||||
|  | 	serverHost := string(outp) | ||||||
|  |  | ||||||
|  | 	split := strings.Split(serverHost[9:len(serverHost)-1], ":") | ||||||
|  | 	os.Setenv("KUBERNETES_SERVICE_HOST", split[0]) | ||||||
|  | 	os.Setenv("KUBERNETES_SERVICE_PORT", split[1]) | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNamespaceCreateDelete(t *testing.T) { | ||||||
|  | 	defer func() { | ||||||
|  | 		exec.Command("kubectl", "delete", "namespace", "foobar").Run() | ||||||
|  | 	}() | ||||||
|  | 	setupClient(t) | ||||||
|  | 	r := NewRuntime() | ||||||
|  | 	if err := r.CreateNamespace("foobar"); err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error creating namespace %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !namespaceExists(t, "foobar") { | ||||||
|  | 		t.Fatalf("Namespace foobar not found") | ||||||
|  | 	} | ||||||
|  | 	if err := r.DeleteNamespace("foobar"); err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error deleting namespace %s", err) | ||||||
|  | 	} | ||||||
|  | 	if namespaceExists(t, "foobar") { | ||||||
|  | 		t.Fatalf("Namespace foobar still exists") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func namespaceExists(t *testing.T, ns string) bool { | ||||||
|  | 	cmd := exec.Command("kubectl", "get", "namespaces") | ||||||
|  | 	outp, err := cmd.Output() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error listing namespaces %s", err) | ||||||
|  | 	} | ||||||
|  | 	exists, err := regexp.Match(ns+"\\s+Active", outp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Error listing namespaces %s", err) | ||||||
|  | 	} | ||||||
|  | 	return exists | ||||||
|  |  | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user