Graceful exit, and slight restructuring
This commit is contained in:
parent
9ac6dec291
commit
0291404261
28
README.md
28
README.md
|
@ -12,32 +12,8 @@ and placed in whatever directory you will run CFHA from.
|
||||||
|
|
||||||
Example configuration file:
|
Example configuration file:
|
||||||
|
|
||||||
```js
|
(the configuration format is currently in flux, the example will be updated when it is stable. for now just look at `core/config` and build a json object to match `Config`)
|
||||||
{
|
|
||||||
"hosts": [ //an array of hosts to check
|
|
||||||
{
|
|
||||||
"host": "192.168.1.1", //the IP address of the host
|
|
||||||
"type": "http", //the type of check to run, can be either http or https
|
|
||||||
"options": {} //optional additional parameters to the check module
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"host": "192.168.1.2",
|
|
||||||
"type": "https",
|
|
||||||
"options": {
|
|
||||||
"hostname": "lb-check-hostname" //this is the only currently supported parameter, and will be sent as the "host" header of an http(s) request
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cloudflare": { //cloudflare configuration
|
|
||||||
"email": "cfemail@example.com",
|
|
||||||
"apiKey": "CF_API_KEY", //get this from your cloudflare profile
|
|
||||||
"domain": "example.com", //the domain in cloudflare you will be operating on
|
|
||||||
"name": "lb.example.com", //the full dns record you wish to edit
|
|
||||||
"ttl": "1" //1 is automatic, otherwise see the cloudflare documentation on TTL
|
|
||||||
},
|
|
||||||
"interval": 1 //how often (in seconds) to ping the server, note that this is a number, not a string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
===========
|
===========
|
||||||
|
|
|
@ -8,14 +8,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewHttpChecker(config core.CheckCreateConfig) *HttpChecker {
|
func NewHttpChecker(config core.CheckCreateConfig) *core.GenericCheck {
|
||||||
checker := HttpChecker{
|
return core.NewGenericCheck(&HttpChecker{
|
||||||
&http.Client{},
|
&http.Client{},
|
||||||
config,
|
config,
|
||||||
config.Host.Type + "://" + config.Host.Host + "/",
|
config.Host.Type + "://" + config.Host.Host + "/",
|
||||||
}
|
})
|
||||||
go checker.run()
|
|
||||||
return &checker
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpChecker struct {
|
type HttpChecker struct {
|
||||||
|
@ -24,16 +22,16 @@ type HttpChecker struct {
|
||||||
endpoint string
|
endpoint string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HttpChecker) run() {
|
func (this *HttpChecker) Check() time.Duration {
|
||||||
interval := time.Duration(this.config.Interval) * time.Second
|
this.config.Engine.Input<- core.Result{
|
||||||
log.Print(fmt.Sprintf("Starting: %v\n", this.config.Host))
|
this.config.Host.Host,
|
||||||
for true {
|
this.check(),
|
||||||
this.config.Engine.Input<- core.Result{
|
|
||||||
this.config.Host.Host,
|
|
||||||
this.check(),
|
|
||||||
}
|
|
||||||
time.Sleep(interval)
|
|
||||||
}
|
}
|
||||||
|
return this.config.Interval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HttpChecker) Stop() bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HttpChecker) check() core.Status {
|
func (this *HttpChecker) check() core.Status {
|
||||||
|
|
|
@ -9,3 +9,42 @@ type CheckCreateConfig struct {
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Host TargetConfig
|
Host TargetConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Check interface{
|
||||||
|
Check() time.Duration
|
||||||
|
Stop() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenericCheck(proxy Check) *GenericCheck {
|
||||||
|
check := &GenericCheck{
|
||||||
|
make(chan bool, 0),
|
||||||
|
make(chan bool, 0),
|
||||||
|
proxy,
|
||||||
|
}
|
||||||
|
go check.run()
|
||||||
|
return check
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericCheck struct {
|
||||||
|
killswitch chan bool
|
||||||
|
killresponse chan bool
|
||||||
|
proxy Check
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *GenericCheck) Stop() bool {
|
||||||
|
this.killswitch<- true
|
||||||
|
return <-this.killresponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *GenericCheck) run() {
|
||||||
|
timeout := time.NewTimer(0)
|
||||||
|
for true {
|
||||||
|
select {
|
||||||
|
case <-this.killswitch:
|
||||||
|
this.killresponse<- this.proxy.Stop()
|
||||||
|
return
|
||||||
|
case <-timeout.C:
|
||||||
|
timeout = time.NewTimer(this.proxy.Check())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckConfig struct {
|
type CheckConfig struct {
|
||||||
Interval uint16
|
Targets []TargetConfig
|
||||||
Target TargetConfig
|
|
||||||
Reactions []ReactionConfig
|
Reactions []ReactionConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type TargetConfig struct {
|
type TargetConfig struct {
|
||||||
|
Interval uint16
|
||||||
Type string
|
Type string
|
||||||
Host string
|
Host string
|
||||||
Options map[string]string
|
Options map[string]string
|
||||||
|
|
|
@ -8,15 +8,32 @@ import (
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
Input chan Result
|
Input chan Result
|
||||||
handlers []*GenericHandler
|
handlers []*GenericHandler
|
||||||
|
checks []*GenericCheck
|
||||||
killswitch chan bool
|
killswitch chan bool
|
||||||
|
killresponse chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEngine() *Engine {
|
func NewEngine() *Engine {
|
||||||
return &Engine{
|
engine := &Engine{
|
||||||
make(chan Result),
|
make(chan Result),
|
||||||
make([]*GenericHandler, 0),
|
make([]*GenericHandler, 0),
|
||||||
make(chan bool, 1),
|
make([]*GenericCheck, 0),
|
||||||
|
make(chan bool, 0),
|
||||||
|
make(chan bool, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go engine.startProcessor()
|
||||||
|
|
||||||
|
return engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Engine) AddCheck(check *GenericCheck) {
|
||||||
|
if check == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checks = append(this.checks, check)
|
||||||
|
log.Print(fmt.Sprintf("%v", this.checks))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Engine) AddHandler(handler *GenericHandler) {
|
func (this *Engine) AddHandler(handler *GenericHandler) {
|
||||||
|
@ -28,33 +45,50 @@ func (this *Engine) AddHandler(handler *GenericHandler) {
|
||||||
log.Print(fmt.Sprintf("%v", this.handlers))
|
log.Print(fmt.Sprintf("%v", this.handlers))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Engine) Run() {
|
func (this *Engine) Stop() bool {
|
||||||
go this.startProcessor()
|
this.killswitch<- true
|
||||||
|
return <-this.killresponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Engine) startProcessor() {
|
func (this *Engine) startProcessor() {
|
||||||
statuses := make(map[string]Status)
|
statuses := make(map[string]Status)
|
||||||
for true {
|
for true {
|
||||||
result := <-this.Input
|
select {
|
||||||
|
case result := <-this.Input:
|
||||||
|
//No transition if we don't exist
|
||||||
|
if result.Status == statuses[result.RecordValue] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
//No transition if we don't exist
|
//Create a record with to, from
|
||||||
if result.Status == statuses[result.RecordValue] {
|
change := Transition{
|
||||||
continue
|
result.Status,
|
||||||
|
statuses[result.RecordValue],
|
||||||
|
result.RecordValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send the record to everyone who cares
|
||||||
|
for _, relay := range this.handlers {
|
||||||
|
relay.Channel<- change
|
||||||
|
}
|
||||||
|
|
||||||
|
//And set our new status
|
||||||
|
statuses[result.RecordValue] = result.Status
|
||||||
|
case <-this.killswitch:
|
||||||
|
this.killresponse<- this.stop()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a record with to, from
|
|
||||||
change := Transition{
|
|
||||||
result.Status,
|
|
||||||
statuses[result.RecordValue],
|
|
||||||
result.RecordValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send the record to everyone who cares
|
|
||||||
for _, relay := range this.handlers {
|
|
||||||
relay.Channel<- change
|
|
||||||
}
|
|
||||||
|
|
||||||
//And set our new status
|
|
||||||
statuses[result.RecordValue] = result.Status
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Engine) stop() bool {
|
||||||
|
exitStatus := true
|
||||||
|
for _, handler := range this.handlers {
|
||||||
|
exitStatus = exitStatus && handler.Stop()
|
||||||
|
}
|
||||||
|
for _, check := range this.checks {
|
||||||
|
exitStatus = exitStatus && check.Stop()
|
||||||
|
}
|
||||||
|
return exitStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,35 +5,42 @@ import (
|
||||||
|
|
||||||
type Handler interface{
|
type Handler interface{
|
||||||
Handle(transition Transition)
|
Handle(transition Transition)
|
||||||
}
|
Stop() bool
|
||||||
|
|
||||||
type GenericHandler struct {
|
|
||||||
Channel chan Transition
|
|
||||||
killswitch chan bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *GenericHandler) Stop() {
|
|
||||||
this.killswitch <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *GenericHandler) run(proxy Handler) {
|
|
||||||
for true {
|
|
||||||
var transition Transition
|
|
||||||
select {
|
|
||||||
case <-this.killswitch:
|
|
||||||
return
|
|
||||||
case transition = <-this.Channel:
|
|
||||||
proxy.Handle(transition)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGenericHandler(input chan Transition, proxy Handler) *GenericHandler {
|
func NewGenericHandler(input chan Transition, proxy Handler) *GenericHandler {
|
||||||
handler := &GenericHandler{
|
handler := &GenericHandler{
|
||||||
input,
|
input,
|
||||||
make(chan bool, 1),
|
make(chan bool, 0),
|
||||||
|
make(chan bool, 0),
|
||||||
|
proxy,
|
||||||
}
|
}
|
||||||
go handler.run(proxy)
|
go handler.run()
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GenericHandler struct {
|
||||||
|
Channel chan Transition
|
||||||
|
killswitch chan bool
|
||||||
|
killresponse chan bool
|
||||||
|
proxy Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *GenericHandler) Stop() bool {
|
||||||
|
this.killswitch<- true
|
||||||
|
return <-this.killresponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *GenericHandler) run() {
|
||||||
|
for true {
|
||||||
|
var transition Transition
|
||||||
|
select {
|
||||||
|
case <-this.killswitch:
|
||||||
|
this.killresponse<- this.proxy.Stop()
|
||||||
|
return
|
||||||
|
case transition = <-this.Channel:
|
||||||
|
this.proxy.Handle(transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
"../core"
|
"../core"
|
||||||
"../checks"
|
"../checks"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createCheck(interval uint16, engine *core.Engine, host core.TargetConfig) {
|
func createCheck(engine *core.Engine, host core.TargetConfig) *core.GenericCheck {
|
||||||
|
log.Print("createCheck called")
|
||||||
|
log.Print(fmt.Sprintf("creating a `%s`", host.Type))
|
||||||
config := core.CheckCreateConfig{
|
config := core.CheckCreateConfig{
|
||||||
engine,
|
engine,
|
||||||
time.Duration(int64(interval)) * time.Second,
|
time.Duration(int64(host.Interval)) * time.Second,
|
||||||
host,
|
host,
|
||||||
}
|
}
|
||||||
switch host.Type {
|
switch host.Type {
|
||||||
case "http", "https":
|
case "http", "https":
|
||||||
checks.NewHttpChecker(config)
|
return checks.NewHttpChecker(config)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ func EngineFromConfig(config core.CheckConfig) *core.Engine {
|
||||||
engine.AddHandler(createHandler(reaction))
|
engine.AddHandler(createHandler(reaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
createCheck(config.Interval, engine, config.Target)
|
for _, target := range config.Targets {
|
||||||
|
engine.AddCheck(createCheck(engine, target))
|
||||||
|
}
|
||||||
|
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,10 @@ func (this *cloudflareHandler) Handle(transition core.Transition) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *cloudflareHandler) Stop() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (this *cloudflareHandler) removeCloudflareRecord(recordValue string) bool {
|
func (this *cloudflareHandler) removeCloudflareRecord(recordValue string) bool {
|
||||||
records, err := this.client.RetrieveRecordsByName(this.config.Options["Domain"], this.config.Options["Name"])
|
records, err := this.client.RetrieveRecordsByName(this.config.Options["Domain"], this.config.Options["Name"])
|
||||||
|
|
||||||
|
|
|
@ -18,3 +18,7 @@ func (this *logHandler) Handle(transition core.Transition) {
|
||||||
transition.RecordValue, transition.To,
|
transition.RecordValue, transition.To,
|
||||||
transition.To.String()))
|
transition.To.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *logHandler) Stop() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
15
main.go
15
main.go
|
@ -3,7 +3,10 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os/signal"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"./core"
|
"./core"
|
||||||
"./engine"
|
"./engine"
|
||||||
|
@ -24,9 +27,13 @@ func main() {
|
||||||
engines = append(engines, engine.EngineFromConfig(check))
|
engines = append(engines, engine.EngineFromConfig(check))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, engine := range engines {
|
sigs := make(chan os.Signal, 1)
|
||||||
engine.Run()
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
}
|
<-sigs
|
||||||
|
|
||||||
select{}
|
log.Print(fmt.Sprintf("Stopping."))
|
||||||
|
for _, engine := range engines {
|
||||||
|
engine.Stop()
|
||||||
|
}
|
||||||
|
log.Print("Exiting.")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue