package main import ( "bytes" "flag" "fmt" "gopkg.in/yaml.v3" "os" "time" "riedstra.dev/go/checkup" ) var Version = "Development" // jobResponse lets us return a bit more info from each of the jobs // and track how the response has changed type jobResponse struct { Id string Message string Err error Time time.Time } type Config struct { RocketChatURL string `yaml:"RocketChatURL"` DiscordURL string `yaml:"DiscordURL"` DefaultCertPort string `yaml:"DefaultCertPort"` CertWindow int `yaml:"CertWindow"` CheckCerts map[string]*string `yaml:"CheckCerts"` ExpectedStatusCode int `yaml:"ExpectedStatusCode"` StatusChecks map[string]*int `yaml:"StatusChecks"` Workers int `yaml:"Workers"` Interval int `yaml:"Interval"` RenotifyInterval int `yaml:"RenotifyInterval"` HTTPTimeout int `yaml:"HTTPTimeout"` } func ReadConfig(fn string, conf *Config) error { fh, err := os.Open(fn) if err != nil { return err } dec := yaml.NewDecoder(fh) err = dec.Decode(conf) return err } func jobNotifyDedup(conf *Config, prevJobs, newJobs map[string]*jobResponse) { buf := &bytes.Buffer{} for id, resp := range newJobs { oldresp, ok := prevJobs[id] if !ok { buf.Write([]byte(resp.Message)) buf.Write([]byte{'\n'}) continue } if oldresp.Message == resp.Message { // This isn't new, adjust accordingly newJobs[id] = prevJobs[id] if time.Now().After( oldresp.Time.Add(time.Duration(conf.RenotifyInterval) * time.Second)) { buf.Write([]byte( "still active --> " + resp.Message, )) buf.Write([]byte{'\n'}) } continue } buf.Write([]byte( oldresp.Message + " now --> " + resp.Message, )) buf.Write([]byte{'\n'}) } for id, resp := range prevJobs { _, ok := newJobs[id] if !ok { buf.Write([]byte( "[CLEARED]: " + resp.Message + "\n", )) } } fmt.Print(string(buf.Bytes())) notify(conf, buf.Bytes()) } func jobNotify(conf *Config, jobs map[string]*jobResponse) { buf := &bytes.Buffer{} for _, resp := range jobs { buf.Write([]byte(resp.Message)) buf.Write([]byte{'\n'}) } notify(conf, buf.Bytes()) } func notify(conf *Config, b []byte) { if len(b) >= 1 { if conf.RocketChatURL != "" { err := checkup.SendRocketChatAlert(conf.RocketChatURL, string(b)) if err != nil { fmt.Fprintln(os.Stderr, string(b)) fmt.Fprintf(os.Stderr, "When sending webhook for rocketchat: %s\n", err) } } if conf.DiscordURL != "" { err := checkup.SendDiscordAlert(conf.DiscordURL, string(b)) if err != nil { fmt.Fprintln(os.Stderr, string(b)) fmt.Fprintf(os.Stderr, "When sending webhook for discord: %s\n", err) } } } } func main() { fl := flag.NewFlagSet("checkup", flag.ExitOnError) confFn := fl.String("c", "config.yml", "Configuration file path") version := fl.Bool("v", false, "Print version and exit") once := fl.Bool("o", false, "Run once then exit") _ = fl.Parse(os.Args[1:]) if *version { fmt.Println(Version) os.Exit(0) } // Defaults to be overridden by config conf := &Config{ Workers: 1, DefaultCertPort: "443", CertWindow: 15, ExpectedStatusCode: 200, Interval: 60, RenotifyInterval: 900, } err := ReadConfig(*confFn, conf) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if *once { jobNotify(conf, checkCerts(conf)) jobNotify(conf, checkStatus(conf)) os.Exit(0) } certsPrev := map[string]*jobResponse{} statusPrev := map[string]*jobResponse{} for { cert := checkCerts(conf) status := checkStatus(conf) jobNotifyDedup(conf, certsPrev, cert) jobNotifyDedup(conf, statusPrev, status) certsPrev = cert statusPrev = status time.Sleep(time.Duration(conf.Interval) * time.Second) } }