restructure the API and update the admin script
This commit is contained in:
105
api/status/service.go
Normal file
105
api/status/service.go
Normal file
@ -0,0 +1,105 @@
|
||||
package status
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ngn13/website/api/database"
|
||||
"github.com/ngn13/website/api/util"
|
||||
)
|
||||
|
||||
const (
|
||||
STATUS_RES_DOWN = 0 // service is down
|
||||
STATUS_RES_OK = 1 // service is up
|
||||
STATUS_RES_SLOW = 2 // service is up, but slow
|
||||
STATUS_RES_NONE = 3 // service doesn't support status checking/status checking is disabled
|
||||
)
|
||||
|
||||
func (s *Type) check_http_service(service *database.Service) (r uint8, err error) {
|
||||
var (
|
||||
req *http.Request
|
||||
res *http.Response
|
||||
|
||||
start time.Time
|
||||
elapsed time.Duration
|
||||
)
|
||||
|
||||
r = STATUS_RES_NONE
|
||||
|
||||
if req, err = http.NewRequest("GET", service.CheckURL, nil); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
trace := &httptrace.ClientTrace{
|
||||
GetConn: func(_ string) { start = time.Now() },
|
||||
GotFirstResponseByte: func() { elapsed = time.Since(start) },
|
||||
}
|
||||
|
||||
http.DefaultClient.Timeout = s.timeout
|
||||
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
|
||||
if res != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
util.Debg("marking service \"%s\" as down (%s)", service.Name, err.Error())
|
||||
err = nil
|
||||
r = STATUS_RES_DOWN
|
||||
} else if res.StatusCode != 200 {
|
||||
util.Debg("marking service \"%s\" as down (status code %d)", service.Name, res.StatusCode)
|
||||
r = STATUS_RES_DOWN
|
||||
} else if elapsed.Microseconds() > s.limit.Microseconds() {
|
||||
r = STATUS_RES_SLOW
|
||||
} else {
|
||||
r = STATUS_RES_OK
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Type) check_service(service *database.Service) error {
|
||||
var (
|
||||
res uint8
|
||||
url *url.URL
|
||||
err error
|
||||
)
|
||||
|
||||
if s.disabled || service.CheckURL == "" {
|
||||
err = nil
|
||||
goto fail
|
||||
}
|
||||
|
||||
if url, err = url.Parse(service.CheckURL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch url.Scheme {
|
||||
case "https":
|
||||
if res, err = s.check_http_service(service); err != nil {
|
||||
goto fail
|
||||
}
|
||||
|
||||
case "http":
|
||||
if res, err = s.check_http_service(service); err != nil {
|
||||
goto fail
|
||||
}
|
||||
|
||||
default:
|
||||
// unsupported protocol
|
||||
err = nil
|
||||
goto fail
|
||||
}
|
||||
|
||||
service.CheckTime = uint64(time.Now().Unix())
|
||||
service.CheckRes = res
|
||||
return nil
|
||||
|
||||
fail:
|
||||
service.CheckTime = 0
|
||||
service.CheckRes = STATUS_RES_NONE
|
||||
return err
|
||||
}
|
139
api/status/status.go
Normal file
139
api/status/status.go
Normal file
@ -0,0 +1,139 @@
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ngn13/website/api/config"
|
||||
"github.com/ngn13/website/api/database"
|
||||
"github.com/ngn13/website/api/util"
|
||||
)
|
||||
|
||||
type Type struct {
|
||||
conf *config.Type
|
||||
db *database.Type
|
||||
|
||||
ticker *time.Ticker
|
||||
updateChan chan int
|
||||
closeChan chan int
|
||||
|
||||
disabled bool
|
||||
timeout time.Duration
|
||||
limit time.Duration
|
||||
}
|
||||
|
||||
func (s *Type) check() {
|
||||
var (
|
||||
services []database.Service
|
||||
service database.Service
|
||||
err error
|
||||
)
|
||||
|
||||
for s.db.ServiceNext(&service) {
|
||||
services = append(services, service)
|
||||
}
|
||||
|
||||
for i := range services {
|
||||
if err = s.check_service(&services[i]); err != nil {
|
||||
util.Fail("failed to check the service status for \"%s\": %s", services[i].Name, err.Error())
|
||||
}
|
||||
|
||||
if err = s.db.ServiceUpdate(&services[i]); err != nil {
|
||||
util.Fail("failed to update service status for \"%s\": %s", services[i].Name, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Type) loop() {
|
||||
s.check()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.closeChan:
|
||||
close(s.updateChan)
|
||||
s.ticker.Stop()
|
||||
s.closeChan <- 0
|
||||
return
|
||||
|
||||
case <-s.updateChan:
|
||||
s.check()
|
||||
|
||||
case <-s.ticker.C:
|
||||
s.check()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Type) Setup(conf *config.Type, db *database.Type) error {
|
||||
var (
|
||||
dur time.Duration
|
||||
iv, to, lm string
|
||||
err error
|
||||
)
|
||||
|
||||
iv = conf.GetStr("interval")
|
||||
to = conf.GetStr("timeout")
|
||||
lm = conf.GetStr("limit")
|
||||
|
||||
if iv == "" || to == "" || lm == "" {
|
||||
s.disabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
if dur, err = util.GetDuration(iv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.timeout, err = util.GetDuration(iv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.limit, err = util.GetDuration(iv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.conf = conf
|
||||
s.db = db
|
||||
|
||||
s.ticker = time.NewTicker(dur)
|
||||
s.updateChan = make(chan int)
|
||||
s.closeChan = make(chan int)
|
||||
|
||||
s.disabled = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Type) Run() error {
|
||||
if s.ticker == nil || s.updateChan == nil || s.closeChan == nil {
|
||||
return fmt.Errorf("you either didn't call Setup() or you called it and it failed")
|
||||
}
|
||||
|
||||
if s.disabled {
|
||||
go s.check()
|
||||
return nil
|
||||
}
|
||||
|
||||
go s.loop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Type) Check() {
|
||||
if !s.disabled {
|
||||
s.updateChan <- 0
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Type) Stop() {
|
||||
// tell loop() to stop
|
||||
s.closeChan <- 0
|
||||
|
||||
// wait till loop() stops
|
||||
for {
|
||||
select {
|
||||
case <-s.closeChan:
|
||||
close(s.closeChan)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user