func TestSilenceStage(t *testing.T) { silences, err := silence.New(silence.Options{}) if err != nil { t.Fatal(err) } if _, err := silences.Create(&silencepb.Silence{ EndsAt: mustTimestampProto(utcNow().Add(time.Hour)), Matchers: []*silencepb.Matcher{{Name: "mute", Pattern: "me"}}, }); err != nil { t.Fatal(err) } marker := types.NewMarker() silencer := NewSilenceStage(silences, marker) in := []model.LabelSet{ {}, {"test": "set"}, {"mute": "me"}, {"foo": "bar", "test": "set"}, {"foo": "bar", "mute": "me"}, {}, {"not": "muted"}, } out := []model.LabelSet{ {}, {"test": "set"}, {"foo": "bar", "test": "set"}, {}, {"not": "muted"}, } var inAlerts []*types.Alert for _, lset := range in { inAlerts = append(inAlerts, &types.Alert{ Alert: model.Alert{Labels: lset}, }) } // Set the second alert als previously silenced. It is expected to have // the WasSilenced flag set to true afterwards. marker.SetSilenced(inAlerts[1].Fingerprint(), "123") _, alerts, err := silencer.Exec(nil, inAlerts...) if err != nil { t.Fatalf("Exec failed: %s", err) } var got []model.LabelSet for i, a := range alerts { got = append(got, a.Labels) if a.WasSilenced != (i == 1) { t.Errorf("Expected WasSilenced to be %v for %d, was %v", i == 1, i, a.WasSilenced) } } if !reflect.DeepEqual(got, out) { t.Fatalf("Muting failed, expected: %v\ngot %v", out, got) } }
func main() { peers := &stringset{} var ( showVersion = flag.Bool("version", false, "Print version information.") configFile = flag.String("config.file", "alertmanager.yml", "Alertmanager configuration file name.") dataDir = flag.String("storage.path", "data/", "Base path for data storage.") retention = flag.Duration("data.retention", 5*24*time.Hour, "How long to keep data for.") externalURL = flag.String("web.external-url", "", "The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). Used for generating relative and absolute links back to Alertmanager itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Alertmanager. If omitted, relevant URL components will be derived automatically.") listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.") meshListen = flag.String("mesh.listen-address", net.JoinHostPort("0.0.0.0", strconv.Itoa(mesh.Port)), "mesh listen address") hwaddr = flag.String("mesh.hardware-address", mustHardwareAddr(), "MAC address, i.e. mesh peer ID") nickname = flag.String("mesh.nickname", mustHostname(), "peer nickname") ) flag.Var(peers, "mesh.peer", "initial peers (may be repeated)") flag.Parse() if len(flag.Args()) > 0 { log.Fatalln("Received unexpected and unparsed arguments: ", strings.Join(flag.Args(), ", ")) } if *showVersion { fmt.Fprintln(os.Stdout, version.Print("alertmanager")) os.Exit(0) } log.Infoln("Starting alertmanager", version.Info()) log.Infoln("Build context", version.BuildContext()) err := os.MkdirAll(*dataDir, 0777) if err != nil { log.Fatal(err) } logger := log.NewLogger(os.Stderr) mrouter := initMesh(*meshListen, *hwaddr, *nickname) stopc := make(chan struct{}) var wg sync.WaitGroup wg.Add(1) notificationLog, err := nflog.New( nflog.WithMesh(func(g mesh.Gossiper) mesh.Gossip { return mrouter.NewGossip("nflog", g) }), nflog.WithRetention(*retention), nflog.WithSnapshot(filepath.Join(*dataDir, "nflog")), nflog.WithMaintenance(15*time.Minute, stopc, wg.Done), nflog.WithLogger(logger.With("component", "nflog")), ) if err != nil { log.Fatal(err) } marker := types.NewMarker() silences, err := silence.New(silence.Options{ SnapshotFile: filepath.Join(*dataDir, "silences"), Retention: *retention, Logger: logger.With("component", "silences"), Gossip: func(g mesh.Gossiper) mesh.Gossip { return mrouter.NewGossip("silences", g) }, }) if err != nil { log.Fatal(err) } // Start providers before router potentially sends updates. wg.Add(1) go func() { silences.Maintenance(15*time.Minute, filepath.Join(*dataDir, "silences"), stopc) wg.Done() }() mrouter.Start() defer func() { close(stopc) // Stop receiving updates from router before shutting down. mrouter.Stop() wg.Wait() }() mrouter.ConnectionMaker.InitiateConnections(peers.slice(), true) alerts, err := mem.NewAlerts(*dataDir) if err != nil { log.Fatal(err) } defer alerts.Close() var ( inhibitor *inhibit.Inhibitor tmpl *template.Template pipeline notify.Stage disp *dispatch.Dispatcher ) defer disp.Stop() apiv := api.New(alerts, silences, func() dispatch.AlertOverview { return disp.Groups() }) amURL, err := extURL(*listenAddress, *externalURL) if err != nil { log.Fatal(err) } waitFunc := meshWait(mrouter, 5*time.Second) timeoutFunc := func(d time.Duration) time.Duration { if d < notify.MinTimeout { d = notify.MinTimeout } return d + waitFunc() } reload := func() (err error) { log.With("file", *configFile).Infof("Loading configuration file") defer func() { if err != nil { log.With("file", *configFile).Errorf("Loading configuration file failed: %s", err) configSuccess.Set(0) } else { configSuccess.Set(1) configSuccessTime.Set(float64(time.Now().Unix())) } }() conf, err := config.LoadFile(*configFile) if err != nil { return err } apiv.Update(conf.String(), time.Duration(conf.Global.ResolveTimeout)) tmpl, err = template.FromGlobs(conf.Templates...) if err != nil { return err } tmpl.ExternalURL = amURL inhibitor.Stop() disp.Stop() inhibitor = inhibit.NewInhibitor(alerts, conf.InhibitRules, marker) pipeline = notify.BuildPipeline( conf.Receivers, tmpl, waitFunc, inhibitor, silences, notificationLog, marker, ) disp = dispatch.NewDispatcher(alerts, dispatch.NewRoute(conf.Route, nil), pipeline, marker, timeoutFunc) go disp.Run() go inhibitor.Run() return nil } if err := reload(); err != nil { os.Exit(1) } router := route.New() webReload := make(chan struct{}) ui.Register(router.WithPrefix(amURL.Path), webReload) apiv.Register(router.WithPrefix(path.Join(amURL.Path, "/api"))) log.Infoln("Listening on", *listenAddress) go listen(*listenAddress, router) var ( hup = make(chan os.Signal) hupReady = make(chan bool) term = make(chan os.Signal) ) signal.Notify(hup, syscall.SIGHUP) signal.Notify(term, os.Interrupt, syscall.SIGTERM) go func() { <-hupReady for { select { case <-hup: case <-webReload: } reload() } }() // Wait for reload or termination signals. close(hupReady) // Unblock SIGHUP handler. <-term log.Infoln("Received SIGTERM, exiting gracefully...") }