Alerting: replace usage of simplejson to json.RawMessage in NotificationChannelConfig (#60423)

* introduce alias for json.RawMessage with name RawMessage. This is needed to keep raw JSON and implement a marshaler for YAML, which does not seem to be used but there are tests that fail.
* replace usage of simplejson with RawMessage in NotificationChannelConfig
* remove usage of simplejson in tests
* change migration code to convert simplejson to raw message
This commit is contained in:
Yuri Tseretyan
2022-12-16 13:01:06 -05:00
committed by GitHub
parent 09bb4423d2
commit 9ad45aedcf
35 changed files with 257 additions and 161 deletions
@@ -1,15 +1,17 @@
package api
import (
"encoding/json"
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
amConfig "github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/pkg/labels"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/util/cmputil"
amConfig "github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/pkg/labels"
)
func (srv AlertmanagerSrv) provenanceGuard(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
@@ -96,11 +98,16 @@ func checkContactPoints(currReceivers []*apimodels.GettableApiReceiver, newRecei
return editErr
}
}
existingSettings, err := contactPoint.Settings.Map()
existingSettings := map[string]interface{}{}
err := json.Unmarshal(contactPoint.Settings, &existingSettings)
if err != nil {
return err
}
newSettings := map[string]interface{}{}
err = json.Unmarshal(contactPoint.Settings, &newSettings)
if err != nil {
return err
}
newSettings, err := postedContactPoint.Settings.Map()
if err != nil {
return err
}
@@ -3,14 +3,14 @@ package api
import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
amConfig "github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/pkg/labels"
"github.com/prometheus/alertmanager/timeinterval"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
func TestCheckRoute(t *testing.T) {
@@ -319,9 +319,9 @@ func defaultGettableReceiver(t *testing.T, uid string, provenance models.Provena
SecureFields: map[string]bool{
"url": true,
},
Settings: simplejson.NewFromAny(map[string]interface{}{
"hello": "world",
}),
Settings: definitions.RawMessage(`{
"hello": "world"
}`),
},
},
},
@@ -338,9 +338,9 @@ func defaultPostableReceiver(t *testing.T, uid string) *definitions.PostableApiR
Name: "yeah",
Type: "slack",
DisableResolveMessage: true,
Settings: simplejson.NewFromAny(map[string]interface{}{
"hello": "world",
}),
Settings: definitions.RawMessage(`{
"hello": "world"
}`),
},
},
},
@@ -17,7 +17,6 @@ import (
"github.com/prometheus/common/model"
"gopkg.in/yaml.v3"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/util"
@@ -964,12 +963,54 @@ func AllReceivers(route *config.Route) (res []string) {
return res
}
type RawMessage json.RawMessage // This type alias adds YAML marshaling to the json.RawMessage.
// MarshalJSON returns m as the JSON encoding of m.
func (r RawMessage) MarshalJSON() ([]byte, error) {
return json.Marshal(json.RawMessage(r))
}
func (r *RawMessage) UnmarshalJSON(data []byte) error {
var raw json.RawMessage
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
*r = RawMessage(raw)
return nil
}
func (r *RawMessage) UnmarshalYAML(unmarshal func(interface{}) error) error {
var data interface{}
if err := unmarshal(&data); err != nil {
return err
}
bytes, err := json.Marshal(data)
if err != nil {
return err
}
*r = bytes
return nil
}
func (r RawMessage) MarshalYAML() (interface{}, error) {
if r == nil {
return nil, nil
}
var d interface{}
err := json.Unmarshal(r, &d)
if err != nil {
return nil, err
}
return d, nil
}
type GettableGrafanaReceiver struct {
UID string `json:"uid"`
Name string `json:"name"`
Type string `json:"type"`
DisableResolveMessage bool `json:"disableResolveMessage"`
Settings *simplejson.Json `json:"settings"`
Settings RawMessage `json:"settings,omitempty"`
SecureFields map[string]bool `json:"secureFields"`
Provenance models.Provenance `json:"provenance,omitempty"`
}
@@ -979,7 +1020,7 @@ type PostableGrafanaReceiver struct {
Name string `json:"name"`
Type string `json:"type"`
DisableResolveMessage bool `json:"disableResolveMessage"`
Settings *simplejson.Json `json:"settings"`
Settings RawMessage `json:"settings,omitempty"`
SecureSettings map[string]string `json:"secureSettings"`
}
@@ -1040,3 +1040,49 @@ func Test_Marshaling_Validation(t *testing.T) {
expected := []model.LabelName{"alertname"}
require.Equal(t, expected, tmp.AlertmanagerConfig.Config.Route.GroupBy)
}
func Test_RawMessageMarshaling(t *testing.T) {
type Data struct {
Field RawMessage `json:"field" yaml:"field"`
}
t.Run("should unmarshal nil", func(t *testing.T) {
v := Data{
Field: nil,
}
data, err := json.Marshal(v)
require.NoError(t, err)
assert.JSONEq(t, `{ "field": null }`, string(data))
var n Data
require.NoError(t, json.Unmarshal(data, &n))
assert.Equal(t, RawMessage("null"), n.Field)
data, err = yaml.Marshal(&v)
require.NoError(t, err)
assert.Equal(t, "field: null\n", string(data))
require.NoError(t, yaml.Unmarshal(data, &n))
assert.Nil(t, n.Field)
})
t.Run("should unmarshal value", func(t *testing.T) {
v := Data{
Field: RawMessage(`{ "data": "test"}`),
}
data, err := json.Marshal(v)
require.NoError(t, err)
assert.JSONEq(t, `{"field":{"data":"test"}}`, string(data))
var n Data
require.NoError(t, json.Unmarshal(data, &n))
assert.Equal(t, RawMessage(`{"data":"test"}`), n.Field)
data, err = yaml.Marshal(&v)
require.NoError(t, err)
assert.Equal(t, "field:\n data: test\n", string(data))
require.NoError(t, yaml.Unmarshal(data, &n))
assert.Equal(t, RawMessage(`{"data":"test"}`), n.Field)
})
}
@@ -108,8 +108,12 @@ func (e *EmbeddedContactPoint) Valid(decryptFunc channels.GetDecryptedValueFn) e
if !exists {
return fmt.Errorf("unknown type '%s'", e.Type)
}
jsonBytes, err := e.Settings.MarshalJSON()
if err != nil {
return err
}
cfg, _ := channels.NewFactoryConfig(&channels.NotificationChannelConfig{
Settings: e.Settings,
Settings: jsonBytes,
Type: e.Type,
}, nil, decryptFunc, nil, nil, func(ctx ...interface{}) channels.Logger {
return &channels.FakeLogger{}