forked from newrelic/sidecar
-
Notifications
You must be signed in to change notification settings - Fork 7
/
static_discovery.go
155 lines (135 loc) · 3.85 KB
/
static_discovery.go
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
package discovery
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/relistan/go-director"
log "github.com/sirupsen/logrus"
"github.com/Nitro/sidecar/service"
)
type Target struct {
Service service.Service
Check StaticCheck
ListenPort int64
}
type StaticDiscovery struct {
Targets []*Target
ConfigFile string
Hostname string
DefaultIP string
}
type StaticCheck struct {
Type string
Args string
}
func NewStaticDiscovery(filename string, defaultIP string) *StaticDiscovery {
hostname, err := os.Hostname()
if err != nil {
log.Errorf("Error getting hostname! %s", err.Error())
}
return &StaticDiscovery{
ConfigFile: filename,
Hostname: hostname,
DefaultIP: defaultIP,
}
}
func (d *StaticDiscovery) HealthCheck(svc *service.Service) (string, string) {
for _, target := range d.Targets {
if svc.ID == target.Service.ID {
return target.Check.Type, target.Check.Args
}
}
return "", ""
}
// Returns the list of services derived from the targets that were parsed
// out of the config file.
func (d *StaticDiscovery) Services() []service.Service {
var services []service.Service
for _, target := range d.Targets {
target.Service.Updated = time.Now().UTC()
services = append(services, target.Service)
}
return services
}
// Listeners returns the list of services configured to be ChangeEvent listeners
func (d *StaticDiscovery) Listeners() []ChangeListener {
var listeners []ChangeListener
for _, target := range d.Targets {
if target.ListenPort > 0 {
listener := ChangeListener{
Name: target.Service.ListenerName(),
Url: fmt.Sprintf("http://%s:%d/update", d.Hostname, target.ListenPort),
}
listeners = append(listeners, listener)
}
}
return listeners
}
// Causes the configuration to be parsed and loaded. There is no background
// processing needed on an ongoing basis.
func (d *StaticDiscovery) Run(looper director.Looper) {
var err error
d.Targets, err = d.ParseConfig(d.ConfigFile)
if err != nil {
log.Errorf("StaticDiscovery cannot parse: %s", err.Error())
}
}
// Parses a JSON config file containing an array of Targets. These are
// then augmented with a random hex ID and stamped with the current
// UTC time as the creation time. The same hex ID is applied to the Check
// and the Service to make sure that they are matched by the healthy
// package later on.
func (d *StaticDiscovery) ParseConfig(filename string) ([]*Target, error) {
file, err := ioutil.ReadFile(filename)
if err != nil {
log.Errorf("Unable to read announcements file: '%s!'", err.Error())
return nil, err
}
var targets []*Target
err = json.Unmarshal(file, &targets)
if err != nil {
return nil, fmt.Errorf("Unable to unmarshal Target: %s", err)
}
// Have to loop with traditional 'for' loop so we can modify entries
for _, target := range targets {
idBytes, err := RandomHex(6)
if err != nil {
log.Errorf("ParseConfig(): Unable to get random bytes (%s)", err.Error())
return nil, err
}
target.Service.ID = string(idBytes)
target.Service.Created = time.Now().UTC()
// We _can_ export services for a 3rd party. If we don't specify
// the hostname, then it's for this host.
if target.Service.Hostname == "" {
target.Service.Hostname = d.Hostname
}
// Make sure we have an IP address on ports
for i, port := range target.Service.Ports {
if len(port.IP) == 0 {
target.Service.Ports[i].IP = d.DefaultIP
}
}
log.Printf("Discovered service: %s, ID: %s",
target.Service.Name,
target.Service.ID,
)
}
return targets, nil
}
// Return a defined number of random bytes as a slice
func RandomHex(count int) ([]byte, error) {
raw := make([]byte, count)
_, err := rand.Read(raw)
if err != nil {
log.Errorf("RandomBytes(): Error %s", err.Error())
return nil, err
}
encoded := make([]byte, count*2)
hex.Encode(encoded, raw)
return encoded, nil
}