make it a CLI app

develop
Norwin 6 years ago
parent 915ea9063f
commit 295c26237b
Signed by: norwin
GPG Key ID: 24BC059DE24C43A3

@ -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,4 +1,4 @@
package main
package core
type AbstractNotifier interface {
GetName() string
@ -12,9 +12,3 @@ type Notification struct {
// TODO: multiple transports? one transport per event? (??)
type SlackConfig struct{}
type NotifyEvent struct {
Type string `json:"type"`
Target string `json:"target"`
Threshold string `json:"threshold"`
}

@ -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…
Cancel
Save