add a UID to each correlation

This commit is contained in:
Elfo404 2022-07-07 17:51:29 +02:00
parent 1f2233ff37
commit f865427587
No known key found for this signature in database
GPG Key ID: 586539D9491F0726
7 changed files with 125 additions and 56 deletions

View File

@ -449,11 +449,11 @@ func (hs *HTTPServer) CreateCorrelation(c *models.ReqContext) response.Response
if errors.Is(err, datasources.ErrDatasourceIsReadOnly) {
return response.Error(http.StatusForbidden, "Data source is read only", nil)
}
if errors.Is(err, datasources.ErrCorrelationExists) {
return response.Error(http.StatusConflict, fmt.Sprintf("Correlation to %s already exists", cmd.TargetUID), nil)
if errors.Is(err, datasources.ErrCorrelationUidExists) {
return response.Error(http.StatusConflict, fmt.Sprintf("Correlation with uid %s already exists", cmd.Uid), nil)
}
return response.Error(http.StatusInternalServerError, "Failed to query datasources", err)
return response.Error(http.StatusInternalServerError, "Failed to add correlation", err)
}
// TODO: maybe this?

View File

@ -3,13 +3,14 @@ package datasources
import "errors"
var (
ErrDataSourceNotFound = errors.New("data source not found")
ErrDataSourceNameExists = errors.New("data source with the same name already exists")
ErrDataSourceUidExists = errors.New("data source with the same uid already exists")
ErrDataSourceUpdatingOldVersion = errors.New("trying to update old version of datasource")
ErrDataSourceAccessDenied = errors.New("data source access denied")
ErrDataSourceFailedGenerateUniqueUid = errors.New("failed to generate unique datasource ID")
ErrDataSourceIdentifierNotSet = errors.New("unique identifier and org id are needed to be able to get or delete a datasource")
ErrDatasourceIsReadOnly = errors.New("data source is readonly, can only be updated from configuration")
ErrCorrelationExists = errors.New("correlation to the same datasource already exists")
ErrDataSourceNotFound = errors.New("data source not found")
ErrDataSourceNameExists = errors.New("data source with the same name already exists")
ErrDataSourceUidExists = errors.New("data source with the same uid already exists")
ErrDataSourceUpdatingOldVersion = errors.New("trying to update old version of datasource")
ErrDataSourceAccessDenied = errors.New("data source access denied")
ErrDataSourceFailedGenerateUniqueUid = errors.New("failed to generate unique datasource ID")
ErrDataSourceIdentifierNotSet = errors.New("unique identifier and org id are needed to be able to get or delete a datasource")
ErrDatasourceIsReadOnly = errors.New("data source is readonly, can only be updated from configuration")
ErrCorrelationUidExists = errors.New("correlation to the same uid already exists")
ErrCorrelationFailedGenerateUniqueUid = errors.New("failed to generate unique correlation ID")
)

View File

@ -31,6 +31,7 @@ const (
type DsAccess string
type Correlation struct {
Uid string `json:"uid"`
Target string `json:"target"`
Label string `json:"label,omitempty"`
Description string `json:"description,omitempty"`
@ -40,6 +41,7 @@ type Correlation struct {
// in the data_source table. It's not part of the Correlation model but needed when editing correlations to handle
// concurrent edits.
type CorrelationDTO struct {
Uid string `json:"uid"`
Target string `json:"target"`
Label string `json:"label,omitempty"`
Description string `json:"description,omitempty"`
@ -175,6 +177,7 @@ type CreateCorrelationCommand struct {
Description string `json:"description"`
Version int `json:"version"`
Uid string `json:"-"`
SourceUID string `json:"-"`
OrgID int64 `json:"-"`
Result CorrelationDTO `json:"-"`
@ -183,7 +186,7 @@ type CreateCorrelationCommand struct {
// UpdateCorrelationsCommand updates a correlation
type UpdateCorrelationsCommand struct {
SourceUID string
TargetUID string
Uid string
Correlations []Correlation
OrgId int64

View File

@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/services/secrets/kvstore"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
type Service struct {
@ -271,13 +272,33 @@ func (s *Service) CreateCorrelation(ctx context.Context, cmd *datasources.Create
return err
}
for _, correlation := range ds.Correlations {
if correlation.Target == cmd.TargetUID {
return datasources.ErrCorrelationExists
if cmd.Uid != "" {
for _, correlation := range ds.Correlations {
if correlation.Target == cmd.Uid {
return datasources.ErrCorrelationUidExists
}
}
} else {
uidMap := make(map[string]bool)
for _, correlation := range ds.Correlations {
uidMap[correlation.Uid] = true
}
for i := 0; i < 10; i++ {
uid := util.GenerateShortUID()
if exists := uidMap[uid]; !exists {
cmd.Uid = uid
break
}
}
if cmd.Uid == "" {
return datasources.ErrCorrelationFailedGenerateUniqueUid
}
}
newCorrelation := datasources.Correlation{
Uid: cmd.Uid,
Target: cmd.TargetUID,
Label: cmd.Label,
Description: cmd.Description,

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/provisioning/values"
"github.com/grafana/grafana/pkg/util"
)
// ConfigVersion is used to figure out which API version a config uses.
@ -42,7 +43,7 @@ type upsertDataSourceFromConfig struct {
BasicAuthUser string
WithCredentials bool
IsDefault bool
Correlations []datasources.Correlation
Correlations []interface{}
JSONData map[string]interface{}
SecureJSONData map[string]string
Editable bool
@ -123,18 +124,6 @@ func (cfg *configsV1) mapToDatasourceFromConfig(apiVersion int64) *configs {
}
for _, ds := range cfg.Datasources {
correlations := make([]datasources.Correlation, 0)
for _, v := range ds.Correlations.Value() {
field, ok := v.(map[string]interface{})
if ok {
correlations = append(correlations, datasources.Correlation{
Target: field["targetUid"].(string),
Description: field["description"].(string),
Label: field["label"].(string),
})
}
}
r.Datasources = append(r.Datasources, &upsertDataSourceFromConfig{
OrgID: ds.OrgID.Value(),
@ -148,7 +137,7 @@ func (cfg *configsV1) mapToDatasourceFromConfig(apiVersion int64) *configs {
BasicAuthUser: ds.BasicAuthUser.Value(),
WithCredentials: ds.WithCredentials.Value(),
IsDefault: ds.IsDefault.Value(),
Correlations: correlations,
Correlations: ds.Correlations.Value(),
JSONData: ds.JSONData.Value(),
SecureJSONData: ds.SecureJSONData.Value(),
Editable: ds.Editable.Value(),
@ -177,18 +166,6 @@ func (cfg *configsV0) mapToDatasourceFromConfig(apiVersion int64) *configs {
}
for _, ds := range cfg.Datasources {
correlations := make([]datasources.Correlation, 0)
for _, v := range ds.Correlations {
field, ok := v.(map[string]interface{})
if ok {
correlations = append(correlations, datasources.Correlation{
Target: field["targetUid"].(string),
Description: field["description"].(string),
Label: field["label"].(string),
})
}
}
r.Datasources = append(r.Datasources, &upsertDataSourceFromConfig{
OrgID: ds.OrgID,
@ -202,7 +179,7 @@ func (cfg *configsV0) mapToDatasourceFromConfig(apiVersion int64) *configs {
BasicAuthUser: ds.BasicAuthUser,
WithCredentials: ds.WithCredentials,
IsDefault: ds.IsDefault,
Correlations: correlations,
Correlations: ds.Correlations,
JSONData: ds.JSONData,
SecureJSONData: ds.SecureJSONData,
Editable: ds.Editable,
@ -240,7 +217,7 @@ func createInsertCommand(ds *upsertDataSourceFromConfig) *datasources.AddDataSou
BasicAuthUser: ds.BasicAuthUser,
WithCredentials: ds.WithCredentials,
IsDefault: ds.IsDefault,
Correlations: ds.Correlations,
Correlations: makeCorrelations(ds.Correlations),
JsonData: jsonData,
SecureJsonData: ds.SecureJSONData,
ReadOnly: !ds.Editable,
@ -253,6 +230,35 @@ func createInsertCommand(ds *upsertDataSourceFromConfig) *datasources.AddDataSou
return cmd
}
func makeCorrelations(correlations []interface{}) []datasources.Correlation {
ret := make([]datasources.Correlation, 0)
uidMap := make(map[string]bool)
for _, v := range correlations {
if field, ok := v.(map[string]interface{}); ok {
uid := ""
for i := 0; i < 10; i++ {
newUid := util.GenerateShortUID()
if exists := uidMap[newUid]; !exists {
uid = newUid
uidMap[uid] = true
break
}
}
ret = append(ret, datasources.Correlation{
Uid: uid,
Target: field["targetUid"].(string),
Description: field["description"].(string),
Label: field["label"].(string),
})
}
}
return ret
}
func safeUIDFromName(name string) string {
h := sha256.New()
_, _ = h.Write([]byte(name))
@ -282,7 +288,7 @@ func createUpdateCommand(ds *upsertDataSourceFromConfig, id int64) *datasources.
BasicAuthUser: ds.BasicAuthUser,
WithCredentials: ds.WithCredentials,
IsDefault: ds.IsDefault,
Correlations: ds.Correlations,
Correlations: makeCorrelations(ds.Correlations),
JsonData: jsonData,
SecureJsonData: ds.SecureJSONData,
ReadOnly: !ds.Editable,

View File

@ -8454,6 +8454,33 @@
}
},
"/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": {
"get": {
"tags": ["provisioning"],
"summary": "Get a rule group.",
"operationId": "RouteGetAlertRuleGroup",
"parameters": [
{
"type": "string",
"name": "FolderUID",
"in": "path",
"required": true
},
{
"type": "string",
"name": "Group",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/AlertRuleGroup"
},
"404": {
"description": " Not found."
}
}
},
"put": {
"consumes": ["application/json"],
"tags": ["provisioning"],
@ -8476,15 +8503,15 @@
"name": "Body",
"in": "body",
"schema": {
"$ref": "#/definitions/AlertRuleGroup"
"$ref": "#/definitions/AlertRuleGroupMetadata"
}
}
],
"responses": {
"200": {
"description": "AlertRuleGroup",
"description": "AlertRuleGroupMetadata",
"schema": {
"$ref": "#/definitions/AlertRuleGroup"
"$ref": "#/definitions/AlertRuleGroupMetadata"
}
},
"400": {
@ -9497,7 +9524,7 @@
}
}
},
"AlertRuleGroup": {
"AlertRuleGroupMetadata": {
"type": "object",
"properties": {
"interval": {
@ -9924,6 +9951,9 @@
"target": {
"type": "string"
},
"uid": {
"type": "string"
},
"version": {
"type": "integer",
"format": "int64"
@ -10109,7 +10139,7 @@
"$ref": "#/definitions/ScheduleDTO"
},
"state": {
"type": "string"
"$ref": "#/definitions/State"
},
"templateVars": {
"type": "object"
@ -12461,6 +12491,9 @@
"type": "string",
"example": "now-1h"
},
"publicDashboardAccessToken": {
"type": "string"
},
"queries": {
"description": "queries.refId Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.",
"type": "array",
@ -15094,8 +15127,9 @@
"type": "string"
},
"URL": {
"description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.",
"type": "object",
"title": "URL is a custom URL type that allows validation at configuration load time.",
"title": "A URL represents a parsed URL (technically, a URI reference).",
"properties": {
"ForceQuery": {
"type": "boolean"
@ -15872,7 +15906,6 @@
}
},
"alertGroup": {
"description": "AlertGroup alert group",
"type": "object",
"required": ["alerts", "labels", "receiver"],
"properties": {
@ -15892,7 +15925,6 @@
}
},
"alertGroups": {
"description": "AlertGroups alert groups",
"type": "array",
"items": {
"$ref": "#/definitions/alertGroup"
@ -15983,6 +16015,7 @@
"$ref": "#/definitions/Duration"
},
"gettableAlert": {
"description": "GettableAlert gettable alert",
"type": "object",
"required": ["labels", "annotations", "endsAt", "fingerprint", "receivers", "startsAt", "status", "updatedAt"],
"properties": {
@ -16035,7 +16068,6 @@
}
},
"gettableSilence": {
"description": "GettableSilence gettable silence",
"type": "object",
"required": ["comment", "createdBy", "endsAt", "matchers", "startsAt", "id", "status", "updatedAt"],
"properties": {
@ -16177,7 +16209,6 @@
}
},
"postableSilence": {
"description": "PostableSilence postable silence",
"type": "object",
"required": ["comment", "createdBy", "endsAt", "matchers", "startsAt"],
"properties": {
@ -16209,6 +16240,7 @@
}
},
"receiver": {
"description": "Receiver receiver",
"type": "object",
"required": ["name"],
"properties": {

View File

@ -9054,6 +9054,9 @@
"target": {
"type": "string"
},
"uid": {
"type": "string"
},
"version": {
"type": "integer",
"format": "int64"
@ -9239,7 +9242,7 @@
"$ref": "#/definitions/ScheduleDTO"
},
"state": {
"type": "string"
"$ref": "#/definitions/State"
},
"templateVars": {
"type": "object"
@ -10791,6 +10794,9 @@
"type": "string",
"example": "now-1h"
},
"publicDashboardAccessToken": {
"type": "string"
},
"queries": {
"description": "queries.refId Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.",
"type": "array",