mirror of https://github.com/grafana/grafana.git
				
				
				
			Alerting: Empty endpoint to load alertmanager config with mimirtool (#106266)
This commit is contained in:
		
							parent
							
								
									0da0fb5af1
								
							
						
					
					
						commit
						a4fa8ab891
					
				|  | @ -513,6 +513,10 @@ func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup( | |||
| 	return grafanaGroup, nil | ||||
| } | ||||
| 
 | ||||
| func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostAlertmanagerConfig(c *contextmodel.ReqContext, config apimodels.AlertmanagerUserConfig) response.Response { | ||||
| 	return response.Error(501, "Not implemented", nil) | ||||
| } | ||||
| 
 | ||||
| // parseBooleanHeader parses a boolean header value, returning an error if the header
 | ||||
| // is present but invalid. If the header is not present, returns (false, nil).
 | ||||
| func parseBooleanHeader(header string, headerName string) (bool, error) { | ||||
|  |  | |||
|  | @ -147,6 +147,9 @@ func (api *API) authorize(method, path string) web.Handler { | |||
| 			ac.EvalPermission(ac.ActionAlertingProvisioningSetStatus), | ||||
| 		) | ||||
| 
 | ||||
| 	case http.MethodPost + "/api/convert/api/v1/alerts": | ||||
| 		eval = ac.EvalPermission(ac.ActionAlertingNotificationsWrite) | ||||
| 
 | ||||
| 	// Alert Instances and Silences
 | ||||
| 
 | ||||
| 	// Silences for Grafana paths.
 | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ func TestAuthorize(t *testing.T) { | |||
| 		} | ||||
| 		paths[p] = methods | ||||
| 	} | ||||
| 	require.Len(t, paths, 63) | ||||
| 	require.Len(t, paths, 64) | ||||
| 
 | ||||
| 	ac := acmock.New() | ||||
| 	api := &API{AccessControl: ac, FeatureManager: featuremgmt.WithFeatures()} | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ type ConvertPrometheusApi interface { | |||
| 	RouteConvertPrometheusGetNamespace(*contextmodel.ReqContext) response.Response | ||||
| 	RouteConvertPrometheusGetRuleGroup(*contextmodel.ReqContext) response.Response | ||||
| 	RouteConvertPrometheusGetRules(*contextmodel.ReqContext) response.Response | ||||
| 	RouteConvertPrometheusPostAlertmanagerConfig(*contextmodel.ReqContext) response.Response | ||||
| 	RouteConvertPrometheusPostRuleGroup(*contextmodel.ReqContext) response.Response | ||||
| 	RouteConvertPrometheusPostRuleGroups(*contextmodel.ReqContext) response.Response | ||||
| } | ||||
|  | @ -93,6 +94,9 @@ func (f *ConvertPrometheusApiHandler) RouteConvertPrometheusGetRuleGroup(ctx *co | |||
| func (f *ConvertPrometheusApiHandler) RouteConvertPrometheusGetRules(ctx *contextmodel.ReqContext) response.Response { | ||||
| 	return f.handleRouteConvertPrometheusGetRules(ctx) | ||||
| } | ||||
| func (f *ConvertPrometheusApiHandler) RouteConvertPrometheusPostAlertmanagerConfig(ctx *contextmodel.ReqContext) response.Response { | ||||
| 	return f.handleRouteConvertPrometheusPostAlertmanagerConfig(ctx) | ||||
| } | ||||
| func (f *ConvertPrometheusApiHandler) RouteConvertPrometheusPostRuleGroup(ctx *contextmodel.ReqContext) response.Response { | ||||
| 	// Parse Path Parameters
 | ||||
| 	namespaceTitleParam := web.Params(ctx.Req)[":NamespaceTitle"] | ||||
|  | @ -248,6 +252,18 @@ func (api *API) RegisterConvertPrometheusApiEndpoints(srv ConvertPrometheusApi, | |||
| 				m, | ||||
| 			), | ||||
| 		) | ||||
| 		group.Post( | ||||
| 			toMacaronPath("/api/convert/api/v1/alerts"), | ||||
| 			requestmeta.SetOwner(requestmeta.TeamAlerting), | ||||
| 			requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), | ||||
| 			api.authorize(http.MethodPost, "/api/convert/api/v1/alerts"), | ||||
| 			metrics.Instrument( | ||||
| 				http.MethodPost, | ||||
| 				"/api/convert/api/v1/alerts", | ||||
| 				api.Hooks.Wrap(srv.RouteConvertPrometheusPostAlertmanagerConfig), | ||||
| 				m, | ||||
| 			), | ||||
| 		) | ||||
| 		group.Post( | ||||
| 			toMacaronPath("/api/convert/prometheus/config/v1/rules/{NamespaceTitle}"), | ||||
| 			requestmeta.SetOwner(requestmeta.TeamAlerting), | ||||
|  |  | |||
|  | @ -15,6 +15,38 @@ import ( | |||
| 
 | ||||
| var errorUnsupportedMediaType = errutil.UnsupportedMediaType("alerting.unsupportedMediaType") | ||||
| 
 | ||||
| // parseJSONOrYAML unmarshals body into target based on content-type, defaulting to YAML
 | ||||
| func parseJSONOrYAML(ctx *contextmodel.ReqContext, target interface{}) error { | ||||
| 	var m string | ||||
| 
 | ||||
| 	body, err := io.ReadAll(ctx.Req.Body) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer func() { _ = ctx.Req.Body.Close() }() | ||||
| 
 | ||||
| 	contentType := ctx.Req.Header.Get("content-type") | ||||
| 
 | ||||
| 	// Parse content-type only if it's not empty,
 | ||||
| 	// otherwise we'll assume it's yaml
 | ||||
| 	if contentType != "" { | ||||
| 		m, _, err = mime.ParseMediaType(contentType) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch m { | ||||
| 	case "application/yaml", "": | ||||
| 		// mimirtool does not send content-type, so if it's empty, we assume it's yaml
 | ||||
| 		return yaml.Unmarshal(body, target) | ||||
| 	case "application/json": | ||||
| 		return json.Unmarshal(body, target) | ||||
| 	default: | ||||
| 		return errorUnsupportedMediaType.Errorf("unsupported media type: %s, only application/yaml and application/json are supported", m) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type ConvertPrometheusApiHandler struct { | ||||
| 	svc *ConvertPrometheusSrv | ||||
| } | ||||
|  | @ -47,75 +79,18 @@ func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusGetRuleGroup(c | |||
| } | ||||
| 
 | ||||
| func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusPostRuleGroup(ctx *contextmodel.ReqContext, namespaceTitle string) response.Response { | ||||
| 	body, err := io.ReadAll(ctx.Req.Body) | ||||
| 	if err != nil { | ||||
| 		return errorToResponse(err) | ||||
| 	} | ||||
| 	defer func() { _ = ctx.Req.Body.Close() }() | ||||
| 
 | ||||
| 	var promGroup apimodels.PrometheusRuleGroup | ||||
| 	var m string | ||||
| 
 | ||||
| 	// Parse content-type only if it's not empty,
 | ||||
| 	// otherwise we'll assume it's yaml
 | ||||
| 	contentType := ctx.Req.Header.Get("content-type") | ||||
| 	if contentType != "" { | ||||
| 		m, _, err = mime.ParseMediaType(contentType) | ||||
| 		if err != nil { | ||||
| 			return errorToResponse(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch m { | ||||
| 	case "application/yaml", "": | ||||
| 		// mimirtool does not send content-type, so if it's empty, we assume it's yaml
 | ||||
| 		if err := yaml.Unmarshal(body, &promGroup); err != nil { | ||||
| 			return errorToResponse(err) | ||||
| 		} | ||||
| 	case "application/json": | ||||
| 		if err := json.Unmarshal(body, &promGroup); err != nil { | ||||
| 			return errorToResponse(err) | ||||
| 		} | ||||
| 	default: | ||||
| 		return errorToResponse(errorUnsupportedMediaType.Errorf("unsupported media type: %s, only application/yaml and application/json are supported", m)) | ||||
| 	if err := parseJSONOrYAML(ctx, &promGroup); err != nil { | ||||
| 		return errorToResponse(err) | ||||
| 	} | ||||
| 
 | ||||
| 	return f.svc.RouteConvertPrometheusPostRuleGroup(ctx, namespaceTitle, promGroup) | ||||
| } | ||||
| 
 | ||||
| func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusPostRuleGroups(ctx *contextmodel.ReqContext) response.Response { | ||||
| 	body, err := io.ReadAll(ctx.Req.Body) | ||||
| 	if err != nil { | ||||
| 		return errorToResponse(err) | ||||
| 	} | ||||
| 	defer func() { _ = ctx.Req.Body.Close() }() | ||||
| 
 | ||||
| 	var m string | ||||
| 
 | ||||
| 	// Parse content-type only if it's not empty,
 | ||||
| 	// otherwise we'll assume it's yaml
 | ||||
| 	contentType := ctx.Req.Header.Get("content-type") | ||||
| 	if contentType != "" { | ||||
| 		m, _, err = mime.ParseMediaType(contentType) | ||||
| 		if err != nil { | ||||
| 			return errorToResponse(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var promNamespaces map[string][]apimodels.PrometheusRuleGroup | ||||
| 
 | ||||
| 	switch m { | ||||
| 	case "application/yaml", "": | ||||
| 		// mimirtool does not send content-type, so if it's empty, we assume it's yaml
 | ||||
| 		if err := yaml.Unmarshal(body, &promNamespaces); err != nil { | ||||
| 			return errorToResponse(err) | ||||
| 		} | ||||
| 	case "application/json": | ||||
| 		if err := json.Unmarshal(body, &promNamespaces); err != nil { | ||||
| 			return errorToResponse(err) | ||||
| 		} | ||||
| 	default: | ||||
| 		return errorToResponse(errorUnsupportedMediaType.Errorf("unsupported media type: %s, only application/yaml and application/json are supported", m)) | ||||
| 	if err := parseJSONOrYAML(ctx, &promNamespaces); err != nil { | ||||
| 		return errorToResponse(err) | ||||
| 	} | ||||
| 
 | ||||
| 	return f.svc.RouteConvertPrometheusPostRuleGroups(ctx, promNamespaces) | ||||
|  | @ -149,3 +124,13 @@ func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusCortexPostRule | |||
| func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusCortexPostRuleGroups(ctx *contextmodel.ReqContext) response.Response { | ||||
| 	return f.handleRouteConvertPrometheusPostRuleGroups(ctx) | ||||
| } | ||||
| 
 | ||||
| // alertmanager
 | ||||
| func (f *ConvertPrometheusApiHandler) handleRouteConvertPrometheusPostAlertmanagerConfig(ctx *contextmodel.ReqContext) response.Response { | ||||
| 	var config apimodels.AlertmanagerUserConfig | ||||
| 	if err := parseJSONOrYAML(ctx, &config); err != nil { | ||||
| 		return errorToResponse(err) | ||||
| 	} | ||||
| 
 | ||||
| 	return f.svc.RouteConvertPrometheusPostAlertmanagerConfig(ctx, config) | ||||
| } | ||||
|  |  | |||
|  | @ -576,6 +576,14 @@ | |||
|    }, | ||||
|    "type": "object" | ||||
|   }, | ||||
|   "AlertmanagerUserConfig": { | ||||
|    "properties": { | ||||
|     "alertmanager_config": { | ||||
|      "$ref": "#/definitions/Config" | ||||
|     } | ||||
|    }, | ||||
|    "type": "object" | ||||
|   }, | ||||
|   "ApiRuleNode": { | ||||
|    "properties": { | ||||
|     "alert": { | ||||
|  | @ -3666,7 +3674,6 @@ | |||
|    "type": "object" | ||||
|   }, | ||||
|   "Route": { | ||||
|    "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", | ||||
|    "properties": { | ||||
|     "active_time_intervals": { | ||||
|      "items": { | ||||
|  | @ -3708,12 +3715,6 @@ | |||
|      }, | ||||
|      "type": "array" | ||||
|     }, | ||||
|     "object_matchers": { | ||||
|      "$ref": "#/definitions/ObjectMatchers" | ||||
|     }, | ||||
|     "provenance": { | ||||
|      "$ref": "#/definitions/Provenance" | ||||
|     }, | ||||
|     "receiver": { | ||||
|      "type": "string" | ||||
|     }, | ||||
|  | @ -3727,6 +3728,7 @@ | |||
|      "type": "array" | ||||
|     } | ||||
|    }, | ||||
|    "title": "A Route is a node that contains definitions of how to handle alerts.", | ||||
|    "type": "object" | ||||
|   }, | ||||
|   "RouteExport": { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package definitions | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/prometheus/alertmanager/config" | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
| 
 | ||||
|  | @ -200,6 +201,21 @@ import ( | |||
| //       202: ConvertPrometheusResponse
 | ||||
| //       403: ForbiddenError
 | ||||
| 
 | ||||
| // Route for `mimirtool alertmanager load`
 | ||||
| // swagger:route POST /convert/api/v1/alerts convert_prometheus RouteConvertPrometheusPostAlertmanagerConfig
 | ||||
| //
 | ||||
| // Load Alertmanager configuration to Grafana and merge it with the existing configuration.
 | ||||
| //
 | ||||
| //     Produces:
 | ||||
| //     - application/json
 | ||||
| //
 | ||||
| //     Responses:
 | ||||
| //       202: ConvertPrometheusResponse
 | ||||
| //       403: ForbiddenError
 | ||||
| //
 | ||||
| //     Extensions:
 | ||||
| //       x-raw-request: true
 | ||||
| 
 | ||||
| // swagger:parameters RouteConvertPrometheusPostRuleGroup RouteConvertPrometheusCortexPostRuleGroup
 | ||||
| type RouteConvertPrometheusPostRuleGroupParams struct { | ||||
| 	// in: path
 | ||||
|  | @ -267,3 +283,14 @@ type ConvertPrometheusResponse struct { | |||
| 	ErrorType string `json:"errorType"` | ||||
| 	Error     string `json:"error"` | ||||
| } | ||||
| 
 | ||||
| // swagger:parameters RouteConvertPrometheusPostAlertmanagerConfig
 | ||||
| type RouteConvertPrometheusPostAlertmanagerConfigParams struct { | ||||
| 	// in:body
 | ||||
| 	Body AlertmanagerUserConfig | ||||
| } | ||||
| 
 | ||||
| // swagger:model
 | ||||
| type AlertmanagerUserConfig struct { | ||||
| 	AlertmanagerConfig config.Config `yaml:"alertmanager_config" json:"alertmanager_config"` | ||||
| } | ||||
|  |  | |||
|  | @ -576,6 +576,14 @@ | |||
|    }, | ||||
|    "type": "object" | ||||
|   }, | ||||
|   "AlertmanagerUserConfig": { | ||||
|    "properties": { | ||||
|     "alertmanager_config": { | ||||
|      "$ref": "#/definitions/Config" | ||||
|     } | ||||
|    }, | ||||
|    "type": "object" | ||||
|   }, | ||||
|   "ApiRuleNode": { | ||||
|    "properties": { | ||||
|     "alert": { | ||||
|  | @ -3666,7 +3674,6 @@ | |||
|    "type": "object" | ||||
|   }, | ||||
|   "Route": { | ||||
|    "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", | ||||
|    "properties": { | ||||
|     "active_time_intervals": { | ||||
|      "items": { | ||||
|  | @ -3708,12 +3715,6 @@ | |||
|      }, | ||||
|      "type": "array" | ||||
|     }, | ||||
|     "object_matchers": { | ||||
|      "$ref": "#/definitions/ObjectMatchers" | ||||
|     }, | ||||
|     "provenance": { | ||||
|      "$ref": "#/definitions/Provenance" | ||||
|     }, | ||||
|     "receiver": { | ||||
|      "type": "string" | ||||
|     }, | ||||
|  | @ -3727,6 +3728,7 @@ | |||
|      "type": "array" | ||||
|     } | ||||
|    }, | ||||
|    "title": "A Route is a node that contains definitions of how to handle alerts.", | ||||
|    "type": "object" | ||||
|   }, | ||||
|   "RouteExport": { | ||||
|  | @ -6820,6 +6822,42 @@ | |||
|     ] | ||||
|    } | ||||
|   }, | ||||
|   "/convert/api/v1/alerts": { | ||||
|    "post": { | ||||
|     "operationId": "RouteConvertPrometheusPostAlertmanagerConfig", | ||||
|     "parameters": [ | ||||
|      { | ||||
|       "in": "body", | ||||
|       "name": "Body", | ||||
|       "schema": { | ||||
|        "$ref": "#/definitions/AlertmanagerUserConfig" | ||||
|       } | ||||
|      } | ||||
|     ], | ||||
|     "produces": [ | ||||
|      "application/json" | ||||
|     ], | ||||
|     "responses": { | ||||
|      "202": { | ||||
|       "description": "ConvertPrometheusResponse", | ||||
|       "schema": { | ||||
|        "$ref": "#/definitions/ConvertPrometheusResponse" | ||||
|       } | ||||
|      }, | ||||
|      "403": { | ||||
|       "description": "ForbiddenError", | ||||
|       "schema": { | ||||
|        "$ref": "#/definitions/ForbiddenError" | ||||
|       } | ||||
|      } | ||||
|     }, | ||||
|     "summary": "Load Alertmanager configuration to Grafana and merge it with the existing configuration.", | ||||
|     "tags": [ | ||||
|      "convert_prometheus" | ||||
|     ], | ||||
|     "x-raw-request": "true" | ||||
|    } | ||||
|   }, | ||||
|   "/convert/prometheus/config/v1/rules": { | ||||
|    "get": { | ||||
|     "operationId": "RouteConvertPrometheusGetRules", | ||||
|  |  | |||
|  | @ -1360,6 +1360,42 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "/convert/api/v1/alerts": { | ||||
|       "post": { | ||||
|         "produces": [ | ||||
|           "application/json" | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "convert_prometheus" | ||||
|         ], | ||||
|         "summary": "Load Alertmanager configuration to Grafana and merge it with the existing configuration.", | ||||
|         "operationId": "RouteConvertPrometheusPostAlertmanagerConfig", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "Body", | ||||
|             "in": "body", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/AlertmanagerUserConfig" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "responses": { | ||||
|           "202": { | ||||
|             "description": "ConvertPrometheusResponse", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/ConvertPrometheusResponse" | ||||
|             } | ||||
|           }, | ||||
|           "403": { | ||||
|             "description": "ForbiddenError", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/ForbiddenError" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "x-raw-request": "true" | ||||
|       } | ||||
|     }, | ||||
|     "/convert/prometheus/config/v1/rules": { | ||||
|       "get": { | ||||
|         "produces": [ | ||||
|  | @ -4747,6 +4783,14 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "AlertmanagerUserConfig": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "alertmanager_config": { | ||||
|           "$ref": "#/definitions/Config" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "ApiRuleNode": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|  | @ -7839,8 +7883,8 @@ | |||
|       } | ||||
|     }, | ||||
|     "Route": { | ||||
|       "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", | ||||
|       "type": "object", | ||||
|       "title": "A Route is a node that contains definitions of how to handle alerts.", | ||||
|       "properties": { | ||||
|         "active_time_intervals": { | ||||
|           "type": "array", | ||||
|  | @ -7882,12 +7926,6 @@ | |||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "object_matchers": { | ||||
|           "$ref": "#/definitions/ObjectMatchers" | ||||
|         }, | ||||
|         "provenance": { | ||||
|           "$ref": "#/definitions/Provenance" | ||||
|         }, | ||||
|         "receiver": { | ||||
|           "type": "string" | ||||
|         }, | ||||
|  |  | |||
|  | @ -12994,6 +12994,14 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "AlertmanagerUserConfig": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "alertmanager_config": { | ||||
|           "$ref": "#/definitions/Config" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "Annotation": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|  | @ -20019,8 +20027,8 @@ | |||
|       } | ||||
|     }, | ||||
|     "Route": { | ||||
|       "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", | ||||
|       "type": "object", | ||||
|       "title": "A Route is a node that contains definitions of how to handle alerts.", | ||||
|       "properties": { | ||||
|         "active_time_intervals": { | ||||
|           "type": "array", | ||||
|  | @ -20062,12 +20070,6 @@ | |||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "object_matchers": { | ||||
|           "$ref": "#/definitions/ObjectMatchers" | ||||
|         }, | ||||
|         "provenance": { | ||||
|           "$ref": "#/definitions/Provenance" | ||||
|         }, | ||||
|         "receiver": { | ||||
|           "type": "string" | ||||
|         }, | ||||
|  |  | |||
|  | @ -3044,6 +3044,14 @@ | |||
|         }, | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "AlertmanagerUserConfig": { | ||||
|         "properties": { | ||||
|           "alertmanager_config": { | ||||
|             "$ref": "#/components/schemas/Config" | ||||
|           } | ||||
|         }, | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "Annotation": { | ||||
|         "properties": { | ||||
|           "alertId": { | ||||
|  | @ -10069,7 +10077,6 @@ | |||
|         "type": "object" | ||||
|       }, | ||||
|       "Route": { | ||||
|         "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", | ||||
|         "properties": { | ||||
|           "active_time_intervals": { | ||||
|             "items": { | ||||
|  | @ -10111,12 +10118,6 @@ | |||
|             }, | ||||
|             "type": "array" | ||||
|           }, | ||||
|           "object_matchers": { | ||||
|             "$ref": "#/components/schemas/ObjectMatchers" | ||||
|           }, | ||||
|           "provenance": { | ||||
|             "$ref": "#/components/schemas/Provenance" | ||||
|           }, | ||||
|           "receiver": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|  | @ -10130,6 +10131,7 @@ | |||
|             "type": "array" | ||||
|           } | ||||
|         }, | ||||
|         "title": "A Route is a node that contains definitions of how to handle alerts.", | ||||
|         "type": "object" | ||||
|       }, | ||||
|       "RouteExport": { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue