API: Make conversion functions public (#2530)

* API: Make conversion functions public

All types are public so it makes sense to make these functions also
public for ease of downstream consumption.

Signed-off-by: gotjosh <josue@grafana.com>

* Add the license header to the new file

Signed-off-by: gotjosh <josue@grafana.com>

* Fix tests

Signed-off-by: gotjosh <josue@grafana.com>

* Make two more helper functions public

Signed-off-by: gotjosh <josue@grafana.com>
This commit is contained in:
gotjosh 2021-03-31 08:58:06 +01:00 committed by GitHub
parent 10757eb5fb
commit bc7b16d61a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 213 additions and 182 deletions

View File

@ -270,7 +270,7 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re
continue continue
} }
alert := alertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers) alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
res = append(res, alert) res = append(res, alert)
} }
@ -290,7 +290,7 @@ func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Re
func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware.Responder { func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware.Responder {
logger := api.requestLogger(params.HTTPRequest) logger := api.requestLogger(params.HTTPRequest)
alerts := openAPIAlertsToAlerts(params.Alerts) alerts := OpenAPIAlertsToAlerts(params.Alerts)
now := time.Now() now := time.Now()
api.mtx.RLock() api.mtx.RLock()
@ -389,7 +389,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
for _, alertGroup := range alertGroups { for _, alertGroup := range alertGroups {
ag := &open_api_models.AlertGroup{ ag := &open_api_models.AlertGroup{
Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver}, Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver},
Labels: modelLabelSetToAPILabelSet(alertGroup.Labels), Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels),
Alerts: make([]*open_api_models.GettableAlert, 0, len(alertGroup.Alerts)), Alerts: make([]*open_api_models.GettableAlert, 0, len(alertGroup.Alerts)),
} }
@ -397,7 +397,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams
fp := alert.Fingerprint() fp := alert.Fingerprint()
receivers := allReceivers[fp] receivers := allReceivers[fp]
status := api.getAlertStatus(fp) status := api.getAlertStatus(fp)
apiAlert := alertToOpenAPIAlert(alert, status, receivers) apiAlert := AlertToOpenAPIAlert(alert, status, receivers)
ag.Alerts = append(ag.Alerts, apiAlert) ag.Alerts = append(ag.Alerts, apiAlert)
} }
res = append(res, ag) res = append(res, ag)
@ -434,65 +434,6 @@ func (api *API) alertFilter(matchers []*labels.Matcher, silenced, inhibited, act
} }
} }
func alertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers []string) *open_api_models.GettableAlert {
startsAt := strfmt.DateTime(alert.StartsAt)
updatedAt := strfmt.DateTime(alert.UpdatedAt)
endsAt := strfmt.DateTime(alert.EndsAt)
apiReceivers := make([]*open_api_models.Receiver, 0, len(receivers))
for i := range receivers {
apiReceivers = append(apiReceivers, &open_api_models.Receiver{Name: &receivers[i]})
}
fp := alert.Fingerprint().String()
state := string(status.State)
aa := &open_api_models.GettableAlert{
Alert: open_api_models.Alert{
GeneratorURL: strfmt.URI(alert.GeneratorURL),
Labels: modelLabelSetToAPILabelSet(alert.Labels),
},
Annotations: modelLabelSetToAPILabelSet(alert.Annotations),
StartsAt: &startsAt,
UpdatedAt: &updatedAt,
EndsAt: &endsAt,
Fingerprint: &fp,
Receivers: apiReceivers,
Status: &open_api_models.AlertStatus{
State: &state,
SilencedBy: status.SilencedBy,
InhibitedBy: status.InhibitedBy,
},
}
if aa.Status.SilencedBy == nil {
aa.Status.SilencedBy = []string{}
}
if aa.Status.InhibitedBy == nil {
aa.Status.InhibitedBy = []string{}
}
return aa
}
func openAPIAlertsToAlerts(apiAlerts open_api_models.PostableAlerts) []*types.Alert {
alerts := []*types.Alert{}
for _, apiAlert := range apiAlerts {
alert := types.Alert{
Alert: prometheus_model.Alert{
Labels: apiLabelSetToModelLabelSet(apiAlert.Labels),
Annotations: apiLabelSetToModelLabelSet(apiAlert.Annotations),
StartsAt: time.Time(apiAlert.StartsAt),
EndsAt: time.Time(apiAlert.EndsAt),
GeneratorURL: string(apiAlert.GeneratorURL),
},
}
alerts = append(alerts, &alert)
}
return alerts
}
func removeEmptyLabels(ls prometheus_model.LabelSet) { func removeEmptyLabels(ls prometheus_model.LabelSet) {
for k, v := range ls { for k, v := range ls {
if string(v) == "" { if string(v) == "" {
@ -501,24 +442,6 @@ func removeEmptyLabels(ls prometheus_model.LabelSet) {
} }
} }
func modelLabelSetToAPILabelSet(modelLabelSet prometheus_model.LabelSet) open_api_models.LabelSet {
apiLabelSet := open_api_models.LabelSet{}
for key, value := range modelLabelSet {
apiLabelSet[string(key)] = string(value)
}
return apiLabelSet
}
func apiLabelSetToModelLabelSet(apiLabelSet open_api_models.LabelSet) prometheus_model.LabelSet {
modelLabelSet := prometheus_model.LabelSet{}
for key, value := range apiLabelSet {
modelLabelSet[prometheus_model.LabelName(key)] = prometheus_model.LabelValue(value)
}
return modelLabelSet
}
func receiversMatchFilter(receivers []string, filter *regexp.Regexp) bool { func receiversMatchFilter(receivers []string, filter *regexp.Regexp) bool {
for _, r := range receivers { for _, r := range receivers {
if filter.MatchString(r) { if filter.MatchString(r) {
@ -585,10 +508,10 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew
sils := open_api_models.GettableSilences{} sils := open_api_models.GettableSilences{}
for _, ps := range psils { for _, ps := range psils {
if !checkSilenceMatchesFilterLabels(ps, matchers) { if !CheckSilenceMatchesFilterLabels(ps, matchers) {
continue continue
} }
silence, err := gettableSilenceFromProto(ps) silence, err := GettableSilenceFromProto(ps)
if err != nil { if err != nil {
level.Error(logger).Log("msg", "Failed to unmarshal silence from proto", "err", err) level.Error(logger).Log("msg", "Failed to unmarshal silence from proto", "err", err)
return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error()) return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error())
@ -596,7 +519,7 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew
sils = append(sils, &silence) sils = append(sils, &silence)
} }
sortSilences(sils) SortSilences(sils)
return silence_ops.NewGetSilencesOK().WithPayload(sils) return silence_ops.NewGetSilencesOK().WithPayload(sils)
} }
@ -609,12 +532,12 @@ var (
} }
) )
// sortSilences sorts first according to the state "active, pending, expired" // SortSilences sorts first according to the state "active, pending, expired"
// then by end time or start time depending on the state. // then by end time or start time depending on the state.
// active silences should show the next to expire first // active silences should show the next to expire first
// pending silences are ordered based on which one starts next // pending silences are ordered based on which one starts next
// expired are ordered based on which one expired most recently // expired are ordered based on which one expired most recently
func sortSilences(sils open_api_models.GettableSilences) { func SortSilences(sils open_api_models.GettableSilences) {
sort.Slice(sils, func(i, j int) bool { sort.Slice(sils, func(i, j int) bool {
state1 := types.SilenceState(*sils[i].Status.State) state1 := types.SilenceState(*sils[i].Status.State)
state2 := types.SilenceState(*sils[j].Status.State) state2 := types.SilenceState(*sils[j].Status.State)
@ -639,12 +562,12 @@ func sortSilences(sils open_api_models.GettableSilences) {
}) })
} }
// checkSilenceMatchesFilterLabels returns true if // CheckSilenceMatchesFilterLabels returns true if
// a given silence matches a list of matchers. // a given silence matches a list of matchers.
// A silence matches a filter (list of matchers) if // A silence matches a filter (list of matchers) if
// for all matchers in the filter, there exists a matcher in the silence // for all matchers in the filter, there exists a matcher in the silence
// such that their names, types, and values are equivalent. // such that their names, types, and values are equivalent.
func checkSilenceMatchesFilterLabels(s *silencepb.Silence, matchers []*labels.Matcher) bool { func CheckSilenceMatchesFilterLabels(s *silencepb.Silence, matchers []*labels.Matcher) bool {
for _, matcher := range matchers { for _, matcher := range matchers {
found := false found := false
for _, m := range s.Matchers { for _, m := range s.Matchers {
@ -680,7 +603,7 @@ func (api *API) getSilenceHandler(params silence_ops.GetSilenceParams) middlewar
return silence_ops.NewGetSilenceNotFound() return silence_ops.NewGetSilenceNotFound()
} }
sil, err := gettableSilenceFromProto(sils[0]) sil, err := GettableSilenceFromProto(sils[0])
if err != nil { if err != nil {
level.Error(logger).Log("msg", "Failed to convert unmarshal from proto", "err", err) level.Error(logger).Log("msg", "Failed to convert unmarshal from proto", "err", err)
return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error()) return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error())
@ -700,62 +623,10 @@ func (api *API) deleteSilenceHandler(params silence_ops.DeleteSilenceParams) mid
return silence_ops.NewDeleteSilenceOK() return silence_ops.NewDeleteSilenceOK()
} }
func gettableSilenceFromProto(s *silencepb.Silence) (open_api_models.GettableSilence, error) {
start := strfmt.DateTime(s.StartsAt)
end := strfmt.DateTime(s.EndsAt)
updated := strfmt.DateTime(s.UpdatedAt)
state := string(types.CalcSilenceState(s.StartsAt, s.EndsAt))
sil := open_api_models.GettableSilence{
Silence: open_api_models.Silence{
StartsAt: &start,
EndsAt: &end,
Comment: &s.Comment,
CreatedBy: &s.CreatedBy,
},
ID: &s.Id,
UpdatedAt: &updated,
Status: &open_api_models.SilenceStatus{
State: &state,
},
}
for _, m := range s.Matchers {
matcher := &open_api_models.Matcher{
Name: &m.Name,
Value: &m.Pattern,
}
f := false
t := true
switch m.Type {
case silencepb.Matcher_EQUAL:
matcher.IsEqual = &t
matcher.IsRegex = &f
case silencepb.Matcher_NOT_EQUAL:
matcher.IsEqual = &f
matcher.IsRegex = &f
case silencepb.Matcher_REGEXP:
matcher.IsEqual = &t
matcher.IsRegex = &t
case silencepb.Matcher_NOT_REGEXP:
matcher.IsEqual = &f
matcher.IsRegex = &t
default:
return sil, fmt.Errorf(
"unknown matcher type for matcher '%v' in silence '%v'",
m.Name,
s.Id,
)
}
sil.Matchers = append(sil.Matchers, matcher)
}
return sil, nil
}
func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middleware.Responder { func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middleware.Responder {
logger := api.requestLogger(params.HTTPRequest) logger := api.requestLogger(params.HTTPRequest)
sil, err := postableSilenceToProto(params.Silence) sil, err := PostableSilenceToProto(params.Silence)
if err != nil { if err != nil {
level.Error(logger).Log("msg", "Failed to marshal silence to proto", "err", err) level.Error(logger).Log("msg", "Failed to marshal silence to proto", "err", err)
return silence_ops.NewPostSilencesBadRequest().WithPayload( return silence_ops.NewPostSilencesBadRequest().WithPayload(
@ -789,43 +660,6 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl
}) })
} }
func postableSilenceToProto(s *open_api_models.PostableSilence) (*silencepb.Silence, error) {
sil := &silencepb.Silence{
Id: s.ID,
StartsAt: time.Time(*s.StartsAt),
EndsAt: time.Time(*s.EndsAt),
Comment: *s.Comment,
CreatedBy: *s.CreatedBy,
}
for _, m := range s.Matchers {
matcher := &silencepb.Matcher{
Name: *m.Name,
Pattern: *m.Value,
}
isEqual := true
if m.IsEqual != nil {
isEqual = *m.IsEqual
}
isRegex := false
if m.IsRegex != nil {
isRegex = *m.IsRegex
}
switch {
case isEqual && !isRegex:
matcher.Type = silencepb.Matcher_EQUAL
case !isEqual && !isRegex:
matcher.Type = silencepb.Matcher_NOT_EQUAL
case isEqual && isRegex:
matcher.Type = silencepb.Matcher_REGEXP
case !isEqual && isRegex:
matcher.Type = silencepb.Matcher_NOT_REGEXP
}
sil.Matchers = append(sil.Matchers, matcher)
}
return sil, nil
}
func parseFilter(filter []string) ([]*labels.Matcher, error) { func parseFilter(filter []string) ([]*labels.Matcher, error) {
matchers := make([]*labels.Matcher, 0, len(filter)) matchers := make([]*labels.Matcher, 0, len(filter))
for _, matcherString := range filter { for _, matcherString := range filter {

View File

@ -124,7 +124,7 @@ func TestGetSilencesHandler(t *testing.T) {
gettableSilence("silence-2-active", "active", updateTime, gettableSilence("silence-2-active", "active", updateTime,
"2019-01-01T12:00:00+00:00", "2019-01-01T14:00:00+00:00"), "2019-01-01T12:00:00+00:00", "2019-01-01T14:00:00+00:00"),
} }
sortSilences(open_api_models.GettableSilences(silences)) SortSilences(open_api_models.GettableSilences(silences))
for i, sil := range silences { for i, sil := range silences {
assertEqualStrings(t, "silence-"+strconv.Itoa(i)+"-"+*sil.Status.State, *sil.ID) assertEqualStrings(t, "silence-"+strconv.Itoa(i)+"-"+*sil.Status.State, *sil.ID)
@ -227,7 +227,7 @@ func TestCheckSilenceMatchesFilterLabels(t *testing.T) {
silence := silencepb.Silence{ silence := silencepb.Silence{
Matchers: test.silenceMatchers, Matchers: test.silenceMatchers,
} }
actual := checkSilenceMatchesFilterLabels(&silence, test.filterMatchers) actual := CheckSilenceMatchesFilterLabels(&silence, test.filterMatchers)
if test.expected != actual { if test.expected != actual {
t.Fatal("unexpected match result between silence and filter. expected:", test.expected, ", actual:", actual) t.Fatal("unexpected match result between silence and filter. expected:", test.expected, ", actual:", actual)
} }
@ -255,7 +255,7 @@ func TestAlertToOpenAPIAlert(t *testing.T) {
UpdatedAt: updated, UpdatedAt: updated,
} }
) )
openAPIAlert := alertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers) openAPIAlert := AlertToOpenAPIAlert(alert, types.AlertStatus{State: types.AlertStateActive}, receivers)
require.Equal(t, &open_api_models.GettableAlert{ require.Equal(t, &open_api_models.GettableAlert{
Annotations: open_api_models.LabelSet{}, Annotations: open_api_models.LabelSet{},
Alert: open_api_models.Alert{ Alert: open_api_models.Alert{

197
api/v2/compat.go Normal file
View File

@ -0,0 +1,197 @@
// Copyright 2021 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 v2
import (
"fmt"
"time"
"github.com/go-openapi/strfmt"
open_api_models "github.com/prometheus/alertmanager/api/v2/models"
"github.com/prometheus/alertmanager/silence/silencepb"
"github.com/prometheus/alertmanager/types"
prometheus_model "github.com/prometheus/common/model"
)
// GettableSilenceFromProto converts *silencepb.Silence to open_api_models.GettableSilence.
func GettableSilenceFromProto(s *silencepb.Silence) (open_api_models.GettableSilence, error) {
start := strfmt.DateTime(s.StartsAt)
end := strfmt.DateTime(s.EndsAt)
updated := strfmt.DateTime(s.UpdatedAt)
state := string(types.CalcSilenceState(s.StartsAt, s.EndsAt))
sil := open_api_models.GettableSilence{
Silence: open_api_models.Silence{
StartsAt: &start,
EndsAt: &end,
Comment: &s.Comment,
CreatedBy: &s.CreatedBy,
},
ID: &s.Id,
UpdatedAt: &updated,
Status: &open_api_models.SilenceStatus{
State: &state,
},
}
for _, m := range s.Matchers {
matcher := &open_api_models.Matcher{
Name: &m.Name,
Value: &m.Pattern,
}
f := false
t := true
switch m.Type {
case silencepb.Matcher_EQUAL:
matcher.IsEqual = &t
matcher.IsRegex = &f
case silencepb.Matcher_NOT_EQUAL:
matcher.IsEqual = &f
matcher.IsRegex = &f
case silencepb.Matcher_REGEXP:
matcher.IsEqual = &t
matcher.IsRegex = &t
case silencepb.Matcher_NOT_REGEXP:
matcher.IsEqual = &f
matcher.IsRegex = &t
default:
return sil, fmt.Errorf(
"unknown matcher type for matcher '%v' in silence '%v'",
m.Name,
s.Id,
)
}
sil.Matchers = append(sil.Matchers, matcher)
}
return sil, nil
}
// PostableSilenceToProto converts *open_api_models.PostableSilenc to *silencepb.Silence.
func PostableSilenceToProto(s *open_api_models.PostableSilence) (*silencepb.Silence, error) {
sil := &silencepb.Silence{
Id: s.ID,
StartsAt: time.Time(*s.StartsAt),
EndsAt: time.Time(*s.EndsAt),
Comment: *s.Comment,
CreatedBy: *s.CreatedBy,
}
for _, m := range s.Matchers {
matcher := &silencepb.Matcher{
Name: *m.Name,
Pattern: *m.Value,
}
isEqual := true
if m.IsEqual != nil {
isEqual = *m.IsEqual
}
isRegex := false
if m.IsRegex != nil {
isRegex = *m.IsRegex
}
switch {
case isEqual && !isRegex:
matcher.Type = silencepb.Matcher_EQUAL
case !isEqual && !isRegex:
matcher.Type = silencepb.Matcher_NOT_EQUAL
case isEqual && isRegex:
matcher.Type = silencepb.Matcher_REGEXP
case !isEqual && isRegex:
matcher.Type = silencepb.Matcher_NOT_REGEXP
}
sil.Matchers = append(sil.Matchers, matcher)
}
return sil, nil
}
// AlertToOpenAPIAlert converts internal alerts, alert types, and receivers to *open_api_models.GettableAlert.
func AlertToOpenAPIAlert(alert *types.Alert, status types.AlertStatus, receivers []string) *open_api_models.GettableAlert {
startsAt := strfmt.DateTime(alert.StartsAt)
updatedAt := strfmt.DateTime(alert.UpdatedAt)
endsAt := strfmt.DateTime(alert.EndsAt)
apiReceivers := make([]*open_api_models.Receiver, 0, len(receivers))
for i := range receivers {
apiReceivers = append(apiReceivers, &open_api_models.Receiver{Name: &receivers[i]})
}
fp := alert.Fingerprint().String()
state := string(status.State)
aa := &open_api_models.GettableAlert{
Alert: open_api_models.Alert{
GeneratorURL: strfmt.URI(alert.GeneratorURL),
Labels: ModelLabelSetToAPILabelSet(alert.Labels),
},
Annotations: ModelLabelSetToAPILabelSet(alert.Annotations),
StartsAt: &startsAt,
UpdatedAt: &updatedAt,
EndsAt: &endsAt,
Fingerprint: &fp,
Receivers: apiReceivers,
Status: &open_api_models.AlertStatus{
State: &state,
SilencedBy: status.SilencedBy,
InhibitedBy: status.InhibitedBy,
},
}
if aa.Status.SilencedBy == nil {
aa.Status.SilencedBy = []string{}
}
if aa.Status.InhibitedBy == nil {
aa.Status.InhibitedBy = []string{}
}
return aa
}
// OpenAPIAlertsToAlerts converts open_api_models.PostableAlerts to []*types.Alert.
func OpenAPIAlertsToAlerts(apiAlerts open_api_models.PostableAlerts) []*types.Alert {
alerts := []*types.Alert{}
for _, apiAlert := range apiAlerts {
alert := types.Alert{
Alert: prometheus_model.Alert{
Labels: APILabelSetToModelLabelSet(apiAlert.Labels),
Annotations: APILabelSetToModelLabelSet(apiAlert.Annotations),
StartsAt: time.Time(apiAlert.StartsAt),
EndsAt: time.Time(apiAlert.EndsAt),
GeneratorURL: string(apiAlert.GeneratorURL),
},
}
alerts = append(alerts, &alert)
}
return alerts
}
// ModelLabelSetToAPILabelSet converts prometheus_model.LabelSet to open_api_models.LabelSet.
func ModelLabelSetToAPILabelSet(modelLabelSet prometheus_model.LabelSet) open_api_models.LabelSet {
apiLabelSet := open_api_models.LabelSet{}
for key, value := range modelLabelSet {
apiLabelSet[string(key)] = string(value)
}
return apiLabelSet
}
// APILabelSetToModelLabelSet converts open_api_models.LabelSet to prometheus_model.LabelSet.
func APILabelSetToModelLabelSet(apiLabelSet open_api_models.LabelSet) prometheus_model.LabelSet {
modelLabelSet := prometheus_model.LabelSet{}
for key, value := range apiLabelSet {
modelLabelSet[prometheus_model.LabelName(key)] = prometheus_model.LabelValue(value)
}
return modelLabelSet
}