-
Notifications
You must be signed in to change notification settings - Fork 624
Expand file tree
/
Copy pathregister.go
More file actions
174 lines (155 loc) · 4.88 KB
/
Copy pathregister.go
File metadata and controls
174 lines (155 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package consul
import (
"errors"
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"time"
"github.com/fabiolb/fabio/config"
"github.com/hashicorp/consul/api"
)
const (
TTLInterval = time.Second * 15
TTLRefreshInterval = time.Second * 10
TTLDeregisterCriticalServiceAfter = time.Minute
)
// register keeps a service registered in consul.
//
// When a value is sent in the dereg channel the service is deregistered from
// consul. To wait for completion the caller should read the next value from
// the dereg channel.
//
// dereg <- true // trigger deregistration
// <-dereg // wait for completion
func register(c *api.Client, service *api.AgentServiceRegistration) chan bool {
registered := func(serviceID string) bool {
if serviceID == "" {
return false
}
services, err := c.Agent().Services()
if err != nil {
log.Printf("[ERROR] consul: Cannot get service list. %s", err)
return false
}
return services[serviceID] != nil
}
register := func() string {
if err := c.Agent().ServiceRegister(service); err != nil {
log.Printf("[ERROR] consul: Cannot register fabio [name:%q] in Consul. %s", service.Name, err)
return ""
}
log.Printf("[INFO] consul: Registered fabio as %q", service.Name)
log.Printf("[INFO] consul: Registered fabio with id %q", service.ID)
log.Printf("[INFO] consul: Registered fabio with address %q", service.Address)
log.Printf("[INFO] consul: Registered fabio with tags %q", strings.Join(service.Tags, ","))
for _, check := range service.Checks {
log.Printf("[INFO] consul: Registered fabio with check %+v", check)
}
return service.ID
}
deregister := func(serviceID string) {
log.Printf("[INFO] consul: Deregistering %q", service.Name)
c.Agent().ServiceDeregister(serviceID)
}
passTTL := func(serviceTTLID string) {
c.Agent().UpdateTTL(serviceTTLID, "", api.HealthPassing)
}
dereg := make(chan bool)
go func() {
var serviceID string
var serviceTTLCheckId string
for {
if !registered(serviceID) {
serviceID = register()
serviceTTLCheckId = computeServiceTTLCheckId(serviceID)
// Pass the TTL check right now so traffic can be served immediately.
passTTL(serviceTTLCheckId)
}
select {
case <-dereg:
deregister(serviceID)
dereg <- true
return
case <-time.After(TTLRefreshInterval):
// Reset the TTL check clock.
passTTL(serviceTTLCheckId)
}
}
}()
return dereg
}
func serviceRegistration(cfg *config.Consul, serviceName string) (*api.AgentServiceRegistration, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
ipstr, portstr, err := net.SplitHostPort(cfg.ServiceAddr)
if err != nil {
return nil, err
}
port, err := strconv.Atoi(portstr)
if err != nil {
return nil, err
}
ip := net.ParseIP(ipstr)
if ip == nil {
ip, err = config.LocalIP()
if err != nil {
return nil, err
}
if ip == nil {
return nil, errors.New("no local ip")
}
}
serviceID := fmt.Sprintf("%s-%s-%d", serviceName, hostname, port)
checkURL := fmt.Sprintf("%s://%s:%d/health", cfg.CheckScheme, ip, port)
if ip.To4() == nil {
checkURL = fmt.Sprintf("%s://[%s]:%d/health", cfg.CheckScheme, ip, port)
}
service := &api.AgentServiceRegistration{
ID: serviceID,
Name: serviceName,
Address: ip.String(),
Port: port,
Tags: cfg.ServiceTags,
Namespace: cfg.Namespace,
// Set the checks for the service.
//
// Both checks must pass for Consul to consider the service healthy and therefore serve the fabio instance to clients.
Checks: []*api.AgentServiceCheck{
// If fabio doesn't exit cleanly, it doesn't auto-deregister itself
// from Consul. In order to address this, we introduce a TTL check
// to confirm that the fabio instance is alive and able to route
// this service.
//
// The TTL check must be refreshed before its timeout is crossed,
// otherwise the check fails. If the check fails, Consul considers
// this service to have become unhealthy. If the check is failing
// (critical) for the DeregisterCriticalServiceAfter duration, the
// Consul reaper will remove it from Consul.
//
// For more info, read https://www.consul.io/api/agent/check.html#deregistercriticalserviceafter.
{
CheckID: computeServiceTTLCheckId(serviceID),
TTL: TTLInterval.String(),
DeregisterCriticalServiceAfter: TTLDeregisterCriticalServiceAfter.String(),
},
// HTTP check is meant to confirm fabio health endpoint is
// reachable from the Consul agent. If the check fails, Consul
// considers this service to have become unhealthy.
{
HTTP: checkURL,
Interval: cfg.CheckInterval.String(),
Timeout: cfg.CheckTimeout.String(),
TLSSkipVerify: cfg.CheckTLSSkipVerify,
},
},
}
return service, nil
}
func computeServiceTTLCheckId(serviceID string) string {
return serviceID + "-ttl"
}