make it a CLI app
parent
915ea9063f
commit
295c26237b
@ -1,22 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/dghubble/sling"
|
||||
)
|
||||
|
||||
type OsemClient struct {
|
||||
sling *sling.Sling
|
||||
}
|
||||
|
||||
func NewOsemClient(client *http.Client) *OsemClient {
|
||||
return &OsemClient{
|
||||
sling: sling.New().Client(client).Base("https://api.opensensemap.org/"),
|
||||
}
|
||||
}
|
||||
|
||||
func (client *OsemClient) GetBox(boxId string) (Box, error) {
|
||||
box := Box{}
|
||||
_, err := client.sling.New().Path("boxes/").Path(boxId).ReceiveSuccess(&box)
|
||||
return box, err
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"../core"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func isValidBoxId(boxId string) bool {
|
||||
// boxIds are UUIDs
|
||||
r := regexp.MustCompile("^[0-9a-fA-F]{24}$")
|
||||
return r.MatchString(boxId)
|
||||
}
|
||||
|
||||
func BoxIdValidator(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("requires at least 1 argument")
|
||||
}
|
||||
for _, boxId := range args {
|
||||
if isValidBoxId(boxId) == false {
|
||||
return fmt.Errorf("invalid boxId specified: %s", boxId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: actually to be read from arg / file
|
||||
var defaultConf = &core.NotifyConfig{
|
||||
// Transports: struct {
|
||||
// Slack: SlackConfig{
|
||||
// Channel: "asdf"
|
||||
// Token: "qwer"
|
||||
// }
|
||||
// },
|
||||
Events: []core.NotifyEvent{
|
||||
core.NotifyEvent{
|
||||
Type: "measurementAge",
|
||||
Target: "593bcd656ccf3b0011791f5d",
|
||||
Threshold: "5h",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// func parseNotifyConfig(conf string) NotifyConfig, error {}
|
@ -0,0 +1,39 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"../core"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
checkCmd.AddCommand(checkBoxCmd)
|
||||
rootCmd.AddCommand(checkCmd)
|
||||
}
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "One-off check for events on boxes",
|
||||
Long: "One-off check for events on boxes",
|
||||
}
|
||||
|
||||
var checkBoxCmd = &cobra.Command{
|
||||
Use: "boxes <boxId> [...<boxIds>]",
|
||||
Short: "one-off check on one or more box IDs",
|
||||
Long: "specify box IDs to check them for events",
|
||||
Args: BoxIdValidator,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
notifications, err := core.CheckNotifications(args, defaultConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking for notifications:", err)
|
||||
}
|
||||
fmt.Println(notifications)
|
||||
|
||||
// logNotifications(notifications)
|
||||
if shouldNotify {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "osem_notify",
|
||||
Long: "Run healthchecks and send notifications for boxes on opensensemap.org",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
var shouldNotify bool
|
||||
var defaultConfig string
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().BoolVarP(&shouldNotify, "notify", "n", false, "if set, will send out notifications.\nOtherwise results are printed to stdout only")
|
||||
rootCmd.PersistentFlags().StringVarP(&defaultConfig, "confdefault", "c", "", "default JSON config to use for event checking")
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"../core"
|
||||
)
|
||||
|
||||
var watchInterval int
|
||||
var ticker <-chan time.Time
|
||||
|
||||
func init() {
|
||||
watchCmd.AddCommand(watchBoxesCmd)
|
||||
watchCmd.PersistentFlags().IntVarP(&watchInterval, "interval", "i", 15, "interval to run checks in minutes")
|
||||
rootCmd.AddCommand(watchCmd)
|
||||
}
|
||||
|
||||
var watchCmd = &cobra.Command{
|
||||
Use: "watch",
|
||||
Aliases: []string{"serve"},
|
||||
Short: "Watch boxes for events at an interval",
|
||||
Long: "Watch boxes for events at an interval",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
ticker = time.NewTicker(time.Duration(watchInterval) * time.Second).C
|
||||
},
|
||||
}
|
||||
|
||||
var watchBoxesCmd = &cobra.Command{
|
||||
Use: "boxes <boxId> [...<boxIds>]",
|
||||
Short: "watch a list of box IDs for events",
|
||||
Long: "specify box IDs to watch them for events",
|
||||
Args: BoxIdValidator,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
exec := func() error {
|
||||
notifications, err := core.CheckNotifications(args, defaultConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking for notifications: ", err)
|
||||
}
|
||||
fmt.Println(notifications)
|
||||
|
||||
// logNotifications(notifications)
|
||||
if shouldNotify {
|
||||
// TODO
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
<-ticker
|
||||
err = exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
@ -1,4 +1,10 @@
|
||||
package main
|
||||
package core
|
||||
|
||||
type NotifyEvent struct {
|
||||
Type string `json:"type"`
|
||||
Target string `json:"target"`
|
||||
Threshold string `json:"threshold"`
|
||||
}
|
||||
|
||||
type NotifyConfig struct {
|
||||
// Transports interface{} `json:"transports"`
|
@ -0,0 +1,34 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/dghubble/sling"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type OsemError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type OsemClient struct {
|
||||
sling *sling.Sling
|
||||
}
|
||||
|
||||
func NewOsemClient(client *http.Client) *OsemClient {
|
||||
return &OsemClient{
|
||||
sling: sling.New().Client(client).Base("https://api.opensensemap.org/"),
|
||||
}
|
||||
}
|
||||
|
||||
func (client *OsemClient) GetBox(boxId string) (Box, error) {
|
||||
box := Box{}
|
||||
fail := OsemError{}
|
||||
client.sling.New().Path("boxes/").Path(boxId).Receive(&box, &fail)
|
||||
if fail.Message != "" {
|
||||
return box, errors.New("could not fetch box: " + fail.Message)
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
var osem = NewOsemClient(&http.Client{}) // default client
|
@ -0,0 +1,74 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetOutput(os.Stdout)
|
||||
// log.SetFormatter(&log.JSONFormatter{})
|
||||
}
|
||||
|
||||
func CheckNotifications(boxIds []string, defaultConf *NotifyConfig) ([]Notification, []error) {
|
||||
log.Info("Checking notifications for ", len(boxIds), " box(es)")
|
||||
|
||||
// TODO: return a map of Box: []Notification instead?
|
||||
notifications := []Notification{}
|
||||
errors := []error{}
|
||||
for _, boxId := range boxIds {
|
||||
n, err := checkBox(boxId, defaultConf)
|
||||
if notifications != nil {
|
||||
notifications = append(notifications, n...)
|
||||
}
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) == 0 {
|
||||
errors = nil
|
||||
}
|
||||
|
||||
return notifications, errors
|
||||
}
|
||||
|
||||
func checkBox(boxId string, defaultConf *NotifyConfig) ([]Notification, error) {
|
||||
boxLogger := log.WithFields(log.Fields{"boxId": boxId})
|
||||
boxLogger.Debug("checking box for due notifications")
|
||||
|
||||
// get box data
|
||||
box, err := osem.GetBox(boxId)
|
||||
if err != nil {
|
||||
boxLogger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if box has no notify config, we use the defaultConf
|
||||
if box.NotifyConf == nil {
|
||||
box.NotifyConf = defaultConf
|
||||
}
|
||||
boxLogger.Debug(box.NotifyConf)
|
||||
|
||||
// run checks
|
||||
notifications, err2 := box.runChecks()
|
||||
if err2 != nil {
|
||||
boxLogger.Error("could not run checks on box: ", err)
|
||||
return notifications, err2
|
||||
}
|
||||
if notifications == nil {
|
||||
boxLogger.Debug("all is fine")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// store notifications for later submit
|
||||
// notifier, err3 := box.getNotifier()
|
||||
// if err3 != nil {
|
||||
// boxLogger.Error("could not get notifier for box: ", err)
|
||||
// return notifications, err3
|
||||
// }
|
||||
// notifier.AddNotifications(notifications)
|
||||
|
||||
return notifications, nil
|
||||
}
|
@ -1,81 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/carlescere/scheduler"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"./cmd"
|
||||
)
|
||||
|
||||
func checkBox(boxId string, defaultConf *NotifyConfig) {
|
||||
boxLogger := log.WithFields(log.Fields{"boxId": boxId})
|
||||
boxLogger.Debug("checking box for due notifications")
|
||||
|
||||
// get box data
|
||||
box, err := osem.GetBox(boxId)
|
||||
if err != nil {
|
||||
boxLogger.Error("could not fetch box: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
// if box has no notify config, we use the defaultConf
|
||||
if box.NotifyConf == nil {
|
||||
box.NotifyConf = defaultConf
|
||||
}
|
||||
boxLogger.Debug(box.NotifyConf)
|
||||
|
||||
// run checks
|
||||
notifications, err2 := box.runChecks()
|
||||
if err2 != nil {
|
||||
boxLogger.Error("could not run checks on box: ", err)
|
||||
return
|
||||
}
|
||||
if notifications == nil {
|
||||
boxLogger.Debug("all is fine")
|
||||
return
|
||||
}
|
||||
|
||||
// store notifications for later submit
|
||||
// notifier, err3 := box.getNotifier()
|
||||
// if err3 != nil {
|
||||
// boxLogger.Error("could not get notifier for box: ", err)
|
||||
// return
|
||||
// }
|
||||
// notifier.AddNotifications(notifications)
|
||||
}
|
||||
|
||||
func checkNotifications() {
|
||||
log.Info("running job checkNotifications()")
|
||||
checkBox("593bcd656ccf3b0011791f5a", defaultConf)
|
||||
}
|
||||
|
||||
var osem = NewOsemClient(&http.Client{})
|
||||
var defaultConf = &NotifyConfig{
|
||||
// Transports: struct {
|
||||
// Slack: SlackConfig{
|
||||
// Channel: "asdf"
|
||||
// Token: "qwer"
|
||||
// }
|
||||
// },
|
||||
Events: []NotifyEvent{
|
||||
NotifyEvent{
|
||||
Type: "measurementAge",
|
||||
Target: "593bcd656ccf3b0011791f5d",
|
||||
Threshold: "5h",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetOutput(os.Stdout)
|
||||
// log.SetFormatter(&log.JSONFormatter{})
|
||||
}
|
||||
|
||||
func main() {
|
||||
scheduler.Every(15).Seconds().Run(checkNotifications)
|
||||
// scheduler.Every(30).Seconds().Run(submitNotifications)
|
||||
runtime.Goexit() // keep runtime running
|
||||
cmd.Execute()
|
||||
}
|
||||
|
Loading…
Reference in New Issue