func MakeIncidentSummary(c conf.RuleConfProvider, s SilenceTester, is *models.IncidentState) (*IncidentSummaryView, error) { alert := c.GetAlert(is.AlertKey.Name()) if alert == nil { return nil, fmt.Errorf("alert %v does not exist in the configuration", is.AlertKey.Name()) } warnNotifications := alert.WarnNotification.Get(c, is.AlertKey.Group()) critNotifications := alert.CritNotification.Get(c, is.AlertKey.Group()) eventSummaries := []EventSummary{} nonNormalNonUnknownCount := 0 for _, event := range is.Events { if event.Status > models.StNormal && event.Status < models.StUnknown { nonNormalNonUnknownCount++ } if eventSummary, unevaluated := MakeEventSummary(event); !unevaluated { eventSummaries = append(eventSummaries, eventSummary) } } actions := make([]EpochAction, len(is.Actions)) for i, action := range is.Actions { actions[i] = MakeEpochAction(action) } subject := is.Subject // There is no rendered subject when the state is unknown and // there is no other non-normal status in the history. if subject == "" && nonNormalNonUnknownCount == 0 { subject = fmt.Sprintf("%s: %v", is.CurrentStatus, is.AlertKey) } return &IncidentSummaryView{ Id: is.Id, Subject: subject, Start: is.Start.Unix(), AlertName: is.AlertKey.Name(), Tags: is.AlertKey.Group(), TagsString: is.AlertKey.Group().String(), CurrentStatus: is.CurrentStatus, WorstStatus: is.WorstStatus, LastAbnormalStatus: is.LastAbnormalStatus, LastAbnormalTime: is.LastAbnormalTime, Unevaluated: is.Unevaluated, NeedAck: is.NeedAck, Silenced: s(is.AlertKey) != nil, Actions: actions, Events: eventSummaries, WarnNotificationChains: conf.GetNotificationChains(c, warnNotifications), CritNotificationChains: conf.GetNotificationChains(c, critNotifications), }, nil }
func main() { flag.Parse() if *flagVersion { fmt.Println(version.GetVersionInfo("bosun")) os.Exit(0) } for _, m := range mains { m() } systemConf, err := conf.LoadSystemConfigFile(*flagConf) if err != nil { slog.Fatalf("couldn't read system configuration: %v", err) } sysProvider, err := systemConf.GetSystemConfProvider() if err != nil { slog.Fatal(err) } ruleConf, err := rule.ParseFile(sysProvider.GetRuleFilePath(), systemConf.EnabledBackends()) if err != nil { slog.Fatalf("couldn't read rules: %v", err) } if *flagTest { os.Exit(0) } var ruleProvider conf.RuleConfProvider = ruleConf httpListen := &url.URL{ Scheme: "http", Host: sysProvider.GetHTTPListen(), } if strings.HasPrefix(httpListen.Host, ":") { httpListen.Host = "localhost" + httpListen.Host } if err := metadata.Init(httpListen, false); err != nil { slog.Fatal(err) } if err := sched.Load(sysProvider, ruleProvider, *flagSkipLast, *flagQuiet); err != nil { slog.Fatal(err) } if sysProvider.GetRelayListen() != "" { go func() { mux := http.NewServeMux() mux.Handle("/api/", util.NewSingleHostProxy(httpListen)) s := &http.Server{ Addr: sysProvider.GetRelayListen(), Handler: mux, } slog.Fatal(s.ListenAndServe()) }() } if sysProvider.GetTSDBHost() != "" { if err := collect.Init(httpListen, "bosun"); err != nil { slog.Fatal(err) } tsdbHost := &url.URL{ Scheme: "http", Host: sysProvider.GetTSDBHost(), } if *flagReadonly { rp := util.NewSingleHostProxy(tsdbHost) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/api/put" { w.WriteHeader(204) return } rp.ServeHTTP(w, r) })) slog.Infoln("readonly relay at", ts.URL, "to", tsdbHost) tsdbHost, _ = url.Parse(ts.URL) sysProvider.SetTSDBHost(tsdbHost.Host) } } if systemConf.GetPing() { go ping.PingHosts(sched.DefaultSched.Search, systemConf.GetPingDuration()) } if sysProvider.GetInternetProxy() != "" { web.InternetProxy, err = url.Parse(sysProvider.GetInternetProxy()) if err != nil { slog.Fatalf("InternetProxy error: %s", err) } } var cmdHook conf.SaveHook if hookPath := sysProvider.GetCommandHookPath(); hookPath != "" { cmdHook, err = conf.MakeSaveCommandHook(hookPath) if err != nil { slog.Fatal(err) } ruleProvider.SetSaveHook(cmdHook) } var reload func() error reloading := make(chan bool, 1) // a lock that we can give up acquiring reload = func() error { select { case reloading <- true: // Got lock default: return fmt.Errorf("not reloading, reload in progress") } defer func() { <-reloading }() newConf, err := rule.ParseFile(sysProvider.GetRuleFilePath(), sysProvider.EnabledBackends()) if err != nil { return err } newConf.SetSaveHook(cmdHook) newConf.SetReload(reload) oldSched := sched.DefaultSched oldDA := oldSched.DataAccess oldSearch := oldSched.Search sched.Close(true) sched.Reset() newSched := sched.DefaultSched newSched.Search = oldSearch newSched.DataAccess = oldDA slog.Infoln("schedule shutdown, loading new schedule") // Load does not set the DataAccess or Search if it is already set if err := sched.Load(sysProvider, newConf, *flagSkipLast, *flagQuiet); err != nil { slog.Fatal(err) } web.ResetSchedule() // Signal web to point to the new DefaultSchedule go func() { slog.Infoln("running new schedule") if !*flagNoChecks { sched.Run() } }() slog.Infoln("config reload complete") return nil } ruleProvider.SetReload(reload) go func() { slog.Fatal(web.Listen(sysProvider.GetHTTPListen(), *flagDev, sysProvider.GetTSDBHost(), reload)) }() go func() { if !*flagNoChecks { sched.Run() } }() go func() { sc := make(chan os.Signal, 1) signal.Notify(sc, os.Interrupt, syscall.SIGTERM) killing := false for range sc { if killing { slog.Infoln("Second interrupt: exiting") os.Exit(1) } killing = true go func() { slog.Infoln("Interrupt: closing down...") sched.Close(false) slog.Infoln("done") os.Exit(1) }() } }() if *flagWatch { watch(".", "*.go", quit) watch(filepath.Join("web", "static", "templates"), "*.html", web.RunEsc) base := filepath.Join("web", "static", "js") watch(base, "*.ts", web.RunTsc) } select {} }