diff --git a/test/acceptance.go b/test/acceptance.go index 384a437e..ed392007 100644 --- a/test/acceptance.go +++ b/test/acceptance.go @@ -13,7 +13,9 @@ import ( "testing" "time" + "github.com/prometheus/client_golang/api/alertmanager" "github.com/prometheus/common/model" + "golang.org/x/net/context" ) type AcceptanceTest struct { @@ -57,7 +59,7 @@ func freeAddress() string { if err != nil { panic(err) } - defer l.Close() + l.Close() return l.Addr().String() } @@ -81,13 +83,20 @@ func (t *AcceptanceTest) Alertmanager() *Alertmanager { t.Fatal(err) } - addr := freeAddress() + am.addr = freeAddress() + + client, err := alertmanager.New(alertmanager.Config{ + Address: fmt.Sprintf("http://%s", am.addr), + }) + if err != nil { + t.Error(err) + } + am.client = client - am.url = fmt.Sprintf("http://%s", addr) am.cmd = exec.Command("../../alertmanager", "-config.file", cf.Name(), "-log.level", "debug", - "-web.listen-address", addr, + "-web.listen-address", am.addr, ) var outb, errb bytes.Buffer @@ -149,16 +158,17 @@ func (t *AcceptanceTest) Run() { // declaring alerts being pushed to it at fixed points in time. type Alertmanager struct { t *testing.T - url string - cmd *exec.Cmd opts *AcceptanceOpts + addr string + client alertmanager.Client + cmd *exec.Cmd confFile *os.File actions map[float64][]func() } -// push declares alerts that are to be pushed to the Alertmanager +// Push declares alerts that are to be pushed to the Alertmanager // server at a relative point in time. func (am *Alertmanager) Push(at float64, alerts ...*TestAlert) { var nas model.Alerts @@ -173,7 +183,7 @@ func (am *Alertmanager) Push(at float64, alerts ...*TestAlert) { return } - resp, err := http.Post(am.url+"/api/alerts", "application/json", &buf) + resp, err := http.Post(fmt.Sprintf("http://%s/api/alerts", am.addr), "application/json", &buf) if err != nil { am.t.Error(err) return @@ -182,14 +192,32 @@ func (am *Alertmanager) Push(at float64, alerts ...*TestAlert) { }) } -func (am *Alertmanager) SetSilence(at float64) { +// SetSilence updates or creates the given Silence. +func (am *Alertmanager) SetSilence(at float64, sil *TestSilence) { + silences := alertmanager.NewSilenceAPI(am.client) + am.Do(at, func() { + sid, err := silences.Set(context.Background(), sil.nativeSilence(am.opts)) + if err != nil { + am.t.Error(err) + return + } + sil.ID = sid + }) } -func (am *Alertmanager) DelSilence(at float64) { +// DelSilence deletes the silence with the sid at the given time. +func (am *Alertmanager) DelSilence(at float64, sil *TestSilence) { + silences := alertmanager.NewSilenceAPI(am.client) + am.Do(at, func() { + if err := silences.Del(context.Background(), sil.ID); err != nil { + am.t.Error(err) + } + }) } +// Do sets the given function to be executed at the given time. func (am *Alertmanager) Do(at float64, f func()) { am.actions[at] = append(am.actions[at], f) } diff --git a/test/mock.go b/test/mock.go index 78077d11..738e50cd 100644 --- a/test/mock.go +++ b/test/mock.go @@ -12,12 +12,6 @@ import ( "github.com/prometheus/alertmanager/notify" ) -type TestAlert struct { - labels model.LabelSet - annotations model.LabelSet - startsAt, endsAt float64 -} - // At is a convenience method to allow for declarative syntax of Acceptance // test definitions. func At(ts float64) float64 { @@ -42,6 +36,73 @@ func Between(start, end float64) Interval { return Interval{start: start, end: end} } +// TestSilence models a model.Silence with relative times. +type TestSilence struct { + ID uint64 + match []string + matchRE []string + startsAt, endsAt float64 +} + +// Silence creates a new TestSilence active for the relative interval given +// by start and end. +func Silence(start, end float64) *TestSilence { + return &TestSilence{ + startsAt: start, + endsAt: end, + } +} + +// Match adds a new plain matcher to the silence. +func (s *TestSilence) Match(v ...string) *TestSilence { + s.match = append(s.match, v...) + return s +} + +// MatchRE adds a new regex matcher to the silence +func (s *TestSilence) MatchRE(v ...string) *TestSilence { + if len(v)%2 == 1 { + panic("bad key/values") + } + s.matchRE = append(s.matchRE, v...) + return s +} + +// nativeSilence converts the declared test silence into a regular +// silence with resolved times. +func (sil *TestSilence) nativeSilence(opts *AcceptanceOpts) *model.Silence { + nsil := &model.Silence{} + + for i := 0; i < len(sil.match); i += 2 { + nsil.Matchers = append(nsil.Matchers, &model.Matcher{ + Name: model.LabelName(sil.match[i]), + Value: sil.match[i+1], + }) + } + for i := 0; i < len(sil.matchRE); i += 2 { + nsil.Matchers = append(nsil.Matchers, &model.Matcher{ + Name: model.LabelName(sil.match[i]), + Value: sil.match[i+1], + IsRegex: true, + }) + } + + if sil.startsAt > 0 { + nsil.StartsAt = opts.expandTime(sil.startsAt) + } + if sil.endsAt > 0 { + nsil.EndsAt = opts.expandTime(sil.endsAt) + } + return nsil +} + +// TestAlert models a model.Alert with relative times. +type TestAlert struct { + labels model.LabelSet + annotations model.LabelSet + startsAt, endsAt float64 +} + // alert creates a new alert declaration with the given key/value pairs // as identifying labels. func Alert(keyval ...interface{}) *TestAlert { diff --git a/vendor/github.com/prometheus/common/model/silence.go b/vendor/github.com/prometheus/common/model/silence.go index ede9bf24..6d142fd8 100644 --- a/vendor/github.com/prometheus/common/model/silence.go +++ b/vendor/github.com/prometheus/common/model/silence.go @@ -37,7 +37,7 @@ func (m *Matcher) UnmarshalJSON(b []byte) error { return fmt.Errorf("label name in matcher must not be empty") } if m.IsRegex { - if _, err := regexp.Compile(expr); err != nil { + if _, err := regexp.Compile(m.Value); err != nil { return err } } diff --git a/vendor/vendor.json b/vendor/vendor.json index 44521ffe..9cb28c47 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -59,8 +59,8 @@ }, { "path": "github.com/prometheus/common/model", - "revision": "4e3fbe8499d234bee309a86d934b0f77d2963a17", - "revisionTime": "2015-10-01T18:06:56+02:00" + "revision": "4c9032a3f7aceade2585f2bbe302bf8c9ea3e9a9", + "revisionTime": "2015-10-01T18:35:54+02:00" }, { "path": "github.com/prometheus/common/route",