Merge pull request #402 from prometheus/master
Merge changes into release-0.2
This commit is contained in:
commit
8da61aa1b0
7
api.go
7
api.go
|
@ -283,14 +283,15 @@ func (api *API) insertAlerts(w http.ResponseWriter, r *http.Request, alerts ...*
|
|||
}
|
||||
|
||||
func (api *API) addSilence(w http.ResponseWriter, r *http.Request) {
|
||||
var sil types.Silence
|
||||
if err := receive(r, &sil); err != nil {
|
||||
var msil model.Silence
|
||||
if err := receive(r, &msil); err != nil {
|
||||
respondError(w, apiError{
|
||||
typ: errorBadData,
|
||||
err: err,
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
sil := types.NewSilence(&msil)
|
||||
|
||||
if sil.CreatedAt.IsZero() {
|
||||
sil.CreatedAt = time.Now()
|
||||
|
@ -304,7 +305,7 @@ func (api *API) addSilence(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
sid, err := api.silences.Set(&sil)
|
||||
sid, err := api.silences.Set(sil)
|
||||
if err != nil {
|
||||
respondError(w, apiError{
|
||||
typ: errorInternal,
|
||||
|
|
|
@ -225,6 +225,9 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if c.Route == nil {
|
||||
return fmt.Errorf("No routes provided")
|
||||
}
|
||||
if len(c.Route.Receiver) == 0 {
|
||||
return fmt.Errorf("Root route must specify a default receiver")
|
||||
}
|
||||
if len(c.Route.Match) > 0 || len(c.Route.MatchRE) > 0 {
|
||||
return fmt.Errorf("Root route must not have any matchers")
|
||||
}
|
||||
|
@ -240,6 +243,9 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
// checkReceiver returns an error if a node in the routing tree
|
||||
// references a receiver not in the given map.
|
||||
func checkReceiver(r *Route, receivers map[string]struct{}) error {
|
||||
if r.Receiver == "" {
|
||||
return nil
|
||||
}
|
||||
if _, ok := receivers[r.Receiver]; !ok {
|
||||
return fmt.Errorf("Undefined receiver %q used in route", r.Receiver)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2016 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestDefaultReceiverExists(t *testing.T) {
|
||||
in := `
|
||||
route:
|
||||
group_wait: 30s
|
||||
`
|
||||
|
||||
conf := &Config{}
|
||||
err := yaml.Unmarshal([]byte(in), conf)
|
||||
|
||||
expected := "Root route must specify a default receiver"
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned, expected:\n%v", expected)
|
||||
}
|
||||
if err.Error() != expected {
|
||||
t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error())
|
||||
}
|
||||
}
|
|
@ -124,7 +124,7 @@ func TestInhibitRuleHasEqual(t *testing.T) {
|
|||
}
|
||||
|
||||
if have := r.hasEqual(c.input); have != c.result {
|
||||
t.Errorf("Unexpected result %q, expected %q", have, c.result)
|
||||
t.Errorf("Unexpected result %t, expected %t", have, c.result)
|
||||
}
|
||||
if !reflect.DeepEqual(r.scache, c.initial) {
|
||||
t.Errorf("Cache state unexpectedly changed")
|
||||
|
|
|
@ -217,6 +217,9 @@ func (a *Alerts) Put(alerts ...*types.Alert) error {
|
|||
type Silences struct {
|
||||
db *bolt.DB
|
||||
mk types.Marker
|
||||
|
||||
mtx sync.RWMutex
|
||||
cache map[uint64]*types.Silence
|
||||
}
|
||||
|
||||
// NewSilences creates a new Silences provider.
|
||||
|
@ -229,7 +232,15 @@ func NewSilences(path string, mk types.Marker) (*Silences, error) {
|
|||
_, err := tx.CreateBucketIfNotExists(bktSilences)
|
||||
return err
|
||||
})
|
||||
return &Silences{db: db, mk: mk}, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Silences{
|
||||
db: db,
|
||||
mk: mk,
|
||||
cache: map[uint64]*types.Silence{},
|
||||
}
|
||||
return s, s.initCache()
|
||||
}
|
||||
|
||||
// Close the silences provider.
|
||||
|
@ -261,7 +272,19 @@ func (s *Silences) Mutes(lset model.LabelSet) bool {
|
|||
|
||||
// All returns all existing silences.
|
||||
func (s *Silences) All() ([]*types.Silence, error) {
|
||||
var res []*types.Silence
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
|
||||
res := make([]*types.Silence, 0, len(s.cache))
|
||||
for _, s := range s.cache {
|
||||
res = append(res, s)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *Silences) initCache() error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(bktSilences)
|
||||
|
@ -272,23 +295,21 @@ func (s *Silences) All() ([]*types.Silence, error) {
|
|||
if err := json.Unmarshal(v, &ms); err != nil {
|
||||
return err
|
||||
}
|
||||
ms.ID = binary.BigEndian.Uint64(k)
|
||||
|
||||
if err := json.Unmarshal(v, &ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res = append(res, types.NewSilence(&ms))
|
||||
// The ID is duplicated in the value and always equal
|
||||
// to the stored key.
|
||||
s.cache[ms.ID] = types.NewSilence(&ms)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return res, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a new silence.
|
||||
func (s *Silences) Set(sil *types.Silence) (uint64, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
var (
|
||||
uid uint64
|
||||
err error
|
||||
|
@ -312,11 +333,18 @@ func (s *Silences) Set(sil *types.Silence) (uint64, error) {
|
|||
}
|
||||
return b.Put(k, msb)
|
||||
})
|
||||
return uid, err
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
s.cache[uid] = sil
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
// Del removes a silence.
|
||||
func (s *Silences) Del(uid uint64) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
err := s.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(bktSilences)
|
||||
|
||||
|
@ -325,33 +353,23 @@ func (s *Silences) Del(uid uint64) error {
|
|||
|
||||
return b.Delete(k)
|
||||
})
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(s.cache, uid)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get a silence associated with a fingerprint.
|
||||
func (s *Silences) Get(uid uint64) (*types.Silence, error) {
|
||||
var sil *types.Silence
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(bktSilences)
|
||||
|
||||
k := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(k, uid)
|
||||
|
||||
v := b.Get(k)
|
||||
if v == nil {
|
||||
return provider.ErrNotFound
|
||||
}
|
||||
var ms model.Silence
|
||||
|
||||
if err := json.Unmarshal(v, &ms); err != nil {
|
||||
return err
|
||||
}
|
||||
sil = types.NewSilence(&ms)
|
||||
|
||||
return nil
|
||||
})
|
||||
return sil, err
|
||||
sil, ok := s.cache[uid]
|
||||
if !ok {
|
||||
return nil, provider.ErrNotFound
|
||||
}
|
||||
return sil, nil
|
||||
}
|
||||
|
||||
// NotificationInfo provides information about pending and successful
|
||||
|
|
|
@ -65,6 +65,19 @@ routes:
|
|||
group_by: ['foo', 'bar']
|
||||
group_wait: 2m
|
||||
receiver: 'notify-BC'
|
||||
|
||||
- match:
|
||||
group_by: 'role'
|
||||
group_by: ['role']
|
||||
|
||||
routes:
|
||||
- match:
|
||||
env: 'testing'
|
||||
receiver: 'notify-testing'
|
||||
routes:
|
||||
- match:
|
||||
wait: 'long'
|
||||
group_wait: 2m
|
||||
`
|
||||
|
||||
var ctree config.Route
|
||||
|
@ -167,6 +180,51 @@ routes:
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"group_by": "role",
|
||||
},
|
||||
result: []*RouteOpts{
|
||||
{
|
||||
Receiver: "notify-def",
|
||||
GroupBy: lset("role"),
|
||||
GroupWait: def.GroupWait,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"env": "testing",
|
||||
"group_by": "role",
|
||||
},
|
||||
result: []*RouteOpts{
|
||||
{
|
||||
Receiver: "notify-testing",
|
||||
GroupBy: lset("role"),
|
||||
GroupWait: def.GroupWait,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"env": "testing",
|
||||
"group_by": "role",
|
||||
"wait": "long",
|
||||
},
|
||||
result: []*RouteOpts{
|
||||
{
|
||||
Receiver: "notify-testing",
|
||||
GroupBy: lset("role"),
|
||||
GroupWait: 2 * time.Minute,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
Loading…
Reference in New Issue