Merge release 0.20 (#2140)
* Revert "slack: retry 429 errors (#2112)" (#2128) This reverts commit26cc96a787
. Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Revert "config: remove support for JSON marshaling (#2086)" (#2133) This reverts commit918f08b66a
. Signed-off-by: Simon Pasquier <spasquie@redhat.com> * config: fix JSON unmarshaling for HostPort (#2134) Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Cut 0.20.0 (#2137) Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
parent
3640bb8d55
commit
b49ebfc683
|
@ -1,4 +1,4 @@
|
||||||
## 0.20.0-rc.0 / 2019-11-27
|
## 0.20.0 / 2019-12-11
|
||||||
|
|
||||||
* [CHANGE] Check that at least one silence matcher matches a non-empty string. #2081
|
* [CHANGE] Check that at least one silence matcher matches a non-empty string. #2081
|
||||||
* [ENHANCEMENT] [pagerduty] Check that PagerDuty keys aren't empty. #2085
|
* [ENHANCEMENT] [pagerduty] Check that PagerDuty keys aren't empty. #2085
|
||||||
|
@ -11,7 +11,6 @@
|
||||||
* [BUGFIX] Don't garbage-collect alerts from the store. #2040
|
* [BUGFIX] Don't garbage-collect alerts from the store. #2040
|
||||||
* [BUGFIX] [ui] Disable the grammarly plugin on all textareas. #2061
|
* [BUGFIX] [ui] Disable the grammarly plugin on all textareas. #2061
|
||||||
* [BUGFIX] [config] Forbid nil regexp matchers. #2083
|
* [BUGFIX] [config] Forbid nil regexp matchers. #2083
|
||||||
* [BUGFIX] [slack] Retry 429 errors. #2112
|
|
||||||
* [BUGFIX] [ui] Fix Silences UI when several filters are applied. #2075
|
* [BUGFIX] [ui] Fix Silences UI when several filters are applied. #2075
|
||||||
|
|
||||||
Contributors:
|
Contributors:
|
||||||
|
|
|
@ -90,9 +90,18 @@ func TestAPI(t *testing.T) {
|
||||||
client := &fakeAPIClient{T: t, ch: make(chan fakeAPIResponse, 1)}
|
client := &fakeAPIClient{T: t, ch: make(chan fakeAPIResponse, 1)}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
|
u, err := url.Parse("http://example.com")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
statusData := &ServerStatus{
|
statusData := &ServerStatus{
|
||||||
ConfigYAML: "{}",
|
ConfigYAML: "{}",
|
||||||
ConfigJSON: &config.Config{},
|
ConfigJSON: &config.Config{
|
||||||
|
Global: &config.GlobalConfig{
|
||||||
|
PagerdutyURL: &config.URL{URL: u},
|
||||||
|
SMTPSmarthost: config.HostPort{Host: "localhost", Port: "25"},
|
||||||
|
},
|
||||||
|
},
|
||||||
VersionInfo: map[string]string{"version": "v1"},
|
VersionInfo: map[string]string{"version": "v1"},
|
||||||
Uptime: now,
|
Uptime: now,
|
||||||
ClusterStatus: &ClusterStatus{Peers: []PeerStatus{}},
|
ClusterStatus: &ClusterStatus{Peers: []PeerStatus{}},
|
||||||
|
|
|
@ -105,6 +105,20 @@ func (u URL) MarshalJSON() ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Marshaler interface for URL.
|
||||||
|
func (u *URL) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
urlp, err := parseURL(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.URL = urlp.URL
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SecretURL is a URL that must not be revealed on marshaling.
|
// SecretURL is a URL that must not be revealed on marshaling.
|
||||||
type SecretURL URL
|
type SecretURL URL
|
||||||
|
|
||||||
|
@ -137,6 +151,18 @@ func (s SecretURL) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(secretToken)
|
return json.Marshal(secretToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Marshaler interface for SecretURL.
|
||||||
|
func (s *SecretURL) UnmarshalJSON(data []byte) error {
|
||||||
|
// In order to deserialize a previously serialized configuration (eg from
|
||||||
|
// the Alertmanager API with amtool), `<secret>` needs to be treated
|
||||||
|
// specially, as it isn't a valid URL.
|
||||||
|
if string(data) == secretToken || string(data) == secretTokenJSON {
|
||||||
|
s.URL = &url.URL{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return json.Unmarshal(data, (*URL)(s))
|
||||||
|
}
|
||||||
|
|
||||||
// Load parses the YAML input s into a Config.
|
// Load parses the YAML input s into a Config.
|
||||||
func Load(s string) (*Config, error) {
|
func Load(s string) (*Config, error) {
|
||||||
cfg := &Config{}
|
cfg := &Config{}
|
||||||
|
@ -490,6 +516,28 @@ func (hp *HostPort) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface for HostPort.
|
||||||
|
func (hp *HostPort) UnmarshalJSON(data []byte) error {
|
||||||
|
var (
|
||||||
|
s string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if err = json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
hp.Host, hp.Port, err = net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hp.Port == "" {
|
||||||
|
return errors.Errorf("address %q: port cannot be empty", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalYAML implements the yaml.Marshaler interface for HostPort.
|
// MarshalYAML implements the yaml.Marshaler interface for HostPort.
|
||||||
func (hp HostPort) MarshalYAML() (interface{}, error) {
|
func (hp HostPort) MarshalYAML() (interface{}, error) {
|
||||||
return hp.String(), nil
|
return hp.String(), nil
|
||||||
|
@ -729,3 +777,26 @@ func (re Regexp) MarshalYAML() (interface{}, error) {
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Marshaler interface for Regexp
|
||||||
|
func (re *Regexp) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
regex, err := regexp.Compile("^(?:" + s + ")$")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
re.Regexp = regex
|
||||||
|
re.original = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface for Regexp.
|
||||||
|
func (re Regexp) MarshalJSON() ([]byte, error) {
|
||||||
|
if re.original != "" {
|
||||||
|
return json.Marshal(re.original)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -367,13 +367,27 @@ func TestMarshalSecretURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
u := &SecretURL{urlp}
|
u := &SecretURL{urlp}
|
||||||
|
|
||||||
c, err := yaml.Marshal(u)
|
c, err := json.Marshal(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// u003c -> "<"
|
||||||
|
// u003e -> ">"
|
||||||
|
require.Equal(t, "\"\\u003csecret\\u003e\"", string(c), "SecretURL not properly elided in JSON.")
|
||||||
|
// Check that the marshaled data can be unmarshaled again.
|
||||||
|
out := &SecretURL{}
|
||||||
|
err = json.Unmarshal(c, out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = yaml.Marshal(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
require.Equal(t, "<secret>\n", string(c), "SecretURL not properly elided in YAML.")
|
require.Equal(t, "<secret>\n", string(c), "SecretURL not properly elided in YAML.")
|
||||||
// Check that the marshaled data can be unmarshaled again.
|
// Check that the marshaled data can be unmarshaled again.
|
||||||
out := &SecretURL{}
|
out = &SecretURL{}
|
||||||
err = yaml.Unmarshal(c, &out)
|
err = yaml.Unmarshal(c, &out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -384,7 +398,13 @@ func TestUnmarshalSecretURL(t *testing.T) {
|
||||||
b := []byte(`"http://example.com/se cret"`)
|
b := []byte(`"http://example.com/se cret"`)
|
||||||
var u SecretURL
|
var u SecretURL
|
||||||
|
|
||||||
err := yaml.Unmarshal(b, &u)
|
err := json.Unmarshal(b, &u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
require.Equal(t, "http://example.com/se%20cret", u.String(), "SecretURL not properly unmarshalled in JSON.")
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, &u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -416,7 +436,13 @@ func TestUnmarshalURL(t *testing.T) {
|
||||||
b := []byte(`"http://example.com/a b"`)
|
b := []byte(`"http://example.com/a b"`)
|
||||||
var u URL
|
var u URL
|
||||||
|
|
||||||
err := yaml.Unmarshal(b, &u)
|
err := json.Unmarshal(b, &u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
require.Equal(t, "http://example.com/a%20b", u.String(), "URL not properly unmarshalled in JSON.")
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, &u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -431,7 +457,12 @@ func TestUnmarshalInvalidURL(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
var u URL
|
var u URL
|
||||||
|
|
||||||
err := yaml.Unmarshal(b, &u)
|
err := json.Unmarshal(b, &u)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected an error unmarshalling %q from JSON", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, &u)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected an error unmarshalling %q from YAML", string(b))
|
t.Errorf("Expected an error unmarshalling %q from YAML", string(b))
|
||||||
}
|
}
|
||||||
|
@ -443,10 +474,27 @@ func TestUnmarshalRelativeURL(t *testing.T) {
|
||||||
b := []byte(`"/home"`)
|
b := []byte(`"/home"`)
|
||||||
var u URL
|
var u URL
|
||||||
|
|
||||||
err := yaml.Unmarshal(b, &u)
|
err := json.Unmarshal(b, &u)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected an error parsing URL")
|
t.Errorf("Expected an error parsing URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, &u)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected an error parsing URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONUnmarshal(t *testing.T) {
|
||||||
|
c, err := LoadFile("testdata/conf.good.yml")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("JSON Marshaling failed:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalIdempotency(t *testing.T) {
|
func TestMarshalIdempotency(t *testing.T) {
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package dispatch
|
package dispatch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -181,3 +182,26 @@ func (ro *RouteOpts) String() string {
|
||||||
return fmt.Sprintf("<RouteOpts send_to:%q group_by:%q group_by_all:%t timers:%q|%q>",
|
return fmt.Sprintf("<RouteOpts send_to:%q group_by:%q group_by_all:%t timers:%q|%q>",
|
||||||
ro.Receiver, labels, ro.GroupByAll, ro.GroupWait, ro.GroupInterval)
|
ro.Receiver, labels, ro.GroupByAll, ro.GroupWait, ro.GroupInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns a JSON representation of the routing options.
|
||||||
|
func (ro *RouteOpts) MarshalJSON() ([]byte, error) {
|
||||||
|
v := struct {
|
||||||
|
Receiver string `json:"receiver"`
|
||||||
|
GroupBy model.LabelNames `json:"groupBy"`
|
||||||
|
GroupByAll bool `json:"groupByAll"`
|
||||||
|
GroupWait time.Duration `json:"groupWait"`
|
||||||
|
GroupInterval time.Duration `json:"groupInterval"`
|
||||||
|
RepeatInterval time.Duration `json:"repeatInterval"`
|
||||||
|
}{
|
||||||
|
Receiver: ro.Receiver,
|
||||||
|
GroupByAll: ro.GroupByAll,
|
||||||
|
GroupWait: ro.GroupWait,
|
||||||
|
GroupInterval: ro.GroupInterval,
|
||||||
|
RepeatInterval: ro.RepeatInterval,
|
||||||
|
}
|
||||||
|
for ln := range ro.GroupBy {
|
||||||
|
v.GroupBy = append(v.GroupBy, ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(&v)
|
||||||
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ func New(c *config.SlackConfig, t *template.Template, l log.Logger) (*Notifier,
|
||||||
tmpl: t,
|
tmpl: t,
|
||||||
logger: l,
|
logger: l,
|
||||||
client: client,
|
client: client,
|
||||||
retrier: ¬ify.Retrier{RetryCodes: []int{http.StatusTooManyRequests}},
|
retrier: ¬ify.Retrier{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ package slack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
|
@ -36,8 +35,7 @@ func TestSlackRetry(t *testing.T) {
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
retryCodes := append(test.DefaultRetryCodes(), http.StatusTooManyRequests)
|
for statusCode, expected := range test.RetryTests(test.DefaultRetryCodes()) {
|
||||||
for statusCode, expected := range test.RetryTests(retryCodes) {
|
|
||||||
actual, _ := notifier.retrier.Check(statusCode, nil)
|
actual, _ := notifier.retrier.Check(statusCode, nil)
|
||||||
require.Equal(t, expected, actual, fmt.Sprintf("error on status %d", statusCode))
|
require.Equal(t, expected, actual, fmt.Sprintf("error on status %d", statusCode))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue