2021-04-20 13:12:32 -04:00
package definitions
2021-04-19 14:26:04 -04:00
import (
2021-10-07 16:33:50 +02:00
"context"
2021-05-10 15:30:42 +03:00
"encoding/base64"
2021-04-19 14:26:04 -04:00
"encoding/json"
"fmt"
2021-05-12 07:58:16 -04:00
"reflect"
2021-10-04 14:06:40 +01:00
"sort"
2021-08-17 13:49:05 +01:00
"time"
2021-04-19 14:26:04 -04:00
2021-06-15 17:14:02 +01:00
"github.com/go-openapi/strfmt"
2021-05-18 13:34:47 +05:30
amv2 "github.com/prometheus/alertmanager/api/v2/models"
2021-04-19 14:26:04 -04:00
"github.com/prometheus/alertmanager/config"
2021-10-04 14:06:40 +01:00
"github.com/prometheus/alertmanager/pkg/labels"
"github.com/prometheus/common/model"
2021-04-19 14:26:04 -04:00
"gopkg.in/yaml.v3"
2022-02-04 12:42:04 -05:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-04-14 13:06:21 -05:00
"github.com/grafana/grafana/pkg/services/ngalert/models"
2022-02-04 12:42:04 -05:00
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/util"
2021-04-19 14:26:04 -04:00
)
2022-02-04 12:42:04 -05:00
// swagger:route POST /api/alertmanager/grafana/config/api/v1/alerts alertmanager RoutePostGrafanaAlertingConfig
//
// sets an Alerting config
//
// Responses:
// 201: Ack
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route POST /api/alertmanager/{DatasourceUID}/config/api/v1/alerts alertmanager RoutePostAlertingConfig
2021-04-19 14:26:04 -04:00
//
// sets an Alerting config
//
// Responses:
// 201: Ack
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route GET /api/alertmanager/grafana/config/api/v1/alerts alertmanager RouteGetGrafanaAlertingConfig
//
// gets an Alerting config
//
// Responses:
// 200: GettableUserConfig
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route GET /api/alertmanager/{DatasourceUID}/config/api/v1/alerts alertmanager RouteGetAlertingConfig
2021-04-19 14:26:04 -04:00
//
// gets an Alerting config
//
// Responses:
// 200: GettableUserConfig
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route DELETE /api/alertmanager/grafana/config/api/v1/alerts alertmanager RouteDeleteGrafanaAlertingConfig
//
// deletes the Alerting config for a tenant
//
// Responses:
// 200: Ack
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route DELETE /api/alertmanager/{DatasourceUID}/config/api/v1/alerts alertmanager RouteDeleteAlertingConfig
2021-04-19 14:26:04 -04:00
//
// deletes the Alerting config for a tenant
//
// Responses:
// 200: Ack
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route GET /api/alertmanager/grafana/api/v2/status alertmanager RouteGetGrafanaAMStatus
//
// get alertmanager status and configuration
//
// Responses:
// 200: GettableStatus
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route GET /api/alertmanager/{DatasourceUID}/api/v2/status alertmanager RouteGetAMStatus
2021-06-15 17:14:02 +01:00
//
// get alertmanager status and configuration
//
// Responses:
// 200: GettableStatus
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route GET /api/alertmanager/grafana/api/v2/alerts alertmanager RouteGetGrafanaAMAlerts
//
// get alertmanager alerts
//
// Responses:
// 200: gettableAlerts
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route GET /api/alertmanager/{DatasourceUID}/api/v2/alerts alertmanager RouteGetAMAlerts
2021-04-19 14:26:04 -04:00
//
// get alertmanager alerts
//
// Responses:
2021-08-13 16:15:53 +03:00
// 200: gettableAlerts
2021-04-19 14:26:04 -04:00
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route POST /api/alertmanager/grafana/api/v2/alerts alertmanager RoutePostGrafanaAMAlerts
//
// create alertmanager alerts
//
// Responses:
// 200: Ack
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route POST /api/alertmanager/{DatasourceUID}/api/v2/alerts alertmanager RoutePostAMAlerts
2021-04-19 14:26:04 -04:00
//
// create alertmanager alerts
//
// Responses:
// 200: Ack
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route GET /api/alertmanager/grafana/api/v2/alerts/groups alertmanager RouteGetGrafanaAMAlertGroups
//
// get alertmanager alerts
//
// Responses:
// 200: alertGroups
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route GET /api/alertmanager/{DatasourceUID}/api/v2/alerts/groups alertmanager RouteGetAMAlertGroups
2021-04-19 14:26:04 -04:00
//
// get alertmanager alerts
//
// Responses:
2021-08-13 16:15:53 +03:00
// 200: alertGroups
2021-04-19 14:26:04 -04:00
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route POST /api/alertmanager/grafana/config/api/v1/receivers/test alertmanager RoutePostTestGrafanaReceivers
//
// Test Grafana managed receivers without saving them.
//
// Responses:
//
// 200: Ack
// 207: MultiStatus
// 400: ValidationError
// 403: PermissionDenied
// 404: AlertManagerNotFound
// 408: Failure
// 409: AlertManagerNotReady
2022-04-29 10:25:22 +03:00
// swagger:route POST /api/alertmanager/{DatasourceUID}/config/api/v1/receivers/test alertmanager RoutePostTestReceivers
2021-08-17 13:49:05 +01:00
//
// Test Grafana managed receivers without saving them.
//
// Responses:
//
// 200: Ack
// 207: MultiStatus
// 400: ValidationError
2021-09-10 12:46:02 +03:00
// 403: PermissionDenied
// 404: AlertManagerNotFound
2021-08-17 13:49:05 +01:00
// 408: Failure
2021-09-10 12:46:02 +03:00
// 409: AlertManagerNotReady
2021-08-17 13:49:05 +01:00
2022-02-04 12:42:04 -05:00
// swagger:route GET /api/alertmanager/grafana/api/v2/silences alertmanager RouteGetGrafanaSilences
//
// get silences
//
// Responses:
// 200: gettableSilences
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route GET /api/alertmanager/{DatasourceUID}/api/v2/silences alertmanager RouteGetSilences
2021-04-19 14:26:04 -04:00
//
// get silences
//
// Responses:
2021-08-13 16:15:53 +03:00
// 200: gettableSilences
2021-04-19 14:26:04 -04:00
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route POST /api/alertmanager/grafana/api/v2/silences alertmanager RouteCreateGrafanaSilence
//
// create silence
//
// Responses:
// 201: gettableSilence
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route POST /api/alertmanager/{DatasourceUID}/api/v2/silences alertmanager RouteCreateSilence
2021-04-19 14:26:04 -04:00
//
// create silence
//
// Responses:
2021-08-13 16:15:53 +03:00
// 201: gettableSilence
2021-04-19 14:26:04 -04:00
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route GET /api/alertmanager/grafana/api/v2/silence/{SilenceId} alertmanager RouteGetGrafanaSilence
//
// get silence
//
// Responses:
// 200: gettableSilence
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route GET /api/alertmanager/{DatasourceUID}/api/v2/silence/{SilenceId} alertmanager RouteGetSilence
2021-04-19 14:26:04 -04:00
//
// get silence
//
// Responses:
2021-08-13 16:15:53 +03:00
// 200: gettableSilence
2021-04-19 14:26:04 -04:00
// 400: ValidationError
2022-02-04 12:42:04 -05:00
// swagger:route DELETE /api/alertmanager/grafana/api/v2/silence/{SilenceId} alertmanager RouteDeleteGrafanaSilence
//
// delete silence
//
// Responses:
// 200: Ack
// 400: ValidationError
2022-04-29 10:25:22 +03:00
// swagger:route DELETE /api/alertmanager/{DatasourceUID}/api/v2/silence/{SilenceId} alertmanager RouteDeleteSilence
2021-04-19 14:26:04 -04:00
//
// delete silence
//
// Responses:
// 200: Ack
// 400: ValidationError
2021-08-17 13:49:05 +01:00
// swagger:model
2021-09-10 12:46:02 +03:00
type PermissionDenied struct { }
// swagger:model
type AlertManagerNotFound struct { }
// swagger:model
type AlertManagerNotReady struct { }
// swagger:model
type MultiStatus struct { }
2021-08-17 13:49:05 +01:00
2022-02-04 12:42:04 -05:00
// swagger:parameters RoutePostTestReceivers RoutePostTestGrafanaReceivers
2021-08-17 13:49:05 +01:00
type TestReceiversConfigParams struct {
2021-10-21 13:47:06 +01:00
// in:body
2021-11-30 21:55:54 +02:00
Body TestReceiversConfigBodyParams
}
type TestReceiversConfigBodyParams struct {
Alert * TestReceiversConfigAlertParams ` yaml:"alert,omitempty" json:"alert,omitempty" `
Receivers [ ] * PostableApiReceiver ` yaml:"receivers,omitempty" json:"receivers,omitempty" `
2021-08-17 13:49:05 +01:00
}
2021-11-30 21:55:54 +02:00
func ( c * TestReceiversConfigBodyParams ) ProcessConfig ( encrypt EncryptFn ) error {
2021-10-07 16:33:50 +02:00
return processReceiverConfigs ( c . Receivers , encrypt )
2021-08-17 13:49:05 +01:00
}
2021-10-21 13:47:06 +01:00
type TestReceiversConfigAlertParams struct {
Annotations model . LabelSet ` yaml:"annotations,omitempty" json:"annotations,omitempty" `
Labels model . LabelSet ` yaml:"labels,omitempty" json:"labels,omitempty" `
}
2021-08-17 13:49:05 +01:00
// swagger:model
type TestReceiversResult struct {
2021-10-21 13:47:06 +01:00
Alert TestReceiversConfigAlertParams ` json:"alert" `
Receivers [ ] TestReceiverResult ` json:"receivers" `
NotifiedAt time . Time ` json:"notified_at" `
2021-08-17 13:49:05 +01:00
}
// swagger:model
type TestReceiverResult struct {
Name string ` json:"name" `
Configs [ ] TestReceiverConfigResult ` json:"grafana_managed_receiver_configs" `
}
// swagger:model
type TestReceiverConfigResult struct {
Name string ` json:"name" `
UID string ` json:"uid" `
Status string ` json:"status" `
Error string ` json:"error,omitempty" `
}
2022-02-04 12:42:04 -05:00
// swagger:parameters RouteCreateSilence RouteCreateGrafanaSilence
2021-04-19 14:26:04 -04:00
type CreateSilenceParams struct {
// in:body
Silence PostableSilence
}
2022-02-04 12:42:04 -05:00
// swagger:parameters RouteGetSilence RouteDeleteSilence RouteGetGrafanaSilence RouteDeleteGrafanaSilence
2021-04-19 14:26:04 -04:00
type GetDeleteSilenceParams struct {
// in:path
SilenceId string
}
2022-02-04 12:42:04 -05:00
// swagger:parameters RouteGetSilences RouteGetGrafanaSilences
2021-04-19 14:26:04 -04:00
type GetSilencesParams struct {
// in:query
Filter [ ] string ` json:"filter" `
}
2021-06-15 17:14:02 +01:00
// swagger:model
type GettableStatus struct {
// cluster
// Required: true
Cluster * amv2 . ClusterStatus ` json:"cluster" `
// config
// Required: true
Config * PostableApiAlertingConfig ` json:"config" `
// uptime
// Required: true
// Format: date-time
Uptime * strfmt . DateTime ` json:"uptime" `
// version info
// Required: true
VersionInfo * amv2 . VersionInfo ` json:"versionInfo" `
}
func ( s * GettableStatus ) UnmarshalJSON ( b [ ] byte ) error {
amStatus := amv2 . AlertmanagerStatus { }
if err := json . Unmarshal ( b , & amStatus ) ; err != nil {
return err
}
c := config . Config { }
if err := yaml . Unmarshal ( [ ] byte ( * amStatus . Config . Original ) , & c ) ; err != nil {
return err
}
s . Cluster = amStatus . Cluster
s . Config = & PostableApiAlertingConfig { Config : Config {
Global : c . Global ,
2021-10-04 14:06:40 +01:00
Route : AsGrafanaRoute ( c . Route ) ,
2021-06-15 17:14:02 +01:00
InhibitRules : c . InhibitRules ,
Templates : c . Templates ,
} }
s . Uptime = amStatus . Uptime
s . VersionInfo = amStatus . VersionInfo
type overrides struct {
Receivers * [ ] * PostableApiReceiver ` yaml:"receivers,omitempty" json:"receivers,omitempty" `
}
if err := yaml . Unmarshal ( [ ] byte ( * amStatus . Config . Original ) , & overrides { Receivers : & s . Config . Receivers } ) ; err != nil {
return err
}
return nil
}
func NewGettableStatus ( cfg * PostableApiAlertingConfig ) * GettableStatus {
// In Grafana, the only field we support is Config.
cs := amv2 . ClusterStatusStatusDisabled
na := "N/A"
return & GettableStatus {
Cluster : & amv2 . ClusterStatus {
Status : & cs ,
Peers : [ ] * amv2 . PeerStatus { } ,
} ,
VersionInfo : & amv2 . VersionInfo {
Branch : & na ,
BuildDate : & na ,
BuildUser : & na ,
GoVersion : & na ,
Revision : & na ,
Version : & na ,
} ,
Config : cfg ,
}
}
2021-08-13 16:15:53 +03:00
// swagger:model postableSilence
2021-04-19 14:26:04 -04:00
type PostableSilence = amv2 . PostableSilence
2021-08-13 16:15:53 +03:00
// swagger:model gettableSilences
2021-04-19 14:26:04 -04:00
type GettableSilences = amv2 . GettableSilences
2021-08-13 16:15:53 +03:00
// swagger:model gettableSilence
2021-04-19 14:26:04 -04:00
type GettableSilence = amv2 . GettableSilence
2021-08-13 16:15:53 +03:00
// swagger:model gettableAlerts
2021-04-19 14:26:04 -04:00
type GettableAlerts = amv2 . GettableAlerts
2021-08-13 16:15:53 +03:00
// swagger:model gettableAlert
2021-04-19 14:26:04 -04:00
type GettableAlert = amv2 . GettableAlert
2021-08-13 16:15:53 +03:00
// swagger:model alertGroups
2021-04-19 14:26:04 -04:00
type AlertGroups = amv2 . AlertGroups
2021-08-13 16:15:53 +03:00
// swagger:model alertGroup
2021-04-19 14:26:04 -04:00
type AlertGroup = amv2 . AlertGroup
2021-08-13 16:15:53 +03:00
// swagger:model receiver
2021-04-19 14:26:04 -04:00
type Receiver = amv2 . Receiver
2022-02-04 12:42:04 -05:00
// swagger:parameters RouteGetAMAlerts RouteGetAMAlertGroups RouteGetGrafanaAMAlerts RouteGetGrafanaAMAlertGroups
2021-04-19 14:26:04 -04:00
type AlertsParams struct {
// Show active alerts
// in: query
// required: false
// default: true
Active bool ` json:"active" `
// Show silenced alerts
// in: query
// required: false
// default: true
Silenced bool ` json:"silenced" `
// Show inhibited alerts
// in: query
// required: false
// default: true
Inhibited bool ` json:"inhibited" `
// A list of matchers to filter alerts by
// in: query
// required: false
Matchers [ ] string ` json:"filter" `
// A regex matching receivers to filter alerts by
// in: query
// required: false
Receivers string ` json:"receiver" `
}
2022-02-04 12:42:04 -05:00
// swagger:parameters RoutePostAMAlerts RoutePostGrafanaAMAlerts
2021-04-19 14:26:04 -04:00
type PostableAlerts struct {
// in:body
PostableAlerts [ ] amv2 . PostableAlert ` yaml:"" json:"" `
}
2022-02-04 12:42:04 -05:00
// swagger:parameters RoutePostAlertingConfig RoutePostGrafanaAlertingConfig
2021-04-19 14:26:04 -04:00
type BodyAlertingConfig struct {
// in:body
Body PostableUserConfig
}
2022-04-29 10:25:22 +03:00
// alertmanager routes
// swagger:parameters RoutePostAlertingConfig RouteGetAlertingConfig RouteDeleteAlertingConfig RouteGetAMStatus RouteGetAMAlerts RoutePostAMAlerts RouteGetAMAlertGroups RouteGetSilences RouteCreateSilence RouteGetSilence RouteDeleteSilence RoutePostAlertingConfig RoutePostTestReceivers
2022-05-17 14:10:20 +03:00
// testing routes
// swagger:parameters RouteTestRuleConfig
2022-05-06 22:05:02 +03:00
// prom routes
// swagger:parameters RouteGetRuleStatuses RouteGetAlertStatuses
2022-05-05 14:58:32 +03:00
// ruler routes
// swagger:parameters RouteGetRulesConfig RoutePostNameRulesConfig RouteGetNamespaceRulesConfig RouteDeleteNamespaceRulesConfig RouteGetRulegGroupConfig RouteDeleteRuleGroupConfig
2022-04-29 10:25:22 +03:00
type DatasourceUIDReference struct {
// DatasoureUID should be the datasource UID identifier
// in:path
DatasourceUID string
}
2021-04-19 14:26:04 -04:00
// swagger:model
type PostableUserConfig struct {
TemplateFiles map [ string ] string ` yaml:"template_files" json:"template_files" `
AlertmanagerConfig PostableApiAlertingConfig ` yaml:"alertmanager_config" json:"alertmanager_config" `
2021-05-05 16:21:53 -04:00
amSimple map [ string ] interface { } ` yaml:"-" json:"-" `
2021-04-19 14:26:04 -04:00
}
func ( c * PostableUserConfig ) UnmarshalJSON ( b [ ] byte ) error {
type plain PostableUserConfig
if err := json . Unmarshal ( b , ( * plain ) ( c ) ) ; err != nil {
return err
}
2021-05-05 16:21:53 -04:00
// validate first
if err := c . validate ( ) ; err != nil {
return err
}
type intermediate struct {
AlertmanagerConfig map [ string ] interface { } ` yaml:"alertmanager_config" json:"alertmanager_config" `
}
var tmp intermediate
if err := json . Unmarshal ( b , & tmp ) ; err != nil {
return err
}
// store the map[string]interface{} variant for re-encoding later without redaction
c . amSimple = tmp . AlertmanagerConfig
return nil
2021-04-19 14:26:04 -04:00
}
func ( c * PostableUserConfig ) validate ( ) error {
// Taken from https://github.com/prometheus/alertmanager/blob/master/config/config.go#L170-L191
// Check if we have a root route. We cannot check for it in the
// UnmarshalYAML method because it won't be called if the input is empty
// (e.g. the config file is empty or only contains whitespace).
if c . AlertmanagerConfig . Route == nil {
return fmt . Errorf ( "no route provided in config" )
}
// Check if continue in root route.
if c . AlertmanagerConfig . Route . Continue {
return fmt . Errorf ( "cannot have continue in root route" )
}
return nil
2021-05-10 15:30:42 +03:00
}
2021-05-18 17:31:00 +03:00
// GetGrafanaReceiverMap returns a map that associates UUIDs to grafana receivers
func ( c * PostableUserConfig ) GetGrafanaReceiverMap ( ) map [ string ] * PostableGrafanaReceiver {
UIDs := make ( map [ string ] * PostableGrafanaReceiver )
for _ , r := range c . AlertmanagerConfig . Receivers {
switch r . Type ( ) {
case GrafanaReceiverType :
for _ , gr := range r . PostableGrafanaReceivers . GrafanaManagedReceivers {
UIDs [ gr . UID ] = gr
}
default :
}
}
return UIDs
}
// ProcessConfig parses grafana receivers, encrypts secrets and assigns UUIDs (if they are missing)
2021-10-07 16:33:50 +02:00
func ( c * PostableUserConfig ) ProcessConfig ( encrypt EncryptFn ) error {
return processReceiverConfigs ( c . AlertmanagerConfig . Receivers , encrypt )
2021-04-19 14:26:04 -04:00
}
// MarshalYAML implements yaml.Marshaller.
func ( c * PostableUserConfig ) MarshalYAML ( ) ( interface { } , error ) {
2021-05-05 16:21:53 -04:00
yml , err := yaml . Marshal ( c . amSimple )
2021-04-19 14:26:04 -04:00
if err != nil {
return nil , err
}
// cortex/loki actually pass the AM config as a string.
cortexPostableUserConfig := struct {
TemplateFiles map [ string ] string ` yaml:"template_files" json:"template_files" `
AlertmanagerConfig string ` yaml:"alertmanager_config" json:"alertmanager_config" `
} {
TemplateFiles : c . TemplateFiles ,
AlertmanagerConfig : string ( yml ) ,
}
return cortexPostableUserConfig , nil
}
func ( c * PostableUserConfig ) UnmarshalYAML ( value * yaml . Node ) error {
// cortex/loki actually pass the AM config as a string.
type cortexPostableUserConfig struct {
TemplateFiles map [ string ] string ` yaml:"template_files" json:"template_files" `
AlertmanagerConfig string ` yaml:"alertmanager_config" json:"alertmanager_config" `
}
var tmp cortexPostableUserConfig
if err := value . Decode ( & tmp ) ; err != nil {
return err
}
if err := yaml . Unmarshal ( [ ] byte ( tmp . AlertmanagerConfig ) , & c . AlertmanagerConfig ) ; err != nil {
return err
}
c . TemplateFiles = tmp . TemplateFiles
return nil
}
// swagger:model
type GettableUserConfig struct {
TemplateFiles map [ string ] string ` yaml:"template_files" json:"template_files" `
AlertmanagerConfig GettableApiAlertingConfig ` yaml:"alertmanager_config" json:"alertmanager_config" `
2021-05-05 16:21:53 -04:00
// amSimple stores a map[string]interface of the decoded alertmanager config.
// This enables circumventing the underlying alertmanager secret type
// which redacts itself during encoding.
amSimple map [ string ] interface { } ` yaml:"-" json:"-" `
2021-04-19 14:26:04 -04:00
}
func ( c * GettableUserConfig ) UnmarshalYAML ( value * yaml . Node ) error {
// cortex/loki actually pass the AM config as a string.
type cortexGettableUserConfig struct {
TemplateFiles map [ string ] string ` yaml:"template_files" json:"template_files" `
AlertmanagerConfig string ` yaml:"alertmanager_config" json:"alertmanager_config" `
}
var tmp cortexGettableUserConfig
if err := value . Decode ( & tmp ) ; err != nil {
return err
}
if err := yaml . Unmarshal ( [ ] byte ( tmp . AlertmanagerConfig ) , & c . AlertmanagerConfig ) ; err != nil {
return err
}
2021-05-05 16:21:53 -04:00
if err := yaml . Unmarshal ( [ ] byte ( tmp . AlertmanagerConfig ) , & c . amSimple ) ; err != nil {
return err
}
2021-04-19 14:26:04 -04:00
c . TemplateFiles = tmp . TemplateFiles
return nil
}
2021-05-05 16:21:53 -04:00
func ( c * GettableUserConfig ) MarshalJSON ( ) ( [ ] byte , error ) {
type plain struct {
TemplateFiles map [ string ] string ` yaml:"template_files" json:"template_files" `
AlertmanagerConfig map [ string ] interface { } ` yaml:"alertmanager_config" json:"alertmanager_config" `
}
tmp := plain {
TemplateFiles : c . TemplateFiles ,
AlertmanagerConfig : c . amSimple ,
}
return json . Marshal ( tmp )
}
2021-05-18 17:31:00 +03:00
// GetGrafanaReceiverMap returns a map that associates UUIDs to grafana receivers
func ( c * GettableUserConfig ) GetGrafanaReceiverMap ( ) map [ string ] * GettableGrafanaReceiver {
UIDs := make ( map [ string ] * GettableGrafanaReceiver )
for _ , r := range c . AlertmanagerConfig . Receivers {
switch r . Type ( ) {
case GrafanaReceiverType :
for _ , gr := range r . GettableGrafanaReceivers . GrafanaManagedReceivers {
UIDs [ gr . UID ] = gr
}
default :
}
}
return UIDs
}
2021-04-19 14:26:04 -04:00
type GettableApiAlertingConfig struct {
Config ` yaml:",inline" `
// Override with our superset receiver type
Receivers [ ] * GettableApiReceiver ` yaml:"receivers,omitempty" json:"receivers,omitempty" `
}
func ( c * GettableApiAlertingConfig ) UnmarshalJSON ( b [ ] byte ) error {
type plain GettableApiAlertingConfig
if err := json . Unmarshal ( b , ( * plain ) ( c ) ) ; err != nil {
return err
}
2021-05-19 06:22:44 -04:00
// Since Config implements json.Unmarshaler, we must handle _all_ other fields independently.
// Otherwise, the json decoder will detect this and only use the embedded type.
// Additionally, we'll use pointers to slices in order to reference the intended target.
type overrides struct {
Receivers * [ ] * GettableApiReceiver ` yaml:"receivers,omitempty" json:"receivers,omitempty" `
}
if err := json . Unmarshal ( b , & overrides { Receivers : & c . Receivers } ) ; err != nil {
return err
}
2021-04-19 14:26:04 -04:00
return c . validate ( )
}
// validate ensures that the two routing trees use the correct receiver types.
func ( c * GettableApiAlertingConfig ) validate ( ) error {
receivers := make ( map [ string ] struct { } , len ( c . Receivers ) )
var hasGrafReceivers , hasAMReceivers bool
for _ , r := range c . Receivers {
receivers [ r . Name ] = struct { } { }
switch r . Type ( ) {
case GrafanaReceiverType :
hasGrafReceivers = true
case AlertmanagerReceiverType :
hasAMReceivers = true
2021-05-12 07:58:16 -04:00
default :
continue
2021-04-19 14:26:04 -04:00
}
}
if hasGrafReceivers && hasAMReceivers {
return fmt . Errorf ( "cannot mix Alertmanager & Grafana receiver types" )
}
2021-10-04 14:06:40 +01:00
for _ , receiver := range AllReceivers ( c . Route . AsAMRoute ( ) ) {
2021-04-19 14:26:04 -04:00
_ , ok := receivers [ receiver ]
if ! ok {
return fmt . Errorf ( "unexpected receiver (%s) is undefined" , receiver )
}
}
return nil
}
// Config is the top-level configuration for Alertmanager's config files.
type Config struct {
2021-11-19 16:50:55 +01:00
Global * config . GlobalConfig ` yaml:"global,omitempty" json:"global,omitempty" `
Route * Route ` yaml:"route,omitempty" json:"route,omitempty" `
InhibitRules [ ] * config . InhibitRule ` yaml:"inhibit_rules,omitempty" json:"inhibit_rules,omitempty" `
MuteTimeIntervals [ ] config . MuteTimeInterval ` yaml:"mute_time_intervals,omitempty" json:"mute_time_intervals,omitempty" `
Templates [ ] string ` yaml:"templates" json:"templates" `
2021-04-19 14:26:04 -04:00
}
2021-10-04 14:06:40 +01:00
// A Route is a node that contains definitions of how to handle alerts. This is modified
// from the upstream alertmanager in that it adds the ObjectMatchers property.
type Route struct {
Receiver string ` yaml:"receiver,omitempty" json:"receiver,omitempty" `
GroupByStr [ ] string ` yaml:"group_by,omitempty" json:"group_by,omitempty" `
GroupBy [ ] model . LabelName ` yaml:"-" json:"-" `
GroupByAll bool ` yaml:"-" json:"-" `
// Deprecated. Remove before v1.0 release.
Match map [ string ] string ` yaml:"match,omitempty" json:"match,omitempty" `
// Deprecated. Remove before v1.0 release.
MatchRE config . MatchRegexps ` yaml:"match_re,omitempty" json:"match_re,omitempty" `
Matchers config . Matchers ` yaml:"matchers,omitempty" json:"matchers,omitempty" `
ObjectMatchers ObjectMatchers ` yaml:"object_matchers,omitempty" json:"object_matchers,omitempty" `
MuteTimeIntervals [ ] string ` yaml:"mute_time_intervals,omitempty" json:"mute_time_intervals,omitempty" `
Continue bool ` yaml:"continue" json:"continue,omitempty" `
Routes [ ] * Route ` yaml:"routes,omitempty" json:"routes,omitempty" `
GroupWait * model . Duration ` yaml:"group_wait,omitempty" json:"group_wait,omitempty" `
GroupInterval * model . Duration ` yaml:"group_interval,omitempty" json:"group_interval,omitempty" `
RepeatInterval * model . Duration ` yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty" `
2022-04-14 13:06:21 -05:00
Provenance models . Provenance ` yaml:"provenance,omitempty" json:"provenance,omitempty" `
2021-10-04 14:06:40 +01:00
}
// UnmarshalYAML implements the yaml.Unmarshaler interface for Route. This is a copy of alertmanager's upstream except it removes validation on the label key.
func ( r * Route ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
type plain Route
if err := unmarshal ( ( * plain ) ( r ) ) ; err != nil {
return err
}
2022-04-27 15:15:41 -05:00
return r . validateChild ( )
2021-10-04 14:06:40 +01:00
}
// Return an alertmanager route from a Grafana route. The ObjectMatchers are converted to Matchers.
func ( r * Route ) AsAMRoute ( ) * config . Route {
amRoute := & config . Route {
Receiver : r . Receiver ,
GroupByStr : r . GroupByStr ,
GroupBy : r . GroupBy ,
GroupByAll : r . GroupByAll ,
Match : r . Match ,
MatchRE : r . MatchRE ,
Matchers : append ( r . Matchers , r . ObjectMatchers ... ) ,
MuteTimeIntervals : r . MuteTimeIntervals ,
Continue : r . Continue ,
GroupWait : r . GroupWait ,
GroupInterval : r . GroupInterval ,
RepeatInterval : r . RepeatInterval ,
Routes : make ( [ ] * config . Route , 0 , len ( r . Routes ) ) ,
}
for _ , rt := range r . Routes {
amRoute . Routes = append ( amRoute . Routes , rt . AsAMRoute ( ) )
}
return amRoute
}
// Return a Grafana route from an alertmanager route. The Matchers are converted to ObjectMatchers.
func AsGrafanaRoute ( r * config . Route ) * Route {
gRoute := & Route {
Receiver : r . Receiver ,
GroupByStr : r . GroupByStr ,
GroupBy : r . GroupBy ,
GroupByAll : r . GroupByAll ,
Match : r . Match ,
MatchRE : r . MatchRE ,
ObjectMatchers : ObjectMatchers ( r . Matchers ) ,
MuteTimeIntervals : r . MuteTimeIntervals ,
Continue : r . Continue ,
GroupWait : r . GroupWait ,
GroupInterval : r . GroupInterval ,
RepeatInterval : r . RepeatInterval ,
Routes : make ( [ ] * Route , 0 , len ( r . Routes ) ) ,
}
for _ , rt := range r . Routes {
gRoute . Routes = append ( gRoute . Routes , AsGrafanaRoute ( rt ) )
}
return gRoute
}
2022-04-05 16:48:51 -05:00
func ( r * Route ) ResourceType ( ) string {
return "route"
}
func ( r * Route ) ResourceID ( ) string {
return ""
}
2021-05-19 06:22:44 -04:00
// Config is the entrypoint for the embedded Alertmanager config with the exception of receivers.
// Prometheus historically uses yaml files as the method of configuration and thus some
// post-validation is included in the UnmarshalYAML method. Here we simply run this with
// a noop unmarshaling function in order to benefit from said validation.
func ( c * Config ) UnmarshalJSON ( b [ ] byte ) error {
type plain Config
if err := json . Unmarshal ( b , ( * plain ) ( c ) ) ; err != nil {
return err
}
noopUnmarshal := func ( _ interface { } ) error { return nil }
if c . Global != nil {
if err := c . Global . UnmarshalYAML ( noopUnmarshal ) ; err != nil {
return err
}
}
2021-05-19 10:36:28 -04:00
if c . Route == nil {
return fmt . Errorf ( "no routes provided" )
}
2022-04-27 15:15:41 -05:00
err := c . Route . Validate ( )
2021-05-19 10:36:28 -04:00
if err != nil {
2022-04-27 15:15:41 -05:00
return err
2021-11-23 16:25:20 +01:00
}
2021-05-19 06:22:44 -04:00
for _ , r := range c . InhibitRules {
if err := r . UnmarshalYAML ( noopUnmarshal ) ; err != nil {
return err
}
}
2021-11-23 16:25:20 +01:00
tiNames := make ( map [ string ] struct { } )
for _ , mt := range c . MuteTimeIntervals {
if mt . Name == "" {
return fmt . Errorf ( "missing name in mute time interval" )
}
if _ , ok := tiNames [ mt . Name ] ; ok {
return fmt . Errorf ( "mute time interval %q is not unique" , mt . Name )
}
tiNames [ mt . Name ] = struct { } { }
}
return checkTimeInterval ( c . Route , tiNames )
}
func checkTimeInterval ( r * Route , timeIntervals map [ string ] struct { } ) error {
for _ , sr := range r . Routes {
if err := checkTimeInterval ( sr , timeIntervals ) ; err != nil {
return err
}
}
if len ( r . MuteTimeIntervals ) == 0 {
return nil
}
for _ , mt := range r . MuteTimeIntervals {
if _ , ok := timeIntervals [ mt ] ; ! ok {
return fmt . Errorf ( "undefined time interval %q used in route" , mt )
}
}
2021-05-19 06:22:44 -04:00
return nil
}
2021-04-19 14:26:04 -04:00
type PostableApiAlertingConfig struct {
Config ` yaml:",inline" `
// Override with our superset receiver type
Receivers [ ] * PostableApiReceiver ` yaml:"receivers,omitempty" json:"receivers,omitempty" `
}
func ( c * PostableApiAlertingConfig ) UnmarshalJSON ( b [ ] byte ) error {
type plain PostableApiAlertingConfig
if err := json . Unmarshal ( b , ( * plain ) ( c ) ) ; err != nil {
return err
}
2021-05-19 06:22:44 -04:00
// Since Config implements json.Unmarshaler, we must handle _all_ other fields independently.
// Otherwise, the json decoder will detect this and only use the embedded type.
// Additionally, we'll use pointers to slices in order to reference the intended target.
type overrides struct {
Receivers * [ ] * PostableApiReceiver ` yaml:"receivers,omitempty" json:"receivers,omitempty" `
}
if err := json . Unmarshal ( b , & overrides { Receivers : & c . Receivers } ) ; err != nil {
return err
}
2021-04-19 14:26:04 -04:00
return c . validate ( )
}
// validate ensures that the two routing trees use the correct receiver types.
func ( c * PostableApiAlertingConfig ) validate ( ) error {
receivers := make ( map [ string ] struct { } , len ( c . Receivers ) )
var hasGrafReceivers , hasAMReceivers bool
for _ , r := range c . Receivers {
receivers [ r . Name ] = struct { } { }
switch r . Type ( ) {
case GrafanaReceiverType :
hasGrafReceivers = true
case AlertmanagerReceiverType :
hasAMReceivers = true
2021-05-12 07:58:16 -04:00
default :
continue
2021-04-19 14:26:04 -04:00
}
}
if hasGrafReceivers && hasAMReceivers {
return fmt . Errorf ( "cannot mix Alertmanager & Grafana receiver types" )
}
2021-05-14 19:49:54 +01:00
if hasGrafReceivers {
// Taken from https://github.com/prometheus/alertmanager/blob/master/config/config.go#L170-L191
// Check if we have a root route. We cannot check for it in the
// UnmarshalYAML method because it won't be called if the input is empty
// (e.g. the config file is empty or only contains whitespace).
if c . Route == nil {
return fmt . Errorf ( "no route provided in config" )
}
// Check if continue in root route.
if c . Route . Continue {
return fmt . Errorf ( "cannot have continue in root route" )
}
}
2021-10-04 14:06:40 +01:00
for _ , receiver := range AllReceivers ( c . Route . AsAMRoute ( ) ) {
2021-04-19 14:26:04 -04:00
_ , ok := receivers [ receiver ]
if ! ok {
return fmt . Errorf ( "unexpected receiver (%s) is undefined" , receiver )
}
}
return nil
}
// Type requires validate has been called and just checks the first receiver type
2021-05-12 07:58:16 -04:00
func ( c * PostableApiAlertingConfig ) ReceiverType ( ) ReceiverType {
2021-04-19 14:26:04 -04:00
for _ , r := range c . Receivers {
switch r . Type ( ) {
case GrafanaReceiverType :
2021-05-12 07:58:16 -04:00
return GrafanaReceiverType
2021-04-19 14:26:04 -04:00
case AlertmanagerReceiverType :
2021-05-12 07:58:16 -04:00
return AlertmanagerReceiverType
default :
continue
2021-04-19 14:26:04 -04:00
}
}
2021-05-12 07:58:16 -04:00
return EmptyReceiverType
2021-04-19 14:26:04 -04:00
}
// AllReceivers will recursively walk a routing tree and return a list of all the
// referenced receiver names.
func AllReceivers ( route * config . Route ) ( res [ ] string ) {
2021-05-14 19:49:54 +01:00
if route == nil {
return res
}
2021-05-12 07:58:16 -04:00
if route . Receiver != "" {
res = append ( res , route . Receiver )
}
2021-05-14 19:49:54 +01:00
2021-04-19 14:26:04 -04:00
for _ , subRoute := range route . Routes {
res = append ( res , AllReceivers ( subRoute ) ... )
}
return res
}
2021-05-18 13:34:47 +05:30
type GettableGrafanaReceiver struct {
2022-04-27 20:53:36 +02:00
UID string ` json:"uid" `
Name string ` json:"name" `
Type string ` json:"type" `
DisableResolveMessage bool ` json:"disableResolveMessage" `
Settings * simplejson . Json ` json:"settings" `
SecureFields map [ string ] bool ` json:"secureFields" `
Provenance models . Provenance ` json:"provenance,omitempty" `
2021-05-18 13:34:47 +05:30
}
type PostableGrafanaReceiver struct {
UID string ` json:"uid" `
Name string ` json:"name" `
Type string ` json:"type" `
DisableResolveMessage bool ` json:"disableResolveMessage" `
Settings * simplejson . Json ` json:"settings" `
SecureSettings map [ string ] string ` json:"secureSettings" `
}
2021-04-19 14:26:04 -04:00
type ReceiverType int
const (
2021-05-12 07:58:16 -04:00
GrafanaReceiverType ReceiverType = 1 << iota
2021-04-19 14:26:04 -04:00
AlertmanagerReceiverType
2021-05-12 07:58:16 -04:00
EmptyReceiverType = GrafanaReceiverType | AlertmanagerReceiverType
2021-04-19 14:26:04 -04:00
)
2021-05-12 07:58:16 -04:00
func ( r ReceiverType ) String ( ) string {
switch r {
case GrafanaReceiverType :
return "grafana"
case AlertmanagerReceiverType :
return "alertmanager"
case EmptyReceiverType :
return "empty"
default :
return "unknown"
}
}
// Can determines whether a receiver type can implement another receiver type.
// This is useful as receivers with just names but no contact points
// are valid in all backends.
func ( r ReceiverType ) Can ( other ReceiverType ) bool { return r & other != 0 }
// MatchesBackend determines if a config payload can be sent to a particular backend type
func ( r ReceiverType ) MatchesBackend ( backend Backend ) error {
msg := func ( backend Backend , receiver ReceiverType ) error {
return fmt . Errorf (
"unexpected backend type (%s) for receiver type (%s)" ,
backend . String ( ) ,
receiver . String ( ) ,
)
}
var ok bool
switch backend {
case GrafanaBackend :
ok = r . Can ( GrafanaReceiverType )
case AlertmanagerBackend :
ok = r . Can ( AlertmanagerReceiverType )
default :
}
if ! ok {
return msg ( backend , r )
}
return nil
}
2021-04-19 14:26:04 -04:00
type GettableApiReceiver struct {
config . Receiver ` yaml:",inline" `
GettableGrafanaReceivers ` yaml:",inline" `
}
func ( r * GettableApiReceiver ) UnmarshalJSON ( b [ ] byte ) error {
type plain GettableApiReceiver
if err := json . Unmarshal ( b , ( * plain ) ( r ) ) ; err != nil {
return err
}
hasGrafanaReceivers := len ( r . GettableGrafanaReceivers . GrafanaManagedReceivers ) > 0
if hasGrafanaReceivers {
if len ( r . EmailConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager EmailConfigs & Grafana receivers together" )
}
if len ( r . PagerdutyConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager PagerdutyConfigs & Grafana receivers together" )
}
if len ( r . SlackConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager SlackConfigs & Grafana receivers together" )
}
if len ( r . WebhookConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager WebhookConfigs & Grafana receivers together" )
}
if len ( r . OpsGenieConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager OpsGenieConfigs & Grafana receivers together" )
}
if len ( r . WechatConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager WechatConfigs & Grafana receivers together" )
}
if len ( r . PushoverConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager PushoverConfigs & Grafana receivers together" )
}
if len ( r . VictorOpsConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager VictorOpsConfigs & Grafana receivers together" )
}
}
return nil
}
func ( r * GettableApiReceiver ) Type ( ) ReceiverType {
if len ( r . GettableGrafanaReceivers . GrafanaManagedReceivers ) > 0 {
return GrafanaReceiverType
}
return AlertmanagerReceiverType
}
type PostableApiReceiver struct {
config . Receiver ` yaml:",inline" `
PostableGrafanaReceivers ` yaml:",inline" `
}
func ( r * PostableApiReceiver ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
2021-05-12 07:58:16 -04:00
if err := unmarshal ( & r . PostableGrafanaReceivers ) ; err != nil {
2021-04-19 14:26:04 -04:00
return err
}
2021-05-12 07:58:16 -04:00
if err := unmarshal ( & r . Receiver ) ; err != nil {
2021-04-19 14:26:04 -04:00
return err
}
2021-05-12 07:58:16 -04:00
2021-04-19 14:26:04 -04:00
return nil
}
func ( r * PostableApiReceiver ) UnmarshalJSON ( b [ ] byte ) error {
type plain PostableApiReceiver
if err := json . Unmarshal ( b , ( * plain ) ( r ) ) ; err != nil {
return err
}
hasGrafanaReceivers := len ( r . PostableGrafanaReceivers . GrafanaManagedReceivers ) > 0
if hasGrafanaReceivers {
if len ( r . EmailConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager EmailConfigs & Grafana receivers together" )
}
if len ( r . PagerdutyConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager PagerdutyConfigs & Grafana receivers together" )
}
if len ( r . SlackConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager SlackConfigs & Grafana receivers together" )
}
if len ( r . WebhookConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager WebhookConfigs & Grafana receivers together" )
}
if len ( r . OpsGenieConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager OpsGenieConfigs & Grafana receivers together" )
}
if len ( r . WechatConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager WechatConfigs & Grafana receivers together" )
}
if len ( r . PushoverConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager PushoverConfigs & Grafana receivers together" )
}
if len ( r . VictorOpsConfigs ) > 0 {
return fmt . Errorf ( "cannot have both Alertmanager VictorOpsConfigs & Grafana receivers together" )
}
}
return nil
}
func ( r * PostableApiReceiver ) Type ( ) ReceiverType {
if len ( r . PostableGrafanaReceivers . GrafanaManagedReceivers ) > 0 {
return GrafanaReceiverType
}
2021-05-12 07:58:16 -04:00
cpy := r . Receiver
cpy . Name = ""
if reflect . ValueOf ( cpy ) . IsZero ( ) {
return EmptyReceiverType
}
2021-04-19 14:26:04 -04:00
return AlertmanagerReceiverType
}
type GettableGrafanaReceivers struct {
GrafanaManagedReceivers [ ] * GettableGrafanaReceiver ` yaml:"grafana_managed_receiver_configs,omitempty" json:"grafana_managed_receiver_configs,omitempty" `
}
type PostableGrafanaReceivers struct {
GrafanaManagedReceivers [ ] * PostableGrafanaReceiver ` yaml:"grafana_managed_receiver_configs,omitempty" json:"grafana_managed_receiver_configs,omitempty" `
}
2021-08-17 13:49:05 +01:00
2021-11-04 18:47:21 +02:00
type EncryptFn func ( ctx context . Context , payload [ ] byte , scope secrets . EncryptionOptions ) ( [ ] byte , error )
2021-10-07 16:33:50 +02:00
func processReceiverConfigs ( c [ ] * PostableApiReceiver , encrypt EncryptFn ) error {
2021-08-17 13:49:05 +01:00
seenUIDs := make ( map [ string ] struct { } )
// encrypt secure settings for storing them in DB
for _ , r := range c {
switch r . Type ( ) {
case GrafanaReceiverType :
for _ , gr := range r . PostableGrafanaReceivers . GrafanaManagedReceivers {
for k , v := range gr . SecureSettings {
2021-11-04 18:47:21 +02:00
encryptedData , err := encrypt ( context . Background ( ) , [ ] byte ( v ) , secrets . WithoutScope ( ) )
2021-08-17 13:49:05 +01:00
if err != nil {
return fmt . Errorf ( "failed to encrypt secure settings: %w" , err )
}
gr . SecureSettings [ k ] = base64 . StdEncoding . EncodeToString ( encryptedData )
}
if gr . UID == "" {
retries := 5
for i := 0 ; i < retries ; i ++ {
gen := util . GenerateShortUID ( )
_ , ok := seenUIDs [ gen ]
if ! ok {
gr . UID = gen
break
}
}
if gr . UID == "" {
return fmt . Errorf ( "all %d attempts to generate UID for receiver have failed; please retry" , retries )
}
}
seenUIDs [ gr . UID ] = struct { } { }
}
default :
}
}
return nil
}
2021-10-04 14:06:40 +01:00
// ObjectMatchers is Matchers with a different Unmarshal and Marshal methods that accept matchers as objects
// that have already been parsed.
type ObjectMatchers labels . Matchers
// UnmarshalYAML implements the yaml.Unmarshaler interface for Matchers.
func ( m * ObjectMatchers ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
var rawMatchers [ ] [ 3 ] string
if err := unmarshal ( & rawMatchers ) ; err != nil {
return err
}
for _ , rawMatcher := range rawMatchers {
var matchType labels . MatchType
switch rawMatcher [ 1 ] {
case "=" :
matchType = labels . MatchEqual
case "!=" :
matchType = labels . MatchNotEqual
case "=~" :
matchType = labels . MatchRegexp
case "!~" :
matchType = labels . MatchNotRegexp
default :
return fmt . Errorf ( "unsupported match type %q in matcher" , rawMatcher [ 1 ] )
}
matcher , err := labels . NewMatcher ( matchType , rawMatcher [ 0 ] , rawMatcher [ 2 ] )
if err != nil {
return err
}
* m = append ( * m , matcher )
}
sort . Sort ( labels . Matchers ( * m ) )
return nil
}
// UnmarshalJSON implements the json.Unmarshaler interface for Matchers.
func ( m * ObjectMatchers ) UnmarshalJSON ( data [ ] byte ) error {
var rawMatchers [ ] [ 3 ] string
if err := json . Unmarshal ( data , & rawMatchers ) ; err != nil {
return err
}
for _ , rawMatcher := range rawMatchers {
var matchType labels . MatchType
switch rawMatcher [ 1 ] {
case "=" :
matchType = labels . MatchEqual
case "!=" :
matchType = labels . MatchNotEqual
case "=~" :
matchType = labels . MatchRegexp
case "!~" :
matchType = labels . MatchNotRegexp
default :
return fmt . Errorf ( "unsupported match type %q in matcher" , rawMatcher [ 1 ] )
}
matcher , err := labels . NewMatcher ( matchType , rawMatcher [ 0 ] , rawMatcher [ 2 ] )
if err != nil {
return err
}
* m = append ( * m , matcher )
}
sort . Sort ( labels . Matchers ( * m ) )
return nil
}
// MarshalYAML implements the yaml.Marshaler interface for Matchers.
func ( m ObjectMatchers ) MarshalYAML ( ) ( interface { } , error ) {
result := make ( [ ] [ 3 ] string , len ( m ) )
for i , matcher := range m {
result [ i ] = [ 3 ] string { matcher . Name , matcher . Type . String ( ) , matcher . Value }
}
return result , nil
}
// MarshalJSON implements the json.Marshaler interface for Matchers.
func ( m ObjectMatchers ) MarshalJSON ( ) ( [ ] byte , error ) {
if len ( m ) == 0 {
return nil , nil
}
result := make ( [ ] [ 3 ] string , len ( m ) )
for i , matcher := range m {
result [ i ] = [ 3 ] string { matcher . Name , matcher . Type . String ( ) , matcher . Value }
}
return json . Marshal ( result )
}