Compare commits
39 Commits
v3.10.73
...
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