diff --git a/README.md b/README.md index 3b06170..d379362 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # GRPC consul resolver -Feature rich and easy-to-use resolver which return endpoints for service from the [Hashicorp Consul](https://www.consul.io) and watch for the changes. +Feature rich and easy-to-use resolver which return endpoints for service or prepared query from the [Hashicorp Consul](https://www.consul.io) and watch for the changes. -This library is *production ready* and will always *save backward-compatibility* +This library is _production ready_ and will always _save backward-compatibility_ ## Quick Start @@ -11,26 +11,30 @@ For using resolving endpoints from your [Hashicorp Consul](https://www.consul.io For full example see [this section](#example) ## Connection string + `consul://[user:password@]127.0.0.127:8555/my-service?[healthy=]&[wait=]&[near=]&[insecure=]&[limit=]&[tag=]&[token=]` -*Parameters:* - -| Name | Format | Description | -|--------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------| -| tag | string | Select endpoints only with this tag | -| healthy | true/false | Return only endpoints which pass all health-checks. Default: false | -| wait | as in time.ParseDuration | Wait time for watch changes. Due this time period endpoints will be force refreshed. Default: inherits agent property | -| insecure | true/false | Allow insecure communication with Consul. Default: true | -| near | string | Sort endpoints by response duration. Can be efficient combine with `limit` parameter default: "_agent" | -| limit | int | Limit number of endpoints for the service. Default: no limit | -| timeout | as in time.ParseDuration | Http-client timeout. Default: 60s | -| max-backoff | as in time.ParseDuration | Max backoff time for reconnect to consul. Reconnects will start from 10ms to _max-backoff_ exponentialy with factor 2. Default: 1s | -| token | string | Consul token | -| dc | string | Consul datacenter to choose. Optional | -| allow-stale | true/false | Allow stale results from the agent. https://www.consul.io/api/features/consistency.html#stale | -| require-consistent | true/false | RequireConsistent forces the read to be fully consistent. This is more expensive but prevents ever performing a stale read. | +_Parameters:_ + +| Name | Format | Description | +| ------------------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | +| type | service/prepared_query | Whether to query a Consul service or a prepared query. Default: service | +| tag | string | Select endpoints only with this tag. Only when type=service. | +| healthy | true/false | Return only endpoints which pass all health-checks. Only when type=service. Default: false | +| wait | as in time.ParseDuration | Wait time for watch changes. Due this time period endpoints will be force refreshed. Only when type=service. Default: inherits agent property | +| insecure | true/false | Allow insecure communication with Consul. Default: true | +| near | string | Sort endpoints by response duration. Can be efficient combine with `limit` parameter default: "\_agent" | +| limit | int | Limit number of endpoints for the service. Default: no limit | +| timeout | as in time.ParseDuration | Http-client timeout. Default: 60s | +| max-backoff | as in time.ParseDuration | Max backoff time for reconnect to consul. Reconnects will start from 10ms to _max-backoff_ exponentialy with factor 2. Default: 1s | +| poll-interval | as in time.ParseDuration | How often to poll prepared queries. Only when type=prepared_query. Default: 30s | +| token | string | Consul token | +| dc | string | Consul datacenter to choose. Optional | +| allow-stale | true/false | Allow stale results from the agent. https://www.consul.io/api/features/consistency.html#stale | +| require-consistent | true/false | RequireConsistent forces the read to be fully consistent. This is more expensive but prevents ever performing a stale read. | ## Example + ```go package main diff --git a/builder.go b/builder.go index e300a40..2ec2d1a 100644 --- a/builder.go +++ b/builder.go @@ -29,7 +29,13 @@ func (b *builder) Build(url resolver.Target, cc resolver.ClientConn, opts resolv ctx, cancel := context.WithCancel(context.Background()) pipe := make(chan []string) - go watchConsulService(ctx, cli.Health(), tgt, pipe) + switch tgt.Type { + case targetTypeService: + go watchConsulService(ctx, cli.Health(), tgt, pipe) + case targetTypePreparedQuery: + go watchPreparedQuery(ctx, cli.PreparedQuery(), tgt, pipe) + } + go populateEndpoints(ctx, cc, pipe) return &resolvr{cancelFunc: cancel}, nil diff --git a/consul.go b/consul.go index b63c6e0..4313496 100644 --- a/consul.go +++ b/consul.go @@ -32,11 +32,15 @@ func (r *resolvr) Close() { } //go:generate mockgen -package mocks -destination internal/mocks/resolverClientConn.go google.golang.org/grpc/resolver ClientConn -//go:generate mockgen -package mocks -destination internal/mocks/servicer.go -source consul.go servicer +//go:generate mockgen -package mocks -destination internal/mocks/consul.go -source consul.go servicer querier type servicer interface { Service(string, string, bool, *api.QueryOptions) ([]*api.ServiceEntry, *api.QueryMeta, error) } +type querier interface { + Execute(string, *api.QueryOptions) (*api.PreparedQueryExecuteResponse, *api.QueryMeta, error) +} + func watchConsulService(ctx context.Context, s servicer, tgt target, out chan<- []string) { res := make(chan []string) quit := make(chan struct{}) @@ -50,7 +54,7 @@ func watchConsulService(ctx context.Context, s servicer, tgt target, out chan<- var lastIndex uint64 for { ss, meta, err := s.Service( - tgt.Service, + tgt.Target, tgt.Tag, tgt.Healthy, &api.QueryOptions{ @@ -125,6 +129,92 @@ func watchConsulService(ctx context.Context, s servicer, tgt target, out chan<- } } +func watchPreparedQuery(ctx context.Context, q querier, tgt target, out chan<- []string) { + res := make(chan []string) + quit := make(chan struct{}) + bck := &backoff.Backoff{ + Factor: 2, + Jitter: true, + Min: 10 * time.Millisecond, + Max: tgt.MaxBackoff, + } + go func() { + ticker := time.NewTicker(tgt.PollInterval) + defer ticker.Stop() + + for { + ss, meta, err := q.Execute( + tgt.Target, + &api.QueryOptions{ + Near: tgt.Near, + Datacenter: tgt.Dc, + AllowStale: tgt.AllowStale, + RequireConsistent: tgt.RequireConsistent, + }, + ) + if err != nil { + // No need to continue if the context is done/cancelled. + // We check that here directly because the check for the closed quit channel + // at the end of the loop is not reached when calling continue here. + select { + case <-quit: + return + default: + grpclog.Errorf("[Consul resolver] Couldn't fetch endpoints. target={%s}; error={%v}", tgt.String(), err) + time.Sleep(bck.Duration()) + continue + } + } + bck.Reset() + grpclog.Infof("[Consul resolver] %d endpoints fetched in %s for target={%s}", + len(ss.Nodes), + meta.RequestTime, + tgt.String(), + ) + + ee := make([]string, 0, len(ss.Nodes)) + for _, s := range ss.Nodes { + address := s.Service.Address + if s.Service.Address == "" { + address = s.Node.Address + } + ee = append(ee, fmt.Sprintf("%s:%d", address, s.Service.Port)) + } + + if tgt.Limit != 0 && len(ee) > tgt.Limit { + ee = ee[:tgt.Limit] + } + select { + case res <- ee: + <-ticker.C + continue + case <-quit: + return + } + } + }() + + for { + // If in the below select both channels have values that can be read, + // Go picks one pseudo-randomly. + // But when the context is canceled we want to act upon it immediately. + if ctx.Err() != nil { + // Close quit so the goroutine returns and doesn't leak. + // Do NOT close res because that can lead to panics in the goroutine. + // res will be garbage collected at some point. + close(quit) + return + } + select { + case ee := <-res: + out <- ee + case <-ctx.Done(): + close(quit) + return + } + } +} + func populateEndpoints(ctx context.Context, clientConn resolver.ClientConn, input <-chan []string) { for { select { diff --git a/consul_test.go b/consul_test.go index a90bb2c..2027814 100644 --- a/consul_test.go +++ b/consul_test.go @@ -70,7 +70,7 @@ func TestWatchConsulService(t *testing.T) { errorFromService error want []string }{ - {"simple", target{Service: "svc", Wait: time.Second}, + {"simple", target{Target: "svc", Wait: time.Second}, []*api.ServiceEntry{ &api.ServiceEntry{ Service: &api.AgentService{Address: "127.0.0.1", Port: 1024}, @@ -102,7 +102,7 @@ func TestWatchConsulService(t *testing.T) { } }() fconsul := mocks.NewMockservicer(ctrl) - fconsul.EXPECT().Service(tt.tgt.Service, tt.tgt.Tag, tt.tgt.Healthy, &api.QueryOptions{ + fconsul.EXPECT().Service(tt.tgt.Target, tt.tgt.Tag, tt.tgt.Healthy, &api.QueryOptions{ WaitIndex: 0, Near: tt.tgt.Near, WaitTime: tt.tgt.Wait, @@ -112,7 +112,7 @@ func TestWatchConsulService(t *testing.T) { }). Times(1). Return(tt.services, &api.QueryMeta{LastIndex: 1}, tt.errorFromService) - fconsul.EXPECT().Service(tt.tgt.Service, tt.tgt.Tag, tt.tgt.Healthy, &api.QueryOptions{ + fconsul.EXPECT().Service(tt.tgt.Target, tt.tgt.Tag, tt.tgt.Healthy, &api.QueryOptions{ WaitIndex: 1, Near: tt.tgt.Near, WaitTime: tt.tgt.Wait, @@ -137,3 +137,73 @@ func TestWatchConsulService(t *testing.T) { }) } } + +func TestWatchPeparedQuery(t *testing.T) { + tests := []struct { + name string + tgt target + responses []*api.PreparedQueryExecuteResponse + errorFromService error + want [][]string + }{ + {"simple", target{Target: "myquery", PollInterval: 100 * time.Millisecond}, + []*api.PreparedQueryExecuteResponse{ + { + Nodes: []api.ServiceEntry{ + { + Service: &api.AgentService{Address: "127.0.0.1", Port: 1024}, + }, + }, + }, + { + Nodes: []api.ServiceEntry{ + { + Service: &api.AgentService{Address: "127.0.0.2", Port: 1024}, + }, + }, + }, + }, + nil, + [][]string{ + {"127.0.0.1:1024"}, + {"127.0.0.2:1024"}, + }, + }, + // TODO: Add more tests-cases + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + i := 0 + var out = make(chan []string, 1) + fconsul := mocks.NewMockquerier(ctrl) + fconsul.EXPECT().Execute(tt.tgt.Target, &api.QueryOptions{ + Near: tt.tgt.Near, + Datacenter: tt.tgt.Dc, + AllowStale: tt.tgt.AllowStale, + RequireConsistent: tt.tgt.RequireConsistent, + }). + Times(len(tt.responses)). + DoAndReturn(func(arg0 string, arg1 *api.QueryOptions) (*api.PreparedQueryExecuteResponse, *api.QueryMeta, error) { + v := tt.responses[i] + i++ + return v, &api.QueryMeta{}, tt.errorFromService + }) + + go watchPreparedQuery(ctx, fconsul, tt.tgt, out) + + for _, want := range tt.want { + select { + case <-ctx.Done(): + return + case got := <-out: + require.Equal(t, want, got) + } + } + }) + } +} diff --git a/go.mod b/go.mod index 0e4bb9f..73468ee 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/armon/go-metrics v0.3.2 // indirect github.com/go-playground/form v3.1.4+incompatible - github.com/golang/mock v1.1.1 + github.com/golang/mock v1.6.0 github.com/google/btree v1.0.0 // indirect github.com/hashicorp/consul/api v1.3.0 github.com/hashicorp/go-immutable-radix v1.1.0 // indirect @@ -16,9 +16,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.4.2 github.com/stretchr/testify v1.4.0 - golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect - golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect - golang.org/x/text v0.3.2 // indirect google.golang.org/genproto v0.0.0-20200210034751-acff78025515 // indirect google.golang.org/grpc v1.27.1 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 82fca19..6fdcb14 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.2 h1:EyUnxyP2yaGpLgMiuyyz8sHnByqeTJUfGs72pdH0i4A= github.com/armon/go-metrics v0.3.2/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= @@ -36,20 +35,17 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= @@ -64,7 +60,6 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -73,7 +68,6 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -84,7 +78,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -92,7 +85,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.8.5 h1:ZynDUIQiA8usmRgPdGPHFdPnb1wgGI9tK3mO9hcAJjc= github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= @@ -115,7 +107,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -131,12 +122,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -163,75 +152,74 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200210034751-acff78025515 h1:SlofR15fzhHsop1cmdda0uNO88mGfustCgMZoy2VGfA= google.golang.org/genproto v0.0.0-20200210034751-acff78025515/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= @@ -241,16 +229,13 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/mocks/consul.go b/internal/mocks/consul.go new file mode 100644 index 0000000..154b4b3 --- /dev/null +++ b/internal/mocks/consul.go @@ -0,0 +1,90 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: consul.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + api "github.com/hashicorp/consul/api" +) + +// Mockservicer is a mock of servicer interface. +type Mockservicer struct { + ctrl *gomock.Controller + recorder *MockservicerMockRecorder +} + +// MockservicerMockRecorder is the mock recorder for Mockservicer. +type MockservicerMockRecorder struct { + mock *Mockservicer +} + +// NewMockservicer creates a new mock instance. +func NewMockservicer(ctrl *gomock.Controller) *Mockservicer { + mock := &Mockservicer{ctrl: ctrl} + mock.recorder = &MockservicerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Mockservicer) EXPECT() *MockservicerMockRecorder { + return m.recorder +} + +// Service mocks base method. +func (m *Mockservicer) Service(arg0, arg1 string, arg2 bool, arg3 *api.QueryOptions) ([]*api.ServiceEntry, *api.QueryMeta, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Service", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].([]*api.ServiceEntry) + ret1, _ := ret[1].(*api.QueryMeta) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Service indicates an expected call of Service. +func (mr *MockservicerMockRecorder) Service(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Service", reflect.TypeOf((*Mockservicer)(nil).Service), arg0, arg1, arg2, arg3) +} + +// Mockquerier is a mock of querier interface. +type Mockquerier struct { + ctrl *gomock.Controller + recorder *MockquerierMockRecorder +} + +// MockquerierMockRecorder is the mock recorder for Mockquerier. +type MockquerierMockRecorder struct { + mock *Mockquerier +} + +// NewMockquerier creates a new mock instance. +func NewMockquerier(ctrl *gomock.Controller) *Mockquerier { + mock := &Mockquerier{ctrl: ctrl} + mock.recorder = &MockquerierMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Mockquerier) EXPECT() *MockquerierMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *Mockquerier) Execute(arg0 string, arg1 *api.QueryOptions) (*api.PreparedQueryExecuteResponse, *api.QueryMeta, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0, arg1) + ret0, _ := ret[0].(*api.PreparedQueryExecuteResponse) + ret1, _ := ret[1].(*api.QueryMeta) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Execute indicates an expected call of Execute. +func (mr *MockquerierMockRecorder) Execute(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*Mockquerier)(nil).Execute), arg0, arg1) +} diff --git a/internal/mocks/resolverClientConn.go b/internal/mocks/resolverClientConn.go index 12a7dc2..be4061d 100644 --- a/internal/mocks/resolverClientConn.go +++ b/internal/mocks/resolverClientConn.go @@ -5,83 +5,94 @@ package mocks import ( + reflect "reflect" + gomock "github.com/golang/mock/gomock" resolver "google.golang.org/grpc/resolver" serviceconfig "google.golang.org/grpc/serviceconfig" - reflect "reflect" ) -// MockClientConn is a mock of ClientConn interface +// MockClientConn is a mock of ClientConn interface. type MockClientConn struct { ctrl *gomock.Controller recorder *MockClientConnMockRecorder } -// MockClientConnMockRecorder is the mock recorder for MockClientConn +// MockClientConnMockRecorder is the mock recorder for MockClientConn. type MockClientConnMockRecorder struct { mock *MockClientConn } -// NewMockClientConn creates a new mock instance +// NewMockClientConn creates a new mock instance. func NewMockClientConn(ctrl *gomock.Controller) *MockClientConn { mock := &MockClientConn{ctrl: ctrl} mock.recorder = &MockClientConnMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClientConn) EXPECT() *MockClientConnMockRecorder { return m.recorder } -// NewAddress mocks base method +// NewAddress mocks base method. func (m *MockClientConn) NewAddress(arg0 []resolver.Address) { + m.ctrl.T.Helper() m.ctrl.Call(m, "NewAddress", arg0) } -// NewAddress indicates an expected call of NewAddress +// NewAddress indicates an expected call of NewAddress. func (mr *MockClientConnMockRecorder) NewAddress(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAddress", reflect.TypeOf((*MockClientConn)(nil).NewAddress), arg0) } -// NewServiceConfig mocks base method +// NewServiceConfig mocks base method. func (m *MockClientConn) NewServiceConfig(arg0 string) { + m.ctrl.T.Helper() m.ctrl.Call(m, "NewServiceConfig", arg0) } -// NewServiceConfig indicates an expected call of NewServiceConfig +// NewServiceConfig indicates an expected call of NewServiceConfig. func (mr *MockClientConnMockRecorder) NewServiceConfig(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewServiceConfig", reflect.TypeOf((*MockClientConn)(nil).NewServiceConfig), arg0) } -// ParseServiceConfig mocks base method +// ParseServiceConfig mocks base method. func (m *MockClientConn) ParseServiceConfig(arg0 string) *serviceconfig.ParseResult { + m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ParseServiceConfig", arg0) ret0, _ := ret[0].(*serviceconfig.ParseResult) return ret0 } -// ParseServiceConfig indicates an expected call of ParseServiceConfig +// ParseServiceConfig indicates an expected call of ParseServiceConfig. func (mr *MockClientConnMockRecorder) ParseServiceConfig(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParseServiceConfig", reflect.TypeOf((*MockClientConn)(nil).ParseServiceConfig), arg0) } -// ReportError mocks base method +// ReportError mocks base method. func (m *MockClientConn) ReportError(arg0 error) { + m.ctrl.T.Helper() m.ctrl.Call(m, "ReportError", arg0) } -// ReportError indicates an expected call of ReportError +// ReportError indicates an expected call of ReportError. func (mr *MockClientConnMockRecorder) ReportError(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportError", reflect.TypeOf((*MockClientConn)(nil).ReportError), arg0) } -// UpdateState mocks base method +// UpdateState mocks base method. func (m *MockClientConn) UpdateState(arg0 resolver.State) { + m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateState", arg0) } -// UpdateState indicates an expected call of UpdateState +// UpdateState indicates an expected call of UpdateState. func (mr *MockClientConnMockRecorder) UpdateState(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateState", reflect.TypeOf((*MockClientConn)(nil).UpdateState), arg0) } diff --git a/internal/mocks/servicer.go b/internal/mocks/servicer.go deleted file mode 100644 index 28c955e..0000000 --- a/internal/mocks/servicer.go +++ /dev/null @@ -1,48 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: consul.go - -// Package mocks is a generated GoMock package. -package mocks - -import ( - gomock "github.com/golang/mock/gomock" - api "github.com/hashicorp/consul/api" - reflect "reflect" -) - -// Mockservicer is a mock of servicer interface -type Mockservicer struct { - ctrl *gomock.Controller - recorder *MockservicerMockRecorder -} - -// MockservicerMockRecorder is the mock recorder for Mockservicer -type MockservicerMockRecorder struct { - mock *Mockservicer -} - -// NewMockservicer creates a new mock instance -func NewMockservicer(ctrl *gomock.Controller) *Mockservicer { - mock := &Mockservicer{ctrl: ctrl} - mock.recorder = &MockservicerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *Mockservicer) EXPECT() *MockservicerMockRecorder { - return m.recorder -} - -// Service mocks base method -func (m *Mockservicer) Service(arg0, arg1 string, arg2 bool, arg3 *api.QueryOptions) ([]*api.ServiceEntry, *api.QueryMeta, error) { - ret := m.ctrl.Call(m, "Service", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*api.ServiceEntry) - ret1, _ := ret[1].(*api.QueryMeta) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Service indicates an expected call of Service -func (mr *MockservicerMockRecorder) Service(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Service", reflect.TypeOf((*Mockservicer)(nil).Service), arg0, arg1, arg2, arg3) -} diff --git a/target.go b/target.go index 703bbad..ae3f53a 100644 --- a/target.go +++ b/target.go @@ -12,14 +12,23 @@ import ( "github.com/pkg/errors" ) +const ( + targetTypeService = "service" + targetTypePreparedQuery = "prepared_query" + + defaultPollInterval = 30 * time.Second +) + type target struct { + Type string `form:"type"` Addr string `form:"-"` User string `form:"-"` Password string `form:"-"` - Service string `form:"-"` + Target string `form:"-"` Wait time.Duration `form:"wait"` Timeout time.Duration `form:"timeout"` MaxBackoff time.Duration `form:"max-backoff"` + PollInterval time.Duration `form:"poll-interval"` Tag string `form:"tag"` Near string `form:"near"` Limit int `form:"limit"` @@ -34,10 +43,11 @@ type target struct { } func (t *target) String() string { - return fmt.Sprintf("service='%s' healthy='%t' tag='%s'", t.Service, t.Healthy, t.Tag) + return fmt.Sprintf("%s='%s' healthy='%t' tag='%s'", t.Type, t.Target, t.Healthy, t.Tag) } -// parseURL with parameters +// parseURL with parameters +// // see README.md for the actual format // URL schema will stay stable in the future for backward compatibility func parseURL(u string) (target, error) { @@ -56,7 +66,7 @@ func parseURL(u string) (target, error) { tgt.User = rawURL.User.Username() tgt.Password, _ = rawURL.User.Password() tgt.Addr = rawURL.Host - tgt.Service = strings.TrimLeft(rawURL.Path, "/") + tgt.Target = strings.TrimLeft(rawURL.Path, "/") decoder := form.NewDecoder() decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) { return time.ParseDuration(vals[0]) @@ -66,12 +76,33 @@ func parseURL(u string) (target, error) { if err != nil { return target{}, errors.Wrap(err, "Malformed URL parameters") } + if tgt.Type == "" { + tgt.Type = targetTypeService + } + if tgt.Type != targetTypeService && tgt.Type != targetTypePreparedQuery { + return target{}, errors.Errorf(`"type" must be either %q or %q`, targetTypeService, targetTypePreparedQuery) + } + if tgt.Type == targetTypeService && tgt.PollInterval > 0 { + return target{}, errors.Errorf(`"poll-interval" can only be set when type=%q`, targetTypePreparedQuery) + } + if tgt.Type == targetTypePreparedQuery && tgt.Wait > 0 { + return target{}, errors.Errorf(`"wait" can only be set when type=%q`, targetTypeService) + } + if tgt.Type == targetTypePreparedQuery && tgt.Healthy { + return target{}, errors.Errorf(`"healthy" can only be set when type=%q`, targetTypeService) + } + if tgt.Type == targetTypePreparedQuery && tgt.Tag != "" { + return target{}, errors.Errorf(`"tag" can only be set when type=%q`, targetTypeService) + } if len(tgt.Near) == 0 { tgt.Near = "_agent" } if tgt.MaxBackoff == 0 { tgt.MaxBackoff = time.Second } + if tgt.Type == targetTypePreparedQuery && tgt.PollInterval == 0 { + tgt.PollInterval = defaultPollInterval + } return tgt, nil } diff --git a/target_test.go b/target_test.go index 8fd447c..3764e6a 100644 --- a/target_test.go +++ b/target_test.go @@ -17,8 +17,9 @@ func Test_parseURL(t *testing.T) { }{ {"simple", "consul://127.0.0.127:8555/my-service", target{ + Type: "service", Addr: "127.0.0.127:8555", - Service: "my-service", + Target: "my-service", Near: "_agent", MaxBackoff: time.Second, }, @@ -26,10 +27,11 @@ func Test_parseURL(t *testing.T) { }, {"all-args", "consul://user:password@127.0.0.127:8555/my-service?wait=14s&near=host&insecure=true&limit=1&tag=production&token=test_token&max-backoff=2s&dc=xx&allow-stale=true&require-consistent=true", target{ + Type: "service", Addr: "127.0.0.127:8555", User: "user", Password: "password", - Service: "my-service", + Target: "my-service", Near: "host", Wait: 14 * time.Second, TLSInsecure: true, @@ -43,6 +45,40 @@ func Test_parseURL(t *testing.T) { }, false, }, + {"prepared-query", "consul://127.0.0.127:8555/my-query?type=prepared_query", + target{ + Type: "prepared_query", + Addr: "127.0.0.127:8555", + Target: "my-query", + Near: "_agent", + MaxBackoff: time.Second, + PollInterval: 30 * time.Second, + }, + false, + }, + {"prepared-query-poll-interval", "consul://127.0.0.127:8555/my-query?type=prepared_query&poll-interval=10s", + target{ + Type: "prepared_query", + Addr: "127.0.0.127:8555", + Target: "my-query", + Near: "_agent", + MaxBackoff: time.Second, + PollInterval: 10 * time.Second, + }, + false, + }, + {"prepared-query-with-wait", "consul://127.0.0.127:8555/my-query?type=prepared_query&wait=10s", + target{}, + true, + }, + {"prepared-query-with-tag", "consul://127.0.0.127:8555/my-query?type=prepared_query&tag=my-tag", + target{}, + true, + }, + {"prepared-query-with-healthy", "consul://127.0.0.127:8555/my-query?type=prepared_query&healthy=true", + target{}, + true, + }, {"bad-scheme", "127.0.0.127:8555/my-service", target{}, true,