Graceful exit, and slight restructuring

This commit is contained in:
Kegan Myers 2015-08-24 00:28:45 -05:00
parent 9ac6dec291
commit 0291404261
11 changed files with 173 additions and 97 deletions

View file

@ -12,32 +12,8 @@ and placed in whatever directory you will run CFHA from.
Example configuration file:
```js
{
"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
}
```
(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`)
Limitations
===========

View file

@ -8,14 +8,12 @@ import (
"fmt"
)
func NewHttpChecker(config core.CheckCreateConfig) *HttpChecker {
checker := HttpChecker{
func NewHttpChecker(config core.CheckCreateConfig) *core.GenericCheck {
return core.NewGenericCheck(&HttpChecker{
&http.Client{},
config,
config.Host.Type + "://" + config.Host.Host + "/",
}
go checker.run()
return &checker
})
}
type HttpChecker struct {
@ -24,16 +22,16 @@ type HttpChecker struct {
endpoint string
}
func (this *HttpChecker) run() {
interval := time.Duration(this.config.Interval) * time.Second
log.Print(fmt.Sprintf("Starting: %v\n", this.config.Host))
for true {
func (this *HttpChecker) Check() time.Duration {
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 {

View file

@ -9,3 +9,42 @@ type CheckCreateConfig struct {
Interval time.Duration
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())
}
}
}

View file

@ -5,12 +5,12 @@ type Config struct {
}
type CheckConfig struct {
Interval uint16
Target TargetConfig
Targets []TargetConfig
Reactions []ReactionConfig
}
type TargetConfig struct {
Interval uint16
Type string
Host string
Options map[string]string

View file

@ -8,15 +8,32 @@ import (
type Engine struct {
Input chan Result
handlers []*GenericHandler
checks []*GenericCheck
killswitch chan bool
killresponse chan bool
}
func NewEngine() *Engine {
return &Engine{
engine := &Engine{
make(chan Result),
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) {
@ -28,15 +45,16 @@ func (this *Engine) AddHandler(handler *GenericHandler) {
log.Print(fmt.Sprintf("%v", this.handlers))
}
func (this *Engine) Run() {
go this.startProcessor()
func (this *Engine) Stop() bool {
this.killswitch<- true
return <-this.killresponse
}
func (this *Engine) startProcessor() {
statuses := make(map[string]Status)
for true {
result := <-this.Input
select {
case result := <-this.Input:
//No transition if we don't exist
if result.Status == statuses[result.RecordValue] {
continue
@ -56,5 +74,21 @@ func (this *Engine) startProcessor() {
//And set our new status
statuses[result.RecordValue] = result.Status
case <-this.killswitch:
this.killresponse<- this.stop()
return
}
}
}
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
}

View file

@ -5,35 +5,42 @@ import (
type Handler interface{
Handle(transition Transition)
}
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)
}
}
Stop() bool
}
func NewGenericHandler(input chan Transition, proxy Handler) *GenericHandler {
handler := &GenericHandler{
input,
make(chan bool, 1),
make(chan bool, 0),
make(chan bool, 0),
proxy,
}
go handler.run(proxy)
go handler.run()
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)
}
}
}

View file

@ -1,19 +1,24 @@
package engine
import (
"log"
"fmt"
"time"
"../core"
"../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{
engine,
time.Duration(int64(interval)) * time.Second,
time.Duration(int64(host.Interval)) * time.Second,
host,
}
switch host.Type {
case "http", "https":
checks.NewHttpChecker(config)
return checks.NewHttpChecker(config)
}
return nil
}

View file

@ -11,7 +11,9 @@ func EngineFromConfig(config core.CheckConfig) *core.Engine {
engine.AddHandler(createHandler(reaction))
}
createCheck(config.Interval, engine, config.Target)
for _, target := range config.Targets {
engine.AddCheck(createCheck(engine, target))
}
return engine
}

View file

@ -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 {
records, err := this.client.RetrieveRecordsByName(this.config.Options["Domain"], this.config.Options["Name"])

View file

@ -18,3 +18,7 @@ func (this *logHandler) Handle(transition core.Transition) {
transition.RecordValue, transition.To,
transition.To.String()))
}
func (this *logHandler) Stop() bool {
return true
}

15
main.go
View file

@ -3,7 +3,10 @@ package main
import (
"log"
"fmt"
"os"
"syscall"
"io/ioutil"
"os/signal"
"encoding/json"
"./core"
"./engine"
@ -24,9 +27,13 @@ func main() {
engines = append(engines, engine.EngineFromConfig(check))
}
for _, engine := range engines {
engine.Run()
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
select{}
log.Print(fmt.Sprintf("Stopping."))
for _, engine := range engines {
engine.Stop()
}
log.Print("Exiting.")
}