/
main.go
181 lines (137 loc) · 4.97 KB
/
main.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package main
import (
"log"
"flag"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/fields"
vClient "github.com/vulcand/vulcand/api"
vPlugin "github.com/vulcand/vulcand/plugin"
kClient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
"encoding/json"
"time"
"k8s.io/kubernetes/pkg/runtime"
"github.com/vulcand/vulcand/engine"
"strconv"
)
var (
vServer string
kServer string
namespace string
labelQuery string
)
func init() {
flag.StringVar(&kServer, "kubernetes-api-server", "https://kubernetes.default", "Kubernetes server to proxy.")
flag.StringVar(&vServer, "vulcand-server", "http://docker:8182", "Vulcand server for external loadbalancing.")
flag.StringVar(&labelQuery, "query", "", "Label query for services to expose via the external loadbalancer.")
flag.StringVar(&namespace, "namespace", "default", "Namespace in which to look for services to match")
}
func ensureEndpointConfiguredForVulcand(client *vClient.Client, endpoints api.Endpoints) {
name := endpoints.Name + "." + endpoints.Namespace
backend := engine.Backend{Id: name, Type: "http",
Settings: engine.HTTPBackendSettings{
Timeouts: engine.HTTPBackendTimeouts{Read: "5s", Dial: "5s", TLSHandshake:"10s"},
KeepAlive: engine.HTTPBackendKeepAlive{Period: "30s", MaxIdleConnsPerHost: 12}}}
err := client.UpsertBackend(backend)
if err != nil {
log.Println("Encountered error when creating backend in vulcand: " + err.Error())
}
// add servers to the backend
for _, element := range endpoints.Subsets {
for _, address := range element.Addresses {
url := "http://" + address.IP + ":" + strconv.Itoa(element.Ports[0].Port)
log.Println("Attempting to add server to backend " + backend.GetUniqueId().Id + " using url: " + url)
err = client.UpsertServer(backend.GetUniqueId(), engine.Server{Id: url, URL: url}, time.Second * 5)
if err != nil {
log.Println("Encountered error when adding server to backend: " + err.Error())
}
}
}
// make sure there's a frontend
frontend := engine.Frontend{Id: name, Type: "http", Route: "Path(`/`)", BackendId: name}
err = client.UpsertFrontend(frontend, time.Second * 5)
if err != nil {
log.Println("Encountered error when creating frontend in vulcand: " + err.Error())
}
}
func removeUnusedEndpointsFromVulcand(client *vClient.Client, endpoints api.Endpoints) {
name := endpoints.Name + "." + endpoints.Namespace
backend, err := client.GetBackend(engine.BackendKey{Id: name})
if err != nil {
log.Println("Encountered error when getting backend to remove unused endpoints: " + err.Error())
return
}
servers, err := client.GetServers(engine.BackendKey{Id: name})
if err != nil {
log.Println("Encountered error when getting servers to remove unused endpoints: " + err.Error())
return
}
js, _ := json.Marshal(backend)
log.Println(js)
js, _ = json.Marshal(servers)
log.Println(js)
}
func deserialize(result runtime.Object) (api.Endpoints, error) {
obj, _ := json.Marshal(result)
var endpoints api.Endpoints
err := json.Unmarshal(obj, &endpoints)
if err != nil {
log.Println("Could not unmarshal channel result into endpoint type: \n" + string(obj))
return endpoints, err
}
return endpoints, nil
}
func main() {
flag.Parse()
log.Println("Connecting to kubernetes via url " + kServer)
log.Println("Connecting to vulcand via url " + vServer)
log.Println("Provided label query: " + labelQuery)
log.Println("Observing endpoints within namespace: " + namespace)
vClient := vClient.NewClient(vServer, vPlugin.NewRegistry())
kClient, err := kClient.New(&kClient.Config{Host:kServer})
if err != nil {
log.Println("Error encountered when connecting to kubernetes api." + err.Error())
panic(err)
}
var labelSelector labels.Selector = nil
if labelQuery != "" {
labelSelector, err = labels.Parse(labelQuery)
if err != nil {
log.Println("Error parsing the provided label query.")
panic(err)
}
} else {
labelSelector = labels.Everything()
}
socket, err := kClient.Endpoints(namespace).
Watch(labelSelector, fields.Everything(), api.ListOptions{Watch: true})
if err != nil {
log.Println("Error obtaining a watch on the kubernetes endpoints.")
panic(err)
}
// poll the channel indefinitely
for {
select {
case event := <-socket.ResultChan():
switch event.Type {
case watch.Added:
endpoint, _ := deserialize(event.Object)
ensureEndpointConfiguredForVulcand(vClient, endpoint)
log.Println("Endpoint was added: \n" + endpoint.Name)
case watch.Modified:
endpoint, _ := deserialize(event.Object)
ensureEndpointConfiguredForVulcand(vClient, endpoint)
log.Println("Endpoint was modified: \n" + endpoint.Name)
case watch.Deleted:
endpoint, _ := deserialize(event.Object)
removeUnusedEndpointsFromVulcand(vClient, endpoint)
log.Println("Endpoint was deleted: \n" + endpoint.Name)
case watch.Error:
log.Println("Encountered an error from the endpoints socket. Continuing...")
}
default:
time.Sleep(1 * time.Second)
}
}
}