properly set up loading config
This commit is contained in:
parent
8d87786c05
commit
4106d06493
7 changed files with 216 additions and 126 deletions
|
@ -22,6 +22,6 @@ var checkBoxCmd = &cobra.Command{
|
||||||
Args: BoxIdValidator,
|
Args: BoxIdValidator,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cmd.SilenceUsage = true
|
cmd.SilenceUsage = true
|
||||||
return checkAndNotify(args, defaultConf)
|
return checkAndNotify(args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
123
cmd/config.go
Normal file
123
cmd/config.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* config file handling, as it is kinda broken in spf13/viper
|
||||||
|
* mostly copied from https://github.com/TheThingsNetwork/ttn/blob/f623a6a/ttnctl/util/config.go
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GetConfigFile returns the location of the configuration file.
|
||||||
|
// It checks the following (in this order):
|
||||||
|
// the --config flag
|
||||||
|
// $XDG_CONFIG_HOME/osem_notify/config.yml (if $XDG_CONFIG_HOME is set)
|
||||||
|
// $HOME/.osem_notify.yml
|
||||||
|
func getConfigFile() string {
|
||||||
|
flag := viper.GetString("config")
|
||||||
|
|
||||||
|
xdg := os.Getenv("XDG_CONFIG_HOME")
|
||||||
|
if xdg != "" {
|
||||||
|
xdg = path.Join(xdg, "osem_notify", "config.yml")
|
||||||
|
}
|
||||||
|
|
||||||
|
home := os.Getenv("HOME")
|
||||||
|
homeyml := ""
|
||||||
|
homeyaml := ""
|
||||||
|
|
||||||
|
if home != "" {
|
||||||
|
homeyml = path.Join(home, ".osem_notify.yml")
|
||||||
|
homeyaml = path.Join(home, ".osem_notify.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
try_files := []string{
|
||||||
|
flag,
|
||||||
|
xdg,
|
||||||
|
homeyml,
|
||||||
|
homeyaml,
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a file that exists, and use that
|
||||||
|
for _, file := range try_files {
|
||||||
|
if file != "" {
|
||||||
|
if _, err := os.Stat(file); err == nil {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no file found, set up correct fallback
|
||||||
|
if os.Getenv("XDG_CONFIG_HOME") != "" {
|
||||||
|
return xdg
|
||||||
|
} else {
|
||||||
|
return homeyml
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
func initConfig() {
|
||||||
|
theConfig := cfgFile
|
||||||
|
if cfgFile == "" {
|
||||||
|
theConfig = getConfigFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.SetConfigFile(theConfig)
|
||||||
|
viper.SetEnvPrefix("OSEM_NOTIFY") // keys only work in upper case
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||||
|
viper.AutomaticEnv() // WARNING: OSEM_NOTIFIY_CONFIG will not be considered this way. but why should it..
|
||||||
|
|
||||||
|
// If a config file is found, read it in.
|
||||||
|
if _, err := os.Stat(theConfig); err == nil {
|
||||||
|
err := viper.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error when reading config file:", err)
|
||||||
|
}
|
||||||
|
} else if cfgFile != "" {
|
||||||
|
log.Error("Specified config file not found!")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
validateConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateConfig() {
|
||||||
|
transport := viper.GetString("defaultHealthchecks.notifications.transport")
|
||||||
|
if viper.GetBool("notify") && transport == "email" {
|
||||||
|
if len(viper.GetStringSlice("defaultHealthchecks.notifications.options.recipients")) == 0 {
|
||||||
|
log.Warn("No recipients set up for transport email")
|
||||||
|
}
|
||||||
|
|
||||||
|
emailRequired := []string{
|
||||||
|
viper.GetString("email.host"),
|
||||||
|
viper.GetString("email.port"),
|
||||||
|
viper.GetString("email.user"),
|
||||||
|
viper.GetString("email.pass"),
|
||||||
|
viper.GetString("email.from"),
|
||||||
|
}
|
||||||
|
for _, conf := range emailRequired {
|
||||||
|
if conf == "" {
|
||||||
|
log.Error("Default transport set as email, but missing email config")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printConfig() {
|
||||||
|
log.Debug("Using config:")
|
||||||
|
printKV("config file", viper.ConfigFileUsed())
|
||||||
|
for key, val := range viper.AllSettings() {
|
||||||
|
printKV(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printKV(key, val interface{}) {
|
||||||
|
log.Debugf("%20s: %v", key, val)
|
||||||
|
}
|
52
cmd/root.go
52
cmd/root.go
|
@ -2,7 +2,6 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -11,37 +10,18 @@ import (
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "osem_notify",
|
Use: "osem_notify",
|
||||||
|
Short: "Root command displaying help",
|
||||||
Long: "Run healthchecks and send notifications for boxes on opensensemap.org",
|
Long: "Run healthchecks and send notifications for boxes on opensensemap.org",
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// set up config environment FIXME: cannot open / write file?!
|
|
||||||
viper.SetConfigType("json")
|
|
||||||
viper.SetConfigFile(".osem_notify")
|
|
||||||
viper.AddConfigPath("$HOME")
|
|
||||||
viper.AddConfigPath(".")
|
|
||||||
// // If a config file is found, read it in.
|
|
||||||
// if _, err := os.Stat(path.Join(os.Getenv("HOME"), ".osem_notify.yml")); err == nil {
|
|
||||||
// err := viper.ReadInConfig()
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println("Error when reading config file:", err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
viper.SetEnvPrefix("osem_notify")
|
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
|
||||||
viper.AutomaticEnv()
|
|
||||||
|
|
||||||
// set up logger
|
// set up logger
|
||||||
log.SetOutput(os.Stdout)
|
log.SetOutput(os.Stdout)
|
||||||
switch logLevel {
|
if viper.GetBool("debug") {
|
||||||
case "debug":
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
case "info":
|
printConfig()
|
||||||
|
} else {
|
||||||
log.SetLevel(log.InfoLevel)
|
log.SetLevel(log.InfoLevel)
|
||||||
case "warn":
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
case "error":
|
|
||||||
log.SetLevel(log.ErrorLevel)
|
|
||||||
}
|
}
|
||||||
switch logFormat {
|
switch viper.Get("logformat") {
|
||||||
case "json":
|
case "json":
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
}
|
}
|
||||||
|
@ -53,19 +33,33 @@ var rootCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accessed in initConfig(), as it is initialized before config is loaded (sic)
|
||||||
|
var cfgFile string
|
||||||
|
|
||||||
|
func init() {
|
||||||
var (
|
var (
|
||||||
shouldNotify bool
|
shouldNotify bool
|
||||||
logLevel string
|
debug bool
|
||||||
logFormat string
|
logFormat string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
cobra.OnInitialize(initConfig)
|
||||||
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "", "info", "log level, can be one of debug, info, warn, error")
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&logFormat, "log-format", "", "plain", "log format, can be plain or json")
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "path to config file (default $HOME/.osem_notify.yml)")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&logFormat, "logformat", "l", "plain", "log format, can be plain or json")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "enable verbose logging")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&shouldNotify, "notify", "n", false, "if set, will send out notifications.\nOtherwise results are printed to stdout only")
|
rootCmd.PersistentFlags().BoolVarP(&shouldNotify, "notify", "n", false, "if set, will send out notifications.\nOtherwise results are printed to stdout only")
|
||||||
|
|
||||||
|
viper.BindPFlags(rootCmd.PersistentFlags()) // let flags override config
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
// generate documentation
|
||||||
|
// err := doc.GenMarkdownTree(rootCmd, "./doc")
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"../core"
|
"../core"
|
||||||
)
|
)
|
||||||
|
@ -13,29 +14,6 @@ import (
|
||||||
* shared functionality between watch and check
|
* shared functionality between watch and check
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: actually to be read from arg / file
|
|
||||||
|
|
||||||
var defaultConf = &core.NotifyConfig{
|
|
||||||
Notifications: core.TransportConfig{
|
|
||||||
Transport: "email",
|
|
||||||
Options: core.EmailNotifier{
|
|
||||||
[]string{"test@nroo.de"},
|
|
||||||
"notify@nroo.de",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Events: []core.NotifyEvent{
|
|
||||||
core.NotifyEvent{
|
|
||||||
Type: "measurement_age",
|
|
||||||
Target: "all",
|
|
||||||
Threshold: "15m",
|
|
||||||
},
|
|
||||||
core.NotifyEvent{
|
|
||||||
Type: "measurement_suspicious",
|
|
||||||
Target: "all",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidBoxId(boxId string) bool {
|
func isValidBoxId(boxId string) bool {
|
||||||
// boxIds are UUIDs
|
// boxIds are UUIDs
|
||||||
r := regexp.MustCompile("^[0-9a-fA-F]{24}$")
|
r := regexp.MustCompile("^[0-9a-fA-F]{24}$")
|
||||||
|
@ -54,15 +32,21 @@ func BoxIdValidator(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAndNotify(boxIds []string, defaultNotifyConf *core.NotifyConfig) error {
|
func checkAndNotify(boxIds []string) error {
|
||||||
results, err := core.CheckBoxes(boxIds, defaultConf)
|
defaultNotifyConf := &core.NotifyConfig{}
|
||||||
|
err := viper.UnmarshalKey("defaultHealthchecks", defaultNotifyConf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := core.CheckBoxes(boxIds, defaultNotifyConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
results.Log()
|
results.Log()
|
||||||
|
|
||||||
if shouldNotify {
|
if viper.GetBool("notify") {
|
||||||
return results.SendNotifications()
|
return results.SendNotifications()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -32,13 +32,13 @@ var watchBoxesCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cmd.SilenceUsage = true
|
cmd.SilenceUsage = true
|
||||||
err := checkAndNotify(args, defaultConf)
|
err := checkAndNotify(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
<-ticker
|
<-ticker
|
||||||
err = checkAndNotify(args, defaultConf)
|
err = checkAndNotify(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
62
core/Box.go
62
core/Box.go
|
@ -12,7 +12,7 @@ const (
|
||||||
eventMeasurementAge = "measurement_age"
|
eventMeasurementAge = "measurement_age"
|
||||||
eventMeasurementValMin = "measurement_min"
|
eventMeasurementValMin = "measurement_min"
|
||||||
eventMeasurementValMax = "measurement_max"
|
eventMeasurementValMax = "measurement_max"
|
||||||
eventMeasurementValSuspicious = "measurement_suspicious"
|
eventMeasurementValFaulty = "measurement_faulty"
|
||||||
eventTargetAll = "all" // if event.Target is this value, all sensors will be checked
|
eventTargetAll = "all" // if event.Target is this value, all sensors will be checked
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,19 +22,19 @@ var checkTypes = map[string]checkType{
|
||||||
eventMeasurementAge: checkType{"No measurement from %s since %s"},
|
eventMeasurementAge: checkType{"No measurement from %s since %s"},
|
||||||
eventMeasurementValMin: checkType{"Sensor %s reads low value of %s"},
|
eventMeasurementValMin: checkType{"Sensor %s reads low value of %s"},
|
||||||
eventMeasurementValMax: checkType{"Sensor %s reads high value of %s"},
|
eventMeasurementValMax: checkType{"Sensor %s reads high value of %s"},
|
||||||
eventMeasurementValSuspicious: checkType{"Sensor %s reads presumably faulty value of %s"},
|
eventMeasurementValFaulty: checkType{"Sensor %s reads presumably faulty value of %s"},
|
||||||
}
|
}
|
||||||
|
|
||||||
type SuspiciousValue struct {
|
type FaultyValue struct {
|
||||||
sensor string
|
sensor string
|
||||||
val float64
|
val float64
|
||||||
}
|
}
|
||||||
|
|
||||||
var suspiciousVals = map[SuspiciousValue]bool{
|
var faultyVals = map[FaultyValue]bool{
|
||||||
SuspiciousValue{sensor: "BMP280", val: 0.0}: true,
|
FaultyValue{sensor: "BMP280", val: 0.0}: true,
|
||||||
SuspiciousValue{sensor: "HDC1008", val: 0.0}: true,
|
FaultyValue{sensor: "HDC1008", val: 0.0}: true,
|
||||||
SuspiciousValue{sensor: "HDC1008", val: -40}: true,
|
FaultyValue{sensor: "HDC1008", val: -40}: true,
|
||||||
SuspiciousValue{sensor: "SDS 011", val: 0.0}: true,
|
FaultyValue{sensor: "SDS 011", val: 0.0}: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifyEvent struct {
|
type NotifyEvent struct {
|
||||||
|
@ -71,20 +71,23 @@ func (box Box) RunChecks() ([]CheckResult, error) {
|
||||||
var results = []CheckResult{}
|
var results = []CheckResult{}
|
||||||
|
|
||||||
for _, event := range box.NotifyConf.Events {
|
for _, event := range box.NotifyConf.Events {
|
||||||
target := event.Target
|
|
||||||
|
|
||||||
for _, s := range box.Sensors {
|
for _, s := range box.Sensors {
|
||||||
// if a sensor never measured anything, thats ok. checks would fail anyway
|
// if a sensor never measured anything, thats ok. checks would fail anyway
|
||||||
if s.LastMeasurement == nil {
|
if s.LastMeasurement == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if target == eventTargetAll || target == s.Id {
|
// a validator must set these values
|
||||||
|
var (
|
||||||
|
status = CheckOk
|
||||||
|
target = s.Id
|
||||||
|
value string
|
||||||
|
)
|
||||||
|
|
||||||
|
if event.Target == eventTargetAll || event.Target == s.Id {
|
||||||
|
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case eventMeasurementAge:
|
case eventMeasurementAge:
|
||||||
// check if age of lastMeasurement is within threshold
|
|
||||||
status := CheckOk
|
|
||||||
thresh, err := time.ParseDuration(event.Threshold)
|
thresh, err := time.ParseDuration(event.Threshold)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -93,16 +96,9 @@ func (box Box) RunChecks() ([]CheckResult, error) {
|
||||||
status = CheckErr
|
status = CheckErr
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, CheckResult{
|
value = s.LastMeasurement.Date.String()
|
||||||
Threshold: event.Threshold,
|
|
||||||
Event: event.Type,
|
|
||||||
Target: s.Id,
|
|
||||||
Value: s.LastMeasurement.Date.String(),
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
|
|
||||||
case eventMeasurementValMin, eventMeasurementValMax:
|
case eventMeasurementValMin, eventMeasurementValMax:
|
||||||
status := CheckOk
|
|
||||||
thresh, err := strconv.ParseFloat(event.Threshold, 64)
|
thresh, err := strconv.ParseFloat(event.Threshold, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -116,40 +112,34 @@ func (box Box) RunChecks() ([]CheckResult, error) {
|
||||||
status = CheckErr
|
status = CheckErr
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, CheckResult{
|
value = s.LastMeasurement.Value
|
||||||
Threshold: event.Threshold,
|
|
||||||
Event: event.Type,
|
|
||||||
Target: s.Id,
|
|
||||||
Value: s.LastMeasurement.Value,
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
|
|
||||||
case eventMeasurementValSuspicious:
|
|
||||||
status := CheckOk
|
|
||||||
|
|
||||||
|
case eventMeasurementValFaulty:
|
||||||
val, err := strconv.ParseFloat(s.LastMeasurement.Value, 64)
|
val, err := strconv.ParseFloat(s.LastMeasurement.Value, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if suspiciousVals[SuspiciousValue{
|
if faultyVals[FaultyValue{
|
||||||
sensor: s.Type,
|
sensor: s.Type,
|
||||||
val: val,
|
val: val,
|
||||||
}] {
|
}] {
|
||||||
status = CheckErr
|
status = CheckErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = s.LastMeasurement.Value
|
||||||
|
}
|
||||||
|
|
||||||
results = append(results, CheckResult{
|
results = append(results, CheckResult{
|
||||||
Threshold: event.Threshold,
|
Threshold: event.Threshold,
|
||||||
Event: event.Type,
|
Event: event.Type,
|
||||||
Target: s.Id,
|
Target: target,
|
||||||
Value: s.LastMeasurement.Value,
|
Value: value,
|
||||||
Status: status,
|
Status: status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// must return ALL events to enable Notifier to clear previous notifications
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var notifiers = map[string]AbstractNotifier{
|
var notifiers = map[string]AbstractNotifier{
|
||||||
|
@ -23,21 +25,20 @@ type Notification struct {
|
||||||
subject string
|
subject string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// box config required for the EmailNotifier
|
||||||
type EmailNotifier struct {
|
type EmailNotifier struct {
|
||||||
Recipients []string
|
Recipients []string
|
||||||
FromAddress string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n EmailNotifier) New(config interface{}) (AbstractNotifier, error) {
|
func (n EmailNotifier) New(config interface{}) (AbstractNotifier, error) {
|
||||||
res, ok := config.(EmailNotifier)
|
res, ok := config.(EmailNotifier)
|
||||||
|
|
||||||
if !ok || res.Recipients == nil || res.FromAddress == "" {
|
if !ok || res.Recipients == nil {
|
||||||
return nil, errors.New("Invalid EmailNotifier options")
|
return nil, errors.New("Invalid EmailNotifier options")
|
||||||
}
|
}
|
||||||
|
|
||||||
return EmailNotifier{
|
return EmailNotifier{
|
||||||
Recipients: res.Recipients,
|
Recipients: res.Recipients,
|
||||||
FromAddress: res.FromAddress,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,24 +56,22 @@ func (n EmailNotifier) ComposeNotification(box *Box, checks []CheckResult) Notif
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n EmailNotifier) Submit(notification Notification) error {
|
func (n EmailNotifier) Submit(notification Notification) error {
|
||||||
// Set up authentication information. TODO: load from config
|
|
||||||
auth := smtp.PlainAuth(
|
auth := smtp.PlainAuth(
|
||||||
"",
|
"",
|
||||||
"USERNAME",
|
viper.GetString("email.user"),
|
||||||
"PASSWORD",
|
viper.GetString("email.pass"),
|
||||||
"SERVER",
|
viper.GetString("email.host"),
|
||||||
)
|
)
|
||||||
|
|
||||||
fromAddress := "EXAMPLE@EXAMPLE.COM"
|
from := viper.GetString("email.from")
|
||||||
from := fmt.Sprintf("openSenseMap Notifier <%s>", fromAddress)
|
body := fmt.Sprintf("From: openSenseMap Notifier <%s>\nSubject: %s\nContent-Type: text/plain; charset=\"utf-8\"\n\n%s", from, notification.subject, notification.body)
|
||||||
body := fmt.Sprintf("From: %s\nSubject: %s\nContent-Type: text/plain; charset=\"utf-8\"\n\n%s", from, notification.subject, notification.body)
|
|
||||||
|
|
||||||
// Connect to the server, authenticate, set the sender and recipient,
|
// Connect to the server, authenticate, set the sender and recipient,
|
||||||
// and send the email all in one step.
|
// and send the email all in one step.
|
||||||
err := smtp.SendMail(
|
err := smtp.SendMail(
|
||||||
"smtp.gmx.de:25",
|
fmt.Sprintf("%s:%s", viper.GetString("email.host"), viper.GetString("email.port")),
|
||||||
auth,
|
auth,
|
||||||
fromAddress,
|
from,
|
||||||
n.Recipients,
|
n.Recipients,
|
||||||
[]byte(body),
|
[]byte(body),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue