Initial PR tidy-up based on feedback
Martin Baillie
6 years ago
0 | 0 | package eureka |
1 | 1 | |
2 | 2 | import ( |
3 | stdeureka "github.com/hudl/fargo" | |
4 | stdeurekalogging "github.com/op/go-logging" | |
3 | fargo "github.com/hudl/fargo" | |
5 | 4 | ) |
6 | ||
7 | func init() { | |
8 | // Quieten Fargo's own logging | |
9 | stdeurekalogging.SetLevel(stdeurekalogging.ERROR, "fargo") | |
10 | } | |
11 | 5 | |
12 | 6 | // Client is a wrapper around the Eureka API. |
13 | 7 | type Client interface { |
14 | 8 | // Register an instance with Eureka. |
15 | Register(i *stdeureka.Instance) error | |
9 | Register(i *fargo.Instance) error | |
16 | 10 | |
17 | 11 | // Deregister an instance from Eureka. |
18 | Deregister(i *stdeureka.Instance) error | |
12 | Deregister(i *fargo.Instance) error | |
19 | 13 | |
20 | 14 | // Send an instance heartbeat to Eureka. |
21 | Heartbeat(i *stdeureka.Instance) error | |
15 | Heartbeat(i *fargo.Instance) error | |
22 | 16 | |
23 | 17 | // Get all instances for an app in Eureka. |
24 | Instances(app string) ([]*stdeureka.Instance, error) | |
18 | Instances(app string) ([]*fargo.Instance, error) | |
25 | 19 | |
26 | 20 | // Receive scheduled updates about an app's instances in Eureka. |
27 | ScheduleUpdates(app string, quitc chan struct{}) <-chan stdeureka.AppUpdate | |
21 | ScheduleUpdates(app string, quitc chan struct{}) <-chan fargo.AppUpdate | |
28 | 22 | } |
29 | 23 | |
30 | 24 | type client struct { |
31 | connection *stdeureka.EurekaConnection | |
25 | connection *fargo.EurekaConnection | |
32 | 26 | } |
33 | 27 | |
34 | 28 | // NewClient returns an implementation of the Client interface, wrapping a |
35 | 29 | // concrete connection to Eureka using the Fargo library. |
36 | 30 | // Taking in Fargo's own connection abstraction gives the user maximum |
37 | 31 | // freedom in regards to how that connection is configured. |
38 | func NewClient(ec *stdeureka.EurekaConnection) Client { | |
32 | func NewClient(ec *fargo.EurekaConnection) Client { | |
39 | 33 | return &client{connection: ec} |
40 | 34 | } |
41 | 35 | |
42 | func (c *client) Register(i *stdeureka.Instance) error { | |
36 | func (c *client) Register(i *fargo.Instance) error { | |
43 | 37 | if c.instanceRegistered(i) { |
44 | 38 | // Already registered. Send a heartbeat instead. |
45 | 39 | return c.Heartbeat(i) |
47 | 41 | return c.connection.RegisterInstance(i) |
48 | 42 | } |
49 | 43 | |
50 | func (c *client) Deregister(i *stdeureka.Instance) error { | |
44 | func (c *client) Deregister(i *fargo.Instance) error { | |
51 | 45 | return c.connection.DeregisterInstance(i) |
52 | 46 | } |
53 | 47 | |
54 | func (c *client) Heartbeat(i *stdeureka.Instance) (err error) { | |
48 | func (c *client) Heartbeat(i *fargo.Instance) (err error) { | |
55 | 49 | if err = c.connection.HeartBeatInstance(i); err != nil && c.instanceNotFoundErr(err) { |
56 | 50 | // Instance not registered. Register first before sending heartbeats. |
57 | 51 | return c.Register(i) |
59 | 53 | return err |
60 | 54 | } |
61 | 55 | |
62 | func (c *client) Instances(app string) ([]*stdeureka.Instance, error) { | |
56 | func (c *client) Instances(app string) ([]*fargo.Instance, error) { | |
63 | 57 | stdApp, err := c.connection.GetApp(app) |
64 | 58 | if err != nil { |
65 | 59 | return nil, err |
67 | 61 | return stdApp.Instances, nil |
68 | 62 | } |
69 | 63 | |
70 | func (c *client) ScheduleUpdates(app string, quitc chan struct{}) <-chan stdeureka.AppUpdate { | |
64 | func (c *client) ScheduleUpdates(app string, quitc chan struct{}) <-chan fargo.AppUpdate { | |
71 | 65 | return c.connection.ScheduleAppUpdates(app, false, quitc) |
72 | 66 | } |
73 | 67 | |
74 | func (c *client) instanceRegistered(i *stdeureka.Instance) bool { | |
68 | func (c *client) instanceRegistered(i *fargo.Instance) bool { | |
75 | 69 | _, err := c.connection.GetInstance(i.App, i.Id()) |
76 | 70 | return err == nil |
77 | 71 | } |
78 | 72 | |
79 | 73 | func (c *client) instanceNotFoundErr(err error) bool { |
80 | code, ok := stdeureka.HTTPResponseStatusCode(err) | |
74 | code, ok := fargo.HTTPResponseStatusCode(err) | |
81 | 75 | return ok && code == 404 |
82 | 76 | } |
3 | 3 | "errors" |
4 | 4 | "reflect" |
5 | 5 | |
6 | fargo "github.com/hudl/fargo" | |
7 | ||
6 | 8 | "github.com/go-kit/kit/log" |
7 | stdeureka "github.com/hudl/fargo" | |
8 | 9 | ) |
9 | 10 | |
10 | 11 | var ( |
11 | 12 | errTest = errors.New("kaboom") |
12 | 13 | loggerTest = log.NewNopLogger() |
13 | instanceTest1 = &stdeureka.Instance{ | |
14 | instanceTest1 = &fargo.Instance{ | |
14 | 15 | HostName: "server1.acme.org", |
15 | 16 | Port: 8080, |
16 | 17 | App: "go-kit", |
20 | 21 | HealthCheckUrl: "http://server1.acme.org:8080/healthz", |
21 | 22 | StatusPageUrl: "http://server1.acme.org:8080/status", |
22 | 23 | HomePageUrl: "http://server1.acme.org:8080/", |
23 | Status: stdeureka.UP, | |
24 | DataCenterInfo: stdeureka.DataCenterInfo{Name: stdeureka.MyOwn}, | |
25 | LeaseInfo: stdeureka.LeaseInfo{RenewalIntervalInSecs: 1}, | |
24 | Status: fargo.UP, | |
25 | DataCenterInfo: fargo.DataCenterInfo{Name: fargo.MyOwn}, | |
26 | LeaseInfo: fargo.LeaseInfo{RenewalIntervalInSecs: 1}, | |
26 | 27 | } |
27 | instanceTest2 = &stdeureka.Instance{ | |
28 | instanceTest2 = &fargo.Instance{ | |
28 | 29 | HostName: "server2.acme.org", |
29 | 30 | Port: 8080, |
30 | 31 | App: "go-kit", |
34 | 35 | HealthCheckUrl: "http://server2.acme.org:8080/healthz", |
35 | 36 | StatusPageUrl: "http://server2.acme.org:8080/status", |
36 | 37 | HomePageUrl: "http://server2.acme.org:8080/", |
37 | Status: stdeureka.UP, | |
38 | DataCenterInfo: stdeureka.DataCenterInfo{Name: stdeureka.MyOwn}, | |
38 | Status: fargo.UP, | |
39 | DataCenterInfo: fargo.DataCenterInfo{Name: fargo.MyOwn}, | |
39 | 40 | } |
40 | applicationTest = &stdeureka.Application{ | |
41 | applicationTest = &fargo.Application{ | |
41 | 42 | Name: "go-kit", |
42 | Instances: []*stdeureka.Instance{instanceTest1, instanceTest2}, | |
43 | Instances: []*fargo.Instance{instanceTest1, instanceTest2}, | |
43 | 44 | } |
44 | 45 | ) |
45 | 46 | |
46 | 47 | type testClient struct { |
47 | instances []*stdeureka.Instance | |
48 | application *stdeureka.Application | |
48 | instances []*fargo.Instance | |
49 | application *fargo.Application | |
49 | 50 | errInstances error |
50 | 51 | errApplication error |
51 | 52 | errHeartbeat error |
52 | 53 | } |
53 | 54 | |
54 | func (c *testClient) Register(i *stdeureka.Instance) error { | |
55 | func (c *testClient) Register(i *fargo.Instance) error { | |
55 | 56 | for _, instance := range c.instances { |
56 | 57 | if reflect.DeepEqual(*instance, *i) { |
57 | 58 | return errors.New("already registered") |
62 | 63 | return nil |
63 | 64 | } |
64 | 65 | |
65 | func (c *testClient) Deregister(i *stdeureka.Instance) error { | |
66 | var newInstances []*stdeureka.Instance | |
66 | func (c *testClient) Deregister(i *fargo.Instance) error { | |
67 | var newInstances []*fargo.Instance | |
67 | 68 | for _, instance := range c.instances { |
68 | 69 | if reflect.DeepEqual(*instance, *i) { |
69 | 70 | continue |
78 | 79 | return nil |
79 | 80 | } |
80 | 81 | |
81 | func (c *testClient) Heartbeat(i *stdeureka.Instance) (err error) { | |
82 | func (c *testClient) Heartbeat(i *fargo.Instance) (err error) { | |
82 | 83 | return c.errHeartbeat |
83 | 84 | } |
84 | 85 | |
85 | func (c *testClient) Instances(app string) ([]*stdeureka.Instance, error) { | |
86 | func (c *testClient) Instances(app string) ([]*fargo.Instance, error) { | |
86 | 87 | return c.instances, c.errInstances |
87 | 88 | } |
88 | 89 | |
89 | func (c *testClient) ScheduleUpdates(service string, quitc chan struct{}) <-chan stdeureka.AppUpdate { | |
90 | updatec := make(chan stdeureka.AppUpdate, 1) | |
91 | updatec <- stdeureka.AppUpdate{App: c.application, Err: c.errApplication} | |
90 | func (c *testClient) ScheduleUpdates(service string, quitc chan struct{}) <-chan fargo.AppUpdate { | |
91 | updatec := make(chan fargo.AppUpdate, 1) | |
92 | updatec <- fargo.AppUpdate{App: c.application, Err: c.errApplication} | |
92 | 93 | return updatec |
93 | 94 | } |
7 | 7 | "testing" |
8 | 8 | "time" |
9 | 9 | |
10 | fargo "github.com/hudl/fargo" | |
11 | ||
10 | 12 | "github.com/go-kit/kit/endpoint" |
11 | 13 | "github.com/go-kit/kit/log" |
12 | stdeureka "github.com/hudl/fargo" | |
13 | 14 | ) |
14 | 15 | |
15 | 16 | // Package sd/eureka provides a wrapper around the Netflix Eureka service |
29 | 30 | |
30 | 31 | var client Client |
31 | 32 | { |
32 | var stdConfig stdeureka.Config | |
33 | stdConfig.Eureka.ServiceUrls = []string{eurekaAddr} | |
34 | stdConfig.Eureka.PollIntervalSeconds = 1 | |
33 | var fargoConfig fargo.Config | |
34 | fargoConfig.Eureka.ServiceUrls = []string{eurekaAddr} | |
35 | fargoConfig.Eureka.PollIntervalSeconds = 1 | |
35 | 36 | |
36 | stdConnection := stdeureka.NewConnFromConfig(stdConfig) | |
37 | client = NewClient(&stdConnection) | |
37 | fargoConnection := fargo.NewConnFromConfig(fargoConfig) | |
38 | client = NewClient(&fargoConnection) | |
38 | 39 | } |
39 | 40 | |
40 | 41 | logger := log.NewLogfmtLogger(os.Stderr) |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "fmt" |
4 | "sync" | |
5 | 4 | "time" |
6 | 5 | |
7 | stdeureka "github.com/hudl/fargo" | |
6 | fargo "github.com/hudl/fargo" | |
8 | 7 | |
9 | 8 | "github.com/go-kit/kit/log" |
10 | 9 | ) |
12 | 11 | // Registrar maintains service instance liveness information in Eureka. |
13 | 12 | type Registrar struct { |
14 | 13 | client Client |
15 | instance *stdeureka.Instance | |
14 | instance *fargo.Instance | |
16 | 15 | logger log.Logger |
17 | ||
18 | quitmtx sync.Mutex | |
19 | quit chan bool | |
16 | quit chan bool | |
20 | 17 | } |
21 | 18 | |
22 | 19 | // NewRegistrar returns an Eureka Registrar acting on behalf of the provided |
23 | 20 | // Fargo instance. |
24 | func NewRegistrar(client Client, i *stdeureka.Instance, l log.Logger) *Registrar { | |
21 | func NewRegistrar(client Client, i *fargo.Instance, logger log.Logger) *Registrar { | |
25 | 22 | return &Registrar{ |
26 | 23 | client: client, |
27 | 24 | instance: i, |
28 | logger: log.With(l, "service", i.App, "address", fmt.Sprintf("%s:%d", i.IPAddr, i.Port)), | |
25 | logger: log.With(logger, "service", i.App, "address", fmt.Sprintf("%s:%d", i.IPAddr, i.Port)), | |
29 | 26 | } |
30 | 27 | } |
31 | 28 | |
39 | 36 | |
40 | 37 | if r.instance.LeaseInfo.RenewalIntervalInSecs > 0 { |
41 | 38 | // User has opted for heartbeat functionality in Eureka. |
42 | go r.loop() | |
39 | if r.quit == nil { | |
40 | r.quit = make(chan bool) | |
41 | go r.loop() | |
42 | } | |
43 | 43 | } |
44 | 44 | } |
45 | 45 | |
51 | 51 | r.logger.Log("action", "deregister") |
52 | 52 | } |
53 | 53 | |
54 | r.quitmtx.Lock() | |
55 | defer r.quitmtx.Unlock() | |
56 | 54 | if r.quit != nil { |
57 | 55 | r.quit <- true |
56 | r.quit = nil | |
58 | 57 | } |
59 | 58 | } |
60 | 59 | |
61 | 60 | func (r *Registrar) loop() { |
62 | r.quitmtx.Lock() | |
63 | if r.quit != nil { | |
64 | defer r.quitmtx.Unlock() | |
65 | return // Already running. | |
66 | } | |
67 | r.quit = make(chan bool) | |
68 | r.quitmtx.Unlock() | |
69 | ||
70 | 61 | tick := time.NewTicker(time.Duration(r.instance.LeaseInfo.RenewalIntervalInSecs) * time.Second) |
71 | 62 | defer tick.Stop() |
72 | 63 | for { |
76 | 67 | r.logger.Log("err", err) |
77 | 68 | } |
78 | 69 | case <-r.quit: |
79 | r.quitmtx.Lock() | |
80 | defer r.quitmtx.Unlock() | |
81 | ||
82 | close(r.quit) | |
83 | r.quit = nil | |
84 | ||
85 | 70 | return |
86 | 71 | } |
87 | 72 | } |
3 | 3 | "testing" |
4 | 4 | "time" |
5 | 5 | |
6 | stdeureka "github.com/hudl/fargo" | |
6 | fargo "github.com/hudl/fargo" | |
7 | 7 | ) |
8 | 8 | |
9 | 9 | func TestRegistrar(t *testing.T) { |
10 | 10 | client := &testClient{ |
11 | instances: []*stdeureka.Instance{}, | |
11 | instances: []*fargo.Instance{}, | |
12 | 12 | errHeartbeat: errTest, |
13 | 13 | } |
14 | 14 |
2 | 2 | import ( |
3 | 3 | "fmt" |
4 | 4 | |
5 | stdeureka "github.com/hudl/fargo" | |
5 | fargo "github.com/hudl/fargo" | |
6 | 6 | |
7 | 7 | "github.com/go-kit/kit/endpoint" |
8 | 8 | "github.com/go-kit/kit/log" |
24 | 24 | |
25 | 25 | // NewSubscriber returns a Eureka subscriber. It will start watching the given |
26 | 26 | // app string for changes, and update the endpoints accordingly. |
27 | func NewSubscriber(c Client, f sd.Factory, l log.Logger, app string) *Subscriber { | |
27 | func NewSubscriber(c Client, factory sd.Factory, logger log.Logger, app string) *Subscriber { | |
28 | 28 | s := &Subscriber{ |
29 | 29 | client: c, |
30 | cache: cache.New(f, l), | |
30 | cache: cache.New(factory, logger), | |
31 | 31 | app: app, |
32 | logger: l, | |
32 | logger: logger, | |
33 | 33 | quitc: make(chan struct{}), |
34 | 34 | } |
35 | 35 | |
46 | 46 | } |
47 | 47 | |
48 | 48 | func (s *Subscriber) getInstances() ([]string, error) { |
49 | stdInstances, err := s.client.Instances(s.app) | |
49 | fargoInstances, err := s.client.Instances(s.app) | |
50 | 50 | if err != nil { |
51 | 51 | return nil, err |
52 | 52 | } |
53 | return convertStdInstances(stdInstances), nil | |
53 | return convertFargoInstances(fargoInstances), nil | |
54 | 54 | } |
55 | 55 | |
56 | 56 | func (s *Subscriber) loop() { |
65 | 65 | continue |
66 | 66 | } |
67 | 67 | |
68 | instances := convertStdApplication(u.App) | |
68 | instances := convertFargoApplication(u.App) | |
69 | 69 | s.logger.Log("app", s.app, "instances", len(instances)) |
70 | 70 | s.cache.Update(instances) |
71 | 71 | } |
82 | 82 | close(s.quitc) |
83 | 83 | } |
84 | 84 | |
85 | func convertStdApplication(stdApplication *stdeureka.Application) (instances []string) { | |
86 | if stdApplication != nil { | |
87 | instances = convertStdInstances(stdApplication.Instances) | |
85 | func convertFargoApplication(fargoApplication *fargo.Application) (instances []string) { | |
86 | if fargoApplication != nil { | |
87 | instances = convertFargoInstances(fargoApplication.Instances) | |
88 | 88 | } |
89 | 89 | return instances |
90 | 90 | } |
91 | 91 | |
92 | func convertStdInstances(stdInstances []*stdeureka.Instance) []string { | |
93 | instances := make([]string, len(stdInstances)) | |
94 | for i, stdInstance := range stdInstances { | |
95 | instances[i] = fmt.Sprintf("%s:%d", stdInstance.IPAddr, stdInstance.Port) | |
92 | func convertFargoInstances(fargoInstances []*fargo.Instance) []string { | |
93 | instances := make([]string, len(fargoInstances)) | |
94 | for i, fargoInstance := range fargoInstances { | |
95 | instances[i] = fmt.Sprintf("%s:%d", fargoInstance.IPAddr, fargoInstance.Port) | |
96 | 96 | } |
97 | 97 | return instances |
98 | 98 | } |
4 | 4 | "testing" |
5 | 5 | "time" |
6 | 6 | |
7 | fargo "github.com/hudl/fargo" | |
8 | ||
7 | 9 | "github.com/go-kit/kit/endpoint" |
8 | stdeureka "github.com/hudl/fargo" | |
9 | 10 | ) |
10 | 11 | |
11 | 12 | func TestSubscriber(t *testing.T) { |
14 | 15 | } |
15 | 16 | |
16 | 17 | client := &testClient{ |
17 | instances: []*stdeureka.Instance{instanceTest1}, | |
18 | instances: []*fargo.Instance{instanceTest1}, | |
18 | 19 | application: applicationTest, |
19 | 20 | errApplication: nil, |
20 | 21 | } |
38 | 39 | } |
39 | 40 | |
40 | 41 | client := &testClient{ |
41 | instances: []*stdeureka.Instance{instanceTest1}, | |
42 | instances: []*fargo.Instance{instanceTest1}, | |
42 | 43 | application: applicationTest, |
43 | 44 | errApplication: nil, |
44 | 45 | } |
65 | 66 | } |
66 | 67 | |
67 | 68 | client := &testClient{ |
68 | instances: []*stdeureka.Instance{instanceTest1}, | |
69 | instances: []*fargo.Instance{instanceTest1}, | |
69 | 70 | } |
70 | 71 | |
71 | 72 | s := NewSubscriber(client, factory, loggerTest, instanceTest1.App) |
111 | 112 | } |
112 | 113 | |
113 | 114 | client := &testClient{ |
114 | instances: []*stdeureka.Instance{instanceTest1}, | |
115 | instances: []*fargo.Instance{instanceTest1}, | |
115 | 116 | application: applicationTest, |
116 | 117 | errApplication: errTest, |
117 | 118 | } |