Merge pull request #402 from prometheus/master

Merge changes into release-0.2
This commit is contained in:
Fabian Reinartz 2016-06-23 10:54:37 +02:00 committed by GitHub
commit 8da61aa1b0
6 changed files with 160 additions and 38 deletions

7
api.go
View File

@ -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,

View File

@ -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)
}

39
config/config_test.go Normal file
View File

@ -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())
}
}

View File

@ -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")

View File

@ -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

View File

@ -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 {