Sync ui-rewrite with master (#779)
This commit is contained in:
parent
6784e300cf
commit
d463f1c298
45
CHANGELOG.md
45
CHANGELOG.md
|
@ -1,25 +1,36 @@
|
|||
## v0.6.2 / 2017-05-09
|
||||
|
||||
* [BUGFIX] Correctly link to silences from alert again
|
||||
* [BUGFIX] Correctly hide silenced/show active alerts in UI again
|
||||
* [BUGFIX] Fix regression of alerts not being displayed until first processing
|
||||
* [BUGFIX] Fix internal usage of wrong lock for silence markers
|
||||
* [BUGFIX] Adapt amtool's API parsing to recent API changes
|
||||
* [BUGFIX] Correctly marshal regexes in config JSON response
|
||||
* [ENHANCEMENT] Anchor silence regex matchers to be consistent with Prometheus
|
||||
* [ENHANCEMENT] Error if root route is using `continue` keyword
|
||||
|
||||
## v0.6.1 / 2017-04-28
|
||||
|
||||
- [BUGFIX] Fix incorrectly serialized hash for notification providers.
|
||||
- [ENHANCEMENT] Add config hash metric.
|
||||
- [ENHANCEMENT] Add processing status field to alerts.
|
||||
* [BUGFIX] Fix incorrectly serialized hash for notification providers.
|
||||
* [ENHANCEMENT] Add config hash metric.
|
||||
* [ENHANCEMENT] Add processing status field to alerts.
|
||||
|
||||
## v0.6.0 / 2017-04-25
|
||||
|
||||
- [BUGFIX] Add `groupKey` to `alerts/groups` endpoint https://github.com/prometheus/alertmanager/pull/576
|
||||
- [BUGFIX] Only notify on firing alerts https://github.com/prometheus/alertmanager/pull/595
|
||||
- [BUGFIX] Correctly marshal regex's in config for routing tree https://github.com/prometheus/alertmanager/pull/602
|
||||
- [BUGFIX] Prevent panic when failing to load config https://github.com/prometheus/alertmanager/pull/607
|
||||
- [BUGFIX] Prevent panic when alertmanager is started with an empty `-mesh.peer` https://github.com/prometheus/alertmanager/pull/726
|
||||
- [CHANGE] Add `DELETE` as accepted CORS method https://github.com/prometheus/alertmanager/pull/641
|
||||
- [CHANGE] Rename VictorOps config variables https://github.com/prometheus/alertmanager/pull/667
|
||||
- [CHANGE] Switch to using `gogoproto` for protobuf https://github.com/prometheus/alertmanager/pull/715
|
||||
- [CHANGE] No longer generate releases for openbsd/arm https://github.com/prometheus/alertmanager/pull/732
|
||||
- [ENHANCEMENT] Add `reReplaceAll` template function https://github.com/prometheus/alertmanager/pull/639
|
||||
- [ENHANCEMENT] Expose mesh peers on status page https://github.com/prometheus/alertmanager/pull/644
|
||||
- [ENHANCEMENT] Allow label-based filtering alerts/silences through API https://github.com/prometheus/alertmanager/pull/633
|
||||
- [ENHANCEMENT] Include notifier type in logs and errors https://github.com/prometheus/alertmanager/pull/702
|
||||
- [ENHANCEMENT] Add commandline tool for interacting with alertmanager https://github.com/prometheus/alertmanager/pull/636
|
||||
* [BUGFIX] Add `groupKey` to `alerts/groups` endpoint https://github.com/prometheus/alertmanager/pull/576
|
||||
* [BUGFIX] Only notify on firing alerts https://github.com/prometheus/alertmanager/pull/595
|
||||
* [BUGFIX] Correctly marshal regex's in config for routing tree https://github.com/prometheus/alertmanager/pull/602
|
||||
* [BUGFIX] Prevent panic when failing to load config https://github.com/prometheus/alertmanager/pull/607
|
||||
* [BUGFIX] Prevent panic when alertmanager is started with an empty `-mesh.peer` https://github.com/prometheus/alertmanager/pull/726
|
||||
* [CHANGE] Add `DELETE` as accepted CORS method https://github.com/prometheus/alertmanager/pull/641
|
||||
* [CHANGE] Rename VictorOps config variables https://github.com/prometheus/alertmanager/pull/667
|
||||
* [CHANGE] Switch to using `gogoproto` for protobuf https://github.com/prometheus/alertmanager/pull/715
|
||||
* [CHANGE] No longer generate releases for openbsd/arm https://github.com/prometheus/alertmanager/pull/732
|
||||
* [ENHANCEMENT] Add `reReplaceAll` template function https://github.com/prometheus/alertmanager/pull/639
|
||||
* [ENHANCEMENT] Expose mesh peers on status page https://github.com/prometheus/alertmanager/pull/644
|
||||
* [ENHANCEMENT] Allow label-based filtering alerts/silences through API https://github.com/prometheus/alertmanager/pull/633
|
||||
* [ENHANCEMENT] Include notifier type in logs and errors https://github.com/prometheus/alertmanager/pull/702
|
||||
* [ENHANCEMENT] Add commandline tool for interacting with alertmanager https://github.com/prometheus/alertmanager/pull/636
|
||||
|
||||
## v0.5.1 / 2016-11-24
|
||||
|
||||
|
|
10
api/api.go
10
api/api.go
|
@ -248,9 +248,7 @@ func (api *API) alertGroups(w http.ResponseWriter, req *http.Request) {
|
|||
type APIAlert struct {
|
||||
*model.Alert
|
||||
|
||||
Status types.AlertState `json:"status"`
|
||||
InhibitedBy []string `json:"inhibitedBy"`
|
||||
SilencedBy []string `json:"silencedBy"`
|
||||
Status types.AlertStatus `json:"status"`
|
||||
}
|
||||
|
||||
func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -289,10 +287,8 @@ func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
|||
status := api.getAlertStatus(a.Fingerprint())
|
||||
|
||||
apiAlert := &APIAlert{
|
||||
Alert: &a.Alert,
|
||||
Status: status.Status,
|
||||
SilencedBy: status.SilencedBy,
|
||||
InhibitedBy: status.InhibitedBy,
|
||||
Alert: &a.Alert,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
res = append(res, apiAlert)
|
||||
|
|
|
@ -28,7 +28,7 @@ type alertmanagerAlertResponse struct {
|
|||
|
||||
type alertGroup struct {
|
||||
Labels model.LabelSet `json:"labels"`
|
||||
GroupKey uint64 `json:"groupKey"`
|
||||
GroupKey string `json:"groupKey"`
|
||||
Blocks []*alertBlock `json:"blocks"`
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ func fetchAlerts(filter string) ([]*dispatch.APIAlert, error) {
|
|||
|
||||
err = json.NewDecoder(res.Body).Decode(&alertResponse)
|
||||
if err != nil {
|
||||
return []*dispatch.APIAlert{}, errors.New("Unable to decode json response")
|
||||
return []*dispatch.APIAlert{}, fmt.Errorf("Unable to decode json response: %s", err)
|
||||
}
|
||||
|
||||
if alertResponse.Status != "success" {
|
||||
|
@ -158,7 +158,7 @@ func queryAlerts(cmd *cobra.Command, args []string) error {
|
|||
|
||||
if !showSilenced {
|
||||
// If any silence mutes this alert don't show it
|
||||
if alert.Status == types.AlertStateSuppressed && len(alert.SilencedBy) > 0 {
|
||||
if alert.Status.State == types.AlertStateSuppressed && len(alert.Status.SilencedBy) > 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,12 +74,10 @@ func fetchConfig() (Config, error) {
|
|||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
|
||||
err = decoder.Decode(&configResponse)
|
||||
err = json.NewDecoder(res.Body).Decode(&configResponse)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return Config{}, err
|
||||
return configResponse.Data, err
|
||||
}
|
||||
|
||||
if configResponse.Status != "success" {
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
@ -51,6 +52,11 @@ func Load(s string) (*Config, error) {
|
|||
return nil, errors.New("no route provided in config")
|
||||
}
|
||||
|
||||
// Check if continue in root route.
|
||||
if cfg.Route.Continue {
|
||||
return nil, errors.New("cannot have continue in root route")
|
||||
}
|
||||
|
||||
cfg.original = s
|
||||
return cfg, nil
|
||||
}
|
||||
|
@ -498,7 +504,7 @@ func (re *Regexp) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (re *Regexp) MarshalJSON() ([]byte, error) {
|
||||
func (re Regexp) MarshalJSON() ([]byte, error) {
|
||||
if re.Regexp != nil {
|
||||
return json.Marshal(re.String())
|
||||
}
|
||||
|
|
|
@ -37,3 +37,26 @@ route:
|
|||
t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestContinueErrorInRouteRoot(t *testing.T) {
|
||||
in := `
|
||||
route:
|
||||
receiver: team-X-mails
|
||||
continue: true
|
||||
|
||||
receivers:
|
||||
- name: 'team-X-mails'
|
||||
`
|
||||
|
||||
_, err := Load(in)
|
||||
|
||||
expected := "cannot have continue in root route"
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned, expeceted:\n%q", expected)
|
||||
}
|
||||
if err.Error() != expected {
|
||||
t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,10 +80,7 @@ type AlertBlock struct {
|
|||
// annotated with silencing and inhibition info.
|
||||
type APIAlert struct {
|
||||
*model.Alert
|
||||
|
||||
Status types.AlertState
|
||||
InhibitedBy []string `json:"inhibitedBy"`
|
||||
SilencedBy []string `json:"silencedBy"`
|
||||
Status types.AlertStatus `json:"status"`
|
||||
}
|
||||
|
||||
// AlertGroup is a list of alert blocks grouped by the same label set.
|
||||
|
@ -138,10 +135,8 @@ func (d *Dispatcher) Groups(matchers []*labels.Matcher) AlertOverview {
|
|||
}
|
||||
status := d.marker.Status(a.Fingerprint())
|
||||
aa := &APIAlert{
|
||||
Alert: a,
|
||||
Status: status.Status,
|
||||
SilencedBy: status.SilencedBy,
|
||||
InhibitedBy: status.InhibitedBy,
|
||||
Alert: a,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
if !matchesFilterLabels(aa, matchers) {
|
||||
|
|
|
@ -169,8 +169,8 @@ func TestAlertsGC(t *testing.T) {
|
|||
}
|
||||
|
||||
s := marker.Status(a.Fingerprint())
|
||||
if s.Status != types.AlertStateUnprocessed {
|
||||
t.Errorf("marker %d didn't get GC'd", i)
|
||||
if s.State != types.AlertStateUnprocessed {
|
||||
t.Errorf("marker %d didn't get GC'd: %v", i, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func (m *Matcher) Init() error {
|
|||
if !m.IsRegex {
|
||||
return nil
|
||||
}
|
||||
re, err := regexp.Compile(m.Value)
|
||||
re, err := regexp.Compile("^(?:" + m.Value + ")$")
|
||||
if err == nil {
|
||||
m.regex = re
|
||||
}
|
||||
|
|
102
types/types.go
102
types/types.go
|
@ -14,7 +14,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -24,58 +23,19 @@ import (
|
|||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type AlertState uint8
|
||||
type AlertState string
|
||||
|
||||
const (
|
||||
AlertStateUnprocessed AlertState = iota
|
||||
AlertStateActive
|
||||
AlertStateSuppressed
|
||||
AlertStateUnprocessed AlertState = "unprocessed"
|
||||
AlertStateActive = "active"
|
||||
AlertStateSuppressed = "suppressed"
|
||||
)
|
||||
|
||||
// AlertStatus stores the state and values associated with an Alert.
|
||||
type AlertStatus struct {
|
||||
Status AlertState
|
||||
SilencedBy []string
|
||||
InhibitedBy []string
|
||||
}
|
||||
|
||||
// Save an allocation when serializing an empty string slice.
|
||||
var emptyVals = []string{}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (a *AlertStatus) MarshalJSON() ([]byte, error) {
|
||||
silencedBy := a.SilencedBy
|
||||
if silencedBy == nil {
|
||||
silencedBy = emptyVals
|
||||
}
|
||||
inhibitedBy := a.InhibitedBy
|
||||
if inhibitedBy == nil {
|
||||
inhibitedBy = emptyVals
|
||||
}
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"status": a.Status,
|
||||
"silencedBy": silencedBy,
|
||||
"inhibitedBy": inhibitedBy,
|
||||
})
|
||||
}
|
||||
|
||||
// Save an allocation when serializing the unknown response.
|
||||
const unknown = "unknown"
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (s AlertState) MarshalJSON() ([]byte, error) {
|
||||
status, found := statusMap[s]
|
||||
if !found {
|
||||
status = unknown
|
||||
}
|
||||
|
||||
return json.Marshal(status)
|
||||
}
|
||||
|
||||
var statusMap = map[AlertState]string{
|
||||
AlertStateUnprocessed: "unprocessed",
|
||||
AlertStateActive: "active",
|
||||
AlertStateSuppressed: "suppressed",
|
||||
State AlertState `json:"state"`
|
||||
SilencedBy []string `json:"silencedBy"`
|
||||
InhibitedBy []string `json:"inhibitedBy"`
|
||||
}
|
||||
|
||||
// Marker helps to mark alerts as silenced and/or inhibited.
|
||||
|
@ -109,8 +69,7 @@ type memMarker struct {
|
|||
|
||||
// SetSilenced sets the AlertStatus to suppressed and stores the associated silence IDs.
|
||||
func (m *memMarker) SetSilenced(alert model.Fingerprint, ids ...string) {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
m.mtx.Lock()
|
||||
|
||||
s, found := m.m[alert]
|
||||
if !found {
|
||||
|
@ -122,18 +81,20 @@ func (m *memMarker) SetSilenced(alert model.Fingerprint, ids ...string) {
|
|||
// fingerprint, it is suppressed. Otherwise, set it to
|
||||
// AlertStateUnprocessed.
|
||||
if len(ids) == 0 && len(s.InhibitedBy) == 0 {
|
||||
m.mtx.Unlock()
|
||||
m.SetActive(alert)
|
||||
return
|
||||
}
|
||||
|
||||
s.Status = AlertStateSuppressed
|
||||
s.State = AlertStateSuppressed
|
||||
s.SilencedBy = ids
|
||||
|
||||
m.mtx.Unlock()
|
||||
}
|
||||
|
||||
// SetInhibited sets the AlertStatus to suppressed and stores the associated alert IDs.
|
||||
func (m *memMarker) SetInhibited(alert model.Fingerprint, ids ...string) {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
m.mtx.Lock()
|
||||
|
||||
s, found := m.m[alert]
|
||||
if !found {
|
||||
|
@ -145,24 +106,31 @@ func (m *memMarker) SetInhibited(alert model.Fingerprint, ids ...string) {
|
|||
// fingerprint, it is suppressed. Otherwise, set it to
|
||||
// AlertStateUnprocessed.
|
||||
if len(ids) == 0 && len(s.SilencedBy) == 0 {
|
||||
m.mtx.Unlock()
|
||||
m.SetActive(alert)
|
||||
return
|
||||
}
|
||||
|
||||
s.Status = AlertStateSuppressed
|
||||
s.State = AlertStateSuppressed
|
||||
s.InhibitedBy = ids
|
||||
|
||||
m.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (m *memMarker) SetActive(alert model.Fingerprint) {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
s, found := m.m[alert]
|
||||
if !found {
|
||||
s = &AlertStatus{}
|
||||
s = &AlertStatus{
|
||||
SilencedBy: []string{},
|
||||
InhibitedBy: []string{},
|
||||
}
|
||||
m.m[alert] = s
|
||||
}
|
||||
|
||||
s.Status = AlertStateActive
|
||||
s.State = AlertStateActive
|
||||
s.SilencedBy = []string{}
|
||||
s.InhibitedBy = []string{}
|
||||
}
|
||||
|
@ -174,17 +142,19 @@ func (m *memMarker) Status(alert model.Fingerprint) AlertStatus {
|
|||
|
||||
s, found := m.m[alert]
|
||||
if !found {
|
||||
s = &AlertStatus{}
|
||||
m.m[alert] = s
|
||||
s = &AlertStatus{
|
||||
State: AlertStateUnprocessed,
|
||||
SilencedBy: []string{},
|
||||
InhibitedBy: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
return *s
|
||||
}
|
||||
|
||||
// Delete deletes the given Fingerprint from the internal cache.
|
||||
func (m *memMarker) Delete(alert model.Fingerprint) {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
delete(m.m, alert)
|
||||
}
|
||||
|
@ -192,13 +162,13 @@ func (m *memMarker) Delete(alert model.Fingerprint) {
|
|||
// Unprocessed returns whether the alert for the given Fingerprint is in the
|
||||
// Unprocessed state.
|
||||
func (m *memMarker) Unprocessed(alert model.Fingerprint) bool {
|
||||
return m.Status(alert).Status == AlertStateUnprocessed
|
||||
return m.Status(alert).State == AlertStateUnprocessed
|
||||
}
|
||||
|
||||
// Active returns whether the alert for the given Fingerprint is in the Active
|
||||
// state.
|
||||
func (m *memMarker) Active(alert model.Fingerprint) bool {
|
||||
return m.Status(alert).Status == AlertStateActive
|
||||
return m.Status(alert).State == AlertStateActive
|
||||
}
|
||||
|
||||
// Inhibited returns whether the alert for the given Fingerprint is in the
|
||||
|
@ -206,7 +176,7 @@ func (m *memMarker) Active(alert model.Fingerprint) bool {
|
|||
func (m *memMarker) Inhibited(alert model.Fingerprint) ([]string, bool) {
|
||||
s := m.Status(alert)
|
||||
return s.InhibitedBy,
|
||||
s.Status == AlertStateSuppressed && len(s.InhibitedBy) > 0
|
||||
s.State == AlertStateSuppressed && len(s.InhibitedBy) > 0
|
||||
}
|
||||
|
||||
// Silenced returns whether the alert for the given Fingerprint is in the
|
||||
|
@ -214,7 +184,7 @@ func (m *memMarker) Inhibited(alert model.Fingerprint) ([]string, bool) {
|
|||
func (m *memMarker) Silenced(alert model.Fingerprint) ([]string, bool) {
|
||||
s := m.Status(alert)
|
||||
return s.SilencedBy,
|
||||
s.Status == AlertStateSuppressed && len(s.SilencedBy) > 0
|
||||
s.State == AlertStateSuppressed && len(s.SilencedBy) > 0
|
||||
}
|
||||
|
||||
// MultiError contains multiple errors and implements the error interface. Its
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -62,70 +61,3 @@ func TestAlertMerge(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertStatusMarshal(t *testing.T) {
|
||||
type statusTest struct {
|
||||
alertStatus AlertStatus
|
||||
status string
|
||||
inhibitedBy, silencedBy []string
|
||||
}
|
||||
|
||||
tests := []statusTest{
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{},
|
||||
status: "unprocessed",
|
||||
inhibitedBy: []string{},
|
||||
silencedBy: []string{},
|
||||
},
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{Status: AlertStateUnprocessed},
|
||||
status: "unprocessed",
|
||||
inhibitedBy: []string{},
|
||||
silencedBy: []string{},
|
||||
},
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{Status: AlertStateActive},
|
||||
status: "active",
|
||||
inhibitedBy: []string{},
|
||||
silencedBy: []string{},
|
||||
},
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{Status: AlertStateSuppressed, SilencedBy: []string{"123456"}},
|
||||
status: "suppressed",
|
||||
inhibitedBy: []string{},
|
||||
silencedBy: []string{"123456"},
|
||||
},
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{Status: AlertStateSuppressed, SilencedBy: []string{"123456"}, InhibitedBy: []string{"123", "456"}},
|
||||
status: "suppressed",
|
||||
inhibitedBy: []string{"123", "456"},
|
||||
silencedBy: []string{"123456"},
|
||||
},
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{Status: AlertStateSuppressed},
|
||||
status: "suppressed",
|
||||
silencedBy: []string{},
|
||||
inhibitedBy: []string{},
|
||||
},
|
||||
statusTest{
|
||||
alertStatus: AlertStatus{Status: 255},
|
||||
status: "unknown",
|
||||
silencedBy: []string{},
|
||||
inhibitedBy: []string{},
|
||||
},
|
||||
}
|
||||
for _, asTest := range tests {
|
||||
b, err := json.Marshal(&asTest.alertStatus)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expectedJSON, _ := json.Marshal(map[string]interface{}{
|
||||
"status": asTest.status,
|
||||
"silencedBy": asTest.silencedBy,
|
||||
"inhibitedBy": asTest.inhibitedBy,
|
||||
})
|
||||
if string(b) != string(expectedJSON) {
|
||||
t.Errorf("%v serialization failed, expected %s, got %s", asTest.alertStatus, expectedJSON, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue