/
main.go
183 lines (152 loc) · 4.06 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
182
183
package main
import (
"fmt"
"log"
"net/url"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/codegangsta/cli"
"github.com/coreos/go-semver/semver"
"github.com/fatih/color"
"github.com/octoblu/register-traefik/etcd"
"github.com/octoblu/register-traefik/healthchecker"
De "github.com/tj/go-debug"
)
var debug = De.Debug("register-traefik:main")
func main() {
app := cli.NewApp()
app.Name = "register-traefik"
app.Version = version()
app.Action = run
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "etcd-uri, e",
EnvVar: "REGISTER_TRAEFIK_ETCD_URI",
Usage: "Etcd URI to register traefik server to",
},
cli.StringFlag{
Name: "server-key, s",
EnvVar: "REGISTER_TRAEFIK_SERVER_KEY",
Usage: "Etcd key to register traefik server to",
},
cli.StringFlag{
Name: "uri, u",
EnvVar: "REGISTER_TRAEFIK_URI",
Usage: "URI to healthcheck, must return status 200",
},
}
app.Run(os.Args)
}
func run(context *cli.Context) {
etcdURI, serverKey, uri := getOpts(context)
control := make(chan bool)
go loop(etcdURI, serverKey, uri, control)
go iterate(control)
sigTerm := make(chan os.Signal)
signal.Notify(sigTerm, syscall.SIGTERM)
sigHup := make(chan os.Signal)
signal.Notify(sigHup, syscall.SIGHUP)
go func() {
for {
<-sigHup
fmt.Println("SIGHUP received, deregistering")
control <- false
onNotHealthy(etcdURI, serverKey)
fmt.Println("deregistered, paused for 5 seconds")
time.Sleep(5 * time.Second)
go loop(etcdURI, serverKey, uri, control)
}
}()
<-sigTerm
fmt.Println("SIGTERM received, cleaning up")
control <- false
onNotHealthy(etcdURI, serverKey)
os.Exit(0)
}
func loop(etcdURI, serverKey, uri string, control <-chan bool) {
for {
if !<-control {
return
}
healthcheck(etcdURI, serverKey, uri)
}
}
func iterate(control chan<- bool) {
for {
control <- true
time.Sleep(5 * time.Second)
}
}
func healthcheck(etcdURI, serverKey, uri string) {
if healthchecker.Healthy(fmt.Sprintf("%v/healthcheck", uri)) {
onHealthy(etcdURI, serverKey, uri)
} else {
onNotHealthy(etcdURI, serverKey)
}
}
func onHealthy(etcdURI, serverKey, uri string) {
debug("onHealthy")
var err error
urlKey := fmt.Sprintf("%v/url", serverKey)
weightKey := fmt.Sprintf("%v/weight", serverKey)
err = etcd.Set(etcdURI, urlKey, uri)
FatalIfError("etcdclient.Set urlKey", err)
err = etcd.Set(etcdURI, weightKey, "10")
FatalIfError("etcdclient.Set weightKey", err)
err = etcd.UpdateDirWithTTL(etcdURI, serverKey, 10)
FatalIfError("etcdclient.UpdateDirWithTTL", err)
}
func onNotHealthy(etcdURI, serverKey string) {
debug("onNotHealthy")
err := etcd.DelDir(etcdURI, serverKey)
FatalIfError("etcd.DelDir", err)
}
func getOpts(context *cli.Context) (string, string, string) {
etcdURI := context.String("etcd-uri")
serverKey := context.String("server-key")
uri := context.String("uri")
if etcdURI == "" || serverKey == "" || uri == "" {
cli.ShowAppHelp(context)
if etcdURI == "" {
color.Red(" Missing required flag --etcd-uri or REGISTER_TRAEFIK_ETCD_URI")
}
if serverKey == "" {
color.Red(" Missing required flag --server-key or REGISTER_TRAEFIK_SERVER_KEY")
}
if uri == "" {
color.Red(" Missing required flag --uri or REGISTER_TRAEFIK_URI")
}
os.Exit(1)
}
validateURI(uri)
return etcdURI, serverKey, uri
}
func validateURI(uriStr string) {
uri, err := url.Parse(uriStr)
FatalIfError(fmt.Sprintf("Failed to parse uri: %v", uriStr), err)
if uri.Scheme != "http" && uri.Scheme != "https" {
log.Fatalf("uri protocol must be one of http/https: %v\n", uriStr)
}
parts := strings.Split(uri.Host, ":")
if len(parts) != 2 || parts[1] == "" {
log.Fatalf("uri must contain a port: %v\n", uriStr)
}
}
func version() string {
version, err := semver.NewVersion(VERSION)
if err != nil {
errorMessage := fmt.Sprintf("Error with version number: %v", VERSION)
log.Fatalln(errorMessage, err.Error())
}
return version.String()
}
// FatalIfError prints error and dies if error is non nil
func FatalIfError(msg string, err error) {
if err == nil {
return
}
log.Fatalf("ERROR(%v):\n\n%v", msg, err)
}