commit
b18135e579
@ -0,0 +1,4 @@
|
|||||||
|
# Config File
|
||||||
|
config.json
|
||||||
|
# Generated Data
|
||||||
|
data.db
|
@ -0,0 +1,34 @@
|
|||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Result denotes the status from a status check.
|
||||||
|
type Result uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
//Online means all requests were fulfilled by the remote end.
|
||||||
|
Online Result = iota
|
||||||
|
//Unstable means some requests were fulfilled by the remote end.
|
||||||
|
Unstable
|
||||||
|
//Offline means every requests to the remote end has failed.
|
||||||
|
Offline
|
||||||
|
)
|
||||||
|
|
||||||
|
//TryCount is the amount of times requests will be made to the remote end.
|
||||||
|
const TryCount = 3
|
||||||
|
|
||||||
|
func statusFromSuccessCount(count int) Result {
|
||||||
|
if count == 0 {
|
||||||
|
return Offline
|
||||||
|
}
|
||||||
|
if count < TryCount {
|
||||||
|
return Unstable
|
||||||
|
}
|
||||||
|
return Online
|
||||||
|
}
|
||||||
|
|
||||||
|
func divide(t time.Duration, dividend int) time.Duration {
|
||||||
|
return time.Duration(int(t) / dividend)
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptrace"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//HTTPPing returns the response time to fetch a page.
|
||||||
|
func HTTPPing(target string) (time.Duration, Result, error) {
|
||||||
|
var totalTime time.Duration
|
||||||
|
var err error
|
||||||
|
successCount := 0
|
||||||
|
okCount := 0
|
||||||
|
for i := 0; i < TryCount; i++ {
|
||||||
|
duration, successful, getErr := timeGet(target)
|
||||||
|
totalTime += duration
|
||||||
|
if getErr != nil {
|
||||||
|
err = getErr
|
||||||
|
}
|
||||||
|
if successful {
|
||||||
|
okCount++
|
||||||
|
}
|
||||||
|
successCount++
|
||||||
|
}
|
||||||
|
return divide(totalTime, successCount), statusFromSuccessCount(okCount), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeGet(url string) (time.Duration, bool, error) {
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
var connect time.Time
|
||||||
|
var duration time.Duration
|
||||||
|
|
||||||
|
trace := &httptrace.ClientTrace{
|
||||||
|
ConnectStart: func(network, addr string) { connect = time.Now() },
|
||||||
|
GotFirstResponseByte: func() {
|
||||||
|
duration = time.Since(connect)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||||
|
res, err := http.DefaultTransport.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
return duration, res.StatusCode < 400, nil
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sparrc/go-ping"
|
||||||
|
)
|
||||||
|
|
||||||
|
//ICMPPing returns the ping to the target host.
|
||||||
|
func ICMPPing(targetHost string) (time.Duration, Result, error) {
|
||||||
|
pinger, err := ping.NewPinger(targetHost)
|
||||||
|
if err != nil {
|
||||||
|
return 0, Offline, err
|
||||||
|
}
|
||||||
|
pinger.Count = TryCount
|
||||||
|
pinger.Run()
|
||||||
|
stats := pinger.Statistics()
|
||||||
|
return stats.AvgRtt, statusFromSuccessCount(stats.PacketsRecv), nil
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"status/check"
|
||||||
|
"status/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type checkTarget struct {
|
||||||
|
CheckType string `json:"type"`
|
||||||
|
ServiceName string `json:"name"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLoop(targets []checkTarget) {
|
||||||
|
lastTime := time.Now()
|
||||||
|
for {
|
||||||
|
if time.Since(lastTime) < time.Minute {
|
||||||
|
secondsUntilNextMinute := 60 - time.Now().Second()
|
||||||
|
time.Sleep(time.Duration(secondsUntilNextMinute) * time.Second)
|
||||||
|
lastTime = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range targets {
|
||||||
|
currentTime := time.Now()
|
||||||
|
var duration time.Duration
|
||||||
|
var result check.Result
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch t.CheckType {
|
||||||
|
case "http":
|
||||||
|
duration, result, err = check.HTTPPing(t.Target)
|
||||||
|
case "icmp":
|
||||||
|
duration, result, err = check.ICMPPing(t.Target)
|
||||||
|
default:
|
||||||
|
result = check.Offline
|
||||||
|
err = errors.New("unknown check type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[ERROR] Failed checking `%s` with `%s` with error: `%s`", t.Target, t.CheckType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := db.PingEntry{
|
||||||
|
ServiceName: t.ServiceName,
|
||||||
|
Ping: duration.Milliseconds(),
|
||||||
|
Status: result,
|
||||||
|
Time: currentTime,
|
||||||
|
}
|
||||||
|
entry.AddToDB()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Targets []checkTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadConfig(file string) (Config, error) {
|
||||||
|
bytes, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
var result Config
|
||||||
|
json.Unmarshal(bytes, &result)
|
||||||
|
return result, nil
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
var db *gorm.DB
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
db, err = gorm.Open("sqlite3", "data.db")
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to connect database")
|
||||||
|
}
|
||||||
|
db.AutoMigrate(&PingEntry{})
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
"status/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
//PingEntry represents an entry of Ping that is persisted in the DB.
|
||||||
|
type PingEntry struct {
|
||||||
|
gorm.Model
|
||||||
|
ServiceName string
|
||||||
|
Time time.Time
|
||||||
|
Ping int64
|
||||||
|
Status check.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PingEntry) AddToDB() {
|
||||||
|
if db == nil {
|
||||||
|
panic("DB is nil")
|
||||||
|
}
|
||||||
|
db.Create(&p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFromDB(serviceName string, from, to time.Time) []*PingEntry {
|
||||||
|
entries := make([]*PingEntry, 0)
|
||||||
|
db.Where("service_name = ? AND time BETWEEN ? AND ?", serviceName, from, to).Find(&entries)
|
||||||
|
return entries
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
module status
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jinzhu/gorm v1.9.12
|
||||||
|
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
|
||||||
|
)
|
@ -0,0 +1,35 @@
|
|||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
|
||||||
|
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||||
|
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||||
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
|
||||||
|
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c h1:gqEdF4VwBu3lTKGHS9rXE9x1/pEaSwCXRLOZRF6qtlw=
|
||||||
|
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c/go.mod h1:eMyUVp6f/5jnzM+3zahzl7q6UXLbgSc3MKg/+ow9QW0=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM=
|
||||||
|
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
Loading…
Reference in New Issue