Codebase list golang-github-go-kit-kit / 33fad1e
Fix Consul SD loop exiting on EOF (#788) * switched io.EOF for quit to errStopped * adding unit test for consul sd EOF Jack Murdock authored 5 years ago Peter Bourgon committed 5 years ago
2 changed file(s) with 79 addition(s) and 5 deletion(s). Raw diff Collapse all Expand all
00 package consul
11
22 import (
3 "errors"
34 "fmt"
4 "io"
55 "time"
66
77 consul "github.com/hashicorp/consul/api"
1313 )
1414
1515 const defaultIndex = 0
16
17 // errStopped notifies the loop to quit. aka stopped via quitc
18 var errStopped = errors.New("quit and closed consul instancer")
1619
1720 // Instancer yields instances for a service in Consul.
1821 type Instancer struct {
6568 for {
6669 instances, lastIndex, err = s.getInstances(lastIndex, s.quitc)
6770 switch {
68 case err == io.EOF:
71 case err == errStopped:
6972 return // stopped via quitc
7073 case err != nil:
7174 s.logger.Log("err", err)
124127 case res := <-resc:
125128 return res.instances, res.index, nil
126129 case <-interruptc:
127 return nil, 0, io.EOF
130 return nil, 0, errStopped
128131 }
129132 }
130133
11
22 import (
33 "context"
4 consul "github.com/hashicorp/consul/api"
5 "io"
46 "testing"
5
6 consul "github.com/hashicorp/consul/api"
7 "time"
78
89 "github.com/go-kit/kit/log"
910 "github.com/go-kit/kit/sd"
130131 t.Errorf("want %q, have %q", want, have)
131132 }
132133 }
134
135 type eofTestClient struct {
136 client *testClient
137 eofSig chan bool
138 called chan struct{}
139 }
140
141 func neweofTestClient(client *testClient, sig chan bool, called chan struct{}) Client {
142 return &eofTestClient{client: client, eofSig: sig, called: called}
143 }
144
145 func (c *eofTestClient) Register(r *consul.AgentServiceRegistration) error {
146 return c.client.Register(r)
147 }
148
149 func (c *eofTestClient) Deregister(r *consul.AgentServiceRegistration) error {
150 return c.client.Deregister(r)
151 }
152
153 func (c *eofTestClient) Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {
154 c.called <- struct{}{}
155 shouldEOF := <-c.eofSig
156 if shouldEOF {
157 return nil, &consul.QueryMeta{}, io.EOF
158 }
159 return c.client.Service(service, tag, passingOnly, queryOpts)
160 }
161
162 func TestInstancerWithEOF(t *testing.T) {
163 var (
164 sig = make(chan bool, 1)
165 called = make(chan struct{}, 1)
166 logger = log.NewNopLogger()
167 client = neweofTestClient(newTestClient(consulState), sig, called)
168 )
169
170 sig <- false
171 s := NewInstancer(client, logger, "search", []string{"api"}, true)
172 defer s.Stop()
173
174 select {
175 case <-called:
176 case <-time.Tick(time.Millisecond * 500):
177 t.Error("failed, to receive call")
178 }
179
180 state := s.cache.State()
181 if want, have := 2, len(state.Instances); want != have {
182 t.Errorf("want %d, have %d", want, have)
183 }
184
185 // some error occurred resulting in io.EOF
186 sig <- true
187
188 // Service Called Once
189 select {
190 case <-called:
191 case <-time.Tick(time.Millisecond * 500):
192 t.Error("failed, to receive call in time")
193 }
194
195 sig <- false
196
197 // loop should continue
198 select {
199 case <-called:
200 case <-time.Tick(time.Millisecond * 500):
201 t.Error("failed, to receive call in time")
202 }
203 }