package main import ( "context" "fmt" "github.com/multiplay/go-slack/chat" "github.com/multiplay/go-slack/webhook" "github.com/robfig/cron" "gopkg.in/alecthomas/kingpin.v2" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "os" "os/signal" "syscall" "time" ) func main() { slackUrl := kingpin.Flag("slack-url", "The Slack Webhook URL").Envar("SLACK_URL").Required().String() kingpin.Parse() config, err := rest.InClusterConfig() if err != nil { panic(err.Error()) } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } slack := webhook.New(*slackUrl) parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) ic := make(chan os.Signal, 1) signal.Notify(ic, os.Interrupt, syscall.SIGTERM) for { select { case <-ic: fmt.Printf("Got SIGTERM signal, exiting\n") break default: cronjobs, err := clientset.BatchV1beta1().CronJobs("").List(context.Background(), v1.ListOptions{}) if err != nil { fmt.Printf("Error getting cronjobs: %s", err) os.Exit(1) } limit := time.Now().Add(-120 * time.Second) for _, c := range cronjobs.Items { if c.Spec.Suspend == nil || !*c.Spec.Suspend { since := c.CreationTimestamp if c.Status.LastScheduleTime != nil { since = *c.Status.LastScheduleTime } schedule, err := parser.Parse(c.Spec.Schedule) if err != nil { fmt.Printf("Error parsing schedule of %s/%s (%s): %s", c.Namespace, c.Name, c.Spec.Schedule, err) os.Exit(1) } next := schedule.Next(since.Time) fmt.Printf("Checking %s/%s since %s, next schedule %s, limit %s.\n", c.Namespace, c.Name, since.Format(time.RFC3339), next.Format(time.RFC3339), limit.Format(time.RFC3339)) if next.Before(limit) { fmt.Printf("%s was not scheduled. Sending Slack notification.\n", c.Name) m := &chat.Message{ Text: fmt.Sprintf("Cronjob %s/%s is not running according to schedule (%s). Last scheduled: %s", c.Namespace, c.Name, c.Spec.Schedule, since.Format(time.RFC3339)), Username: "cron-checker", } resp, err := m.Send(slack) if err != nil { fmt.Printf("Unable to send Slack notification: %s", err) } if !resp.OK { fmt.Printf("Unable to send Slack notification: %s", resp.Error) } } } } time.Sleep(60 * time.Second) } } }