mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 12:14:08 -06:00
946da57b6a
* Allow creating correlations for provisioned data sources * Update docs * Fix linting * Add missing props * Add missing props * Fix linting * Fix linting * Clarify error name * Removed error handling for a non-existing use case * Create a list of deleted data datasources based on all configs * Add org_id to correlations * Add tests * Allow org_id to be null in case org_id=0 is used * Create organization to ensure stable id is generated * Fix linting * Ensure backwards compatibility * Add deprecation information * Update comments * Override existing datasSource variable so the UID is retrieved correctly * Migrate correlations indices * Default org_id when migrating * Remove redundant default * Make PK non-nullable * Post merge fixes * Separate data sources / correlations provisioning * Adjust comments * Store new data sources in spy store so it can be used to test correlations as well * Fix linting * Update tests * Ensure response is closed * Avoid creating duplicates during provisioning * Fix updating provisioned column and update tests * Rename error message * Fix linting errors * Fix linting errors and rename variable * Update test * Update pkg/services/sqlstore/migrations/correlations_mig.go Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Remove unused error * Fix lining --------- Co-authored-by: Giordano Ricci <me@giordanoricci.com>
302 lines
9.0 KiB
Go
302 lines
9.0 KiB
Go
package correlations
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/grafana/grafana/pkg/services/quota"
|
|
)
|
|
|
|
var (
|
|
ErrCorrelationReadOnly = errors.New("correlation can only be edited via provisioning")
|
|
ErrSourceDataSourceDoesNotExists = errors.New("source data source does not exist")
|
|
ErrTargetDataSourceDoesNotExists = errors.New("target data source does not exist")
|
|
ErrCorrelationNotFound = errors.New("correlation not found")
|
|
ErrUpdateCorrelationEmptyParams = errors.New("not enough parameters to edit correlation")
|
|
ErrInvalidConfigType = errors.New("invalid correlation config type")
|
|
ErrInvalidTransformationType = errors.New("invalid transformation type")
|
|
ErrTransformationNotNested = errors.New("transformations must be nested under config")
|
|
ErrTransformationRegexReqExp = errors.New("regex transformations require expression")
|
|
ErrCorrelationsQuotaFailed = errors.New("error getting correlations quota")
|
|
ErrCorrelationsQuotaReached = errors.New("correlations quota reached")
|
|
)
|
|
|
|
const (
|
|
QuotaTargetSrv quota.TargetSrv = "correlations"
|
|
QuotaTarget quota.Target = "correlations"
|
|
)
|
|
|
|
type CorrelationConfigType string
|
|
|
|
type Transformation struct {
|
|
//Enum: regex,logfmt
|
|
Type string `json:"type"`
|
|
Expression string `json:"expression,omitempty"`
|
|
Field string `json:"field,omitempty"`
|
|
MapValue string `json:"mapValue,omitempty"`
|
|
}
|
|
|
|
const (
|
|
ConfigTypeQuery CorrelationConfigType = "query"
|
|
)
|
|
|
|
func (t CorrelationConfigType) Validate() error {
|
|
if t != ConfigTypeQuery {
|
|
return fmt.Errorf("%s: \"%s\"", ErrInvalidConfigType, t)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t Transformations) Validate() error {
|
|
for _, v := range t {
|
|
if v.Type != "regex" && v.Type != "logfmt" {
|
|
return fmt.Errorf("%s: \"%s\"", ErrInvalidTransformationType, t)
|
|
} else if v.Type == "regex" && len(v.Expression) == 0 {
|
|
return fmt.Errorf("%s: \"%s\"", ErrTransformationRegexReqExp, t)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Transformations []Transformation
|
|
|
|
// swagger:model
|
|
type CorrelationConfig struct {
|
|
// Field used to attach the correlation link
|
|
// required:true
|
|
// example: message
|
|
Field string `json:"field" binding:"Required"`
|
|
// Target type
|
|
// required:true
|
|
Type CorrelationConfigType `json:"type" binding:"Required"`
|
|
// Target data query
|
|
// required:true
|
|
// example: {"prop1":"value1","prop2":"value"}
|
|
Target map[string]any `json:"target" binding:"Required"`
|
|
// Source data transformations
|
|
// required:false
|
|
// example: [{"type":"logfmt"}]
|
|
Transformations Transformations `json:"transformations,omitempty"`
|
|
}
|
|
|
|
func (c CorrelationConfig) MarshalJSON() ([]byte, error) {
|
|
target := c.Target
|
|
transformations := c.Transformations
|
|
if target == nil {
|
|
target = map[string]any{}
|
|
}
|
|
return json.Marshal(struct {
|
|
Type CorrelationConfigType `json:"type"`
|
|
Field string `json:"field"`
|
|
Target map[string]any `json:"target"`
|
|
Transformations Transformations `json:"transformations,omitempty"`
|
|
}{
|
|
Type: ConfigTypeQuery,
|
|
Field: c.Field,
|
|
Target: target,
|
|
Transformations: transformations,
|
|
})
|
|
}
|
|
|
|
// Correlation is the model for correlations definitions
|
|
// swagger:model
|
|
type Correlation struct {
|
|
// Unique identifier of the correlation
|
|
// example: 50xhMlg9k
|
|
UID string `json:"uid" xorm:"pk 'uid'"`
|
|
// UID of the data source the correlation originates from
|
|
// example: d0oxYRg4z
|
|
SourceUID string `json:"sourceUID" xorm:"pk 'source_uid'"`
|
|
// OrgID of the data source the correlation originates from
|
|
// Example: 1
|
|
OrgID int64 `json:"orgId" xorm:"pk 'org_id'"`
|
|
// UID of the data source the correlation points to
|
|
// example: PE1C5CBDA0504A6A3
|
|
TargetUID *string `json:"targetUID" xorm:"target_uid"`
|
|
// Label identifying the correlation
|
|
// example: My Label
|
|
Label string `json:"label" xorm:"label"`
|
|
// Description of the correlation
|
|
// example: Logs to Traces
|
|
Description string `json:"description" xorm:"description"`
|
|
// Correlation Configuration
|
|
Config CorrelationConfig `json:"config" xorm:"jsonb config"`
|
|
// Provisioned True if the correlation was created during provisioning
|
|
Provisioned bool `json:"provisioned"`
|
|
}
|
|
|
|
type GetCorrelationsResponseBody struct {
|
|
Correlations []Correlation `json:"correlations"`
|
|
TotalCount int64 `json:"totalCount"`
|
|
Page int64 `json:"page"`
|
|
Limit int64 `json:"limit"`
|
|
}
|
|
|
|
// CreateCorrelationResponse is the response struct for CreateCorrelationCommand
|
|
// swagger:model
|
|
type CreateCorrelationResponseBody struct {
|
|
Result Correlation `json:"result"`
|
|
// example: Correlation created
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// CreateCorrelationCommand is the command for creating a correlation
|
|
// swagger:model
|
|
type CreateCorrelationCommand struct {
|
|
// UID of the data source for which correlation is created.
|
|
SourceUID string `json:"-"`
|
|
OrgId int64 `json:"-"`
|
|
// Target data source UID to which the correlation is created. required if config.type = query
|
|
// example: PE1C5CBDA0504A6A3
|
|
TargetUID *string `json:"targetUID"`
|
|
// Optional label identifying the correlation
|
|
// example: My label
|
|
Label string `json:"label"`
|
|
// Optional description of the correlation
|
|
// example: Logs to Traces
|
|
Description string `json:"description"`
|
|
// Arbitrary configuration object handled in frontend
|
|
Config CorrelationConfig `json:"config" binding:"Required"`
|
|
// True if correlation was created with provisioning. This makes it read-only.
|
|
Provisioned bool `json:"provisioned"`
|
|
}
|
|
|
|
func (c CreateCorrelationCommand) Validate() error {
|
|
if err := c.Config.Type.Validate(); err != nil {
|
|
return err
|
|
}
|
|
if c.TargetUID == nil && c.Config.Type == ConfigTypeQuery {
|
|
return fmt.Errorf("correlations of type \"%s\" must have a targetUID", ConfigTypeQuery)
|
|
}
|
|
|
|
if err := c.Config.Transformations.Validate(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// swagger:model
|
|
type DeleteCorrelationResponseBody struct {
|
|
// example: Correlation deleted
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// DeleteCorrelationCommand is the command for deleting a correlation
|
|
type DeleteCorrelationCommand struct {
|
|
// UID of the correlation to be deleted.
|
|
UID string
|
|
SourceUID string
|
|
OrgId int64
|
|
}
|
|
|
|
// swagger:model
|
|
type UpdateCorrelationResponseBody struct {
|
|
Result Correlation `json:"result"`
|
|
// example: Correlation updated
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// swagger:model
|
|
type CorrelationConfigUpdateDTO struct {
|
|
// Field used to attach the correlation link
|
|
// example: message
|
|
Field *string `json:"field"`
|
|
// Target type
|
|
Type *CorrelationConfigType `json:"type"`
|
|
// Target data query
|
|
// example: {"prop1":"value1","prop2":"value"}
|
|
Target *map[string]any `json:"target"`
|
|
// Source data transformations
|
|
// example: [{"type": "logfmt"},{"type":"regex","expression":"(Superman|Batman)", "variable":"name"}]
|
|
Transformations []Transformation `json:"transformations"`
|
|
}
|
|
|
|
func (c CorrelationConfigUpdateDTO) Validate() error {
|
|
if c.Type != nil {
|
|
if err := c.Type.Validate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateCorrelationCommand is the command for updating a correlation
|
|
// swagger:model
|
|
type UpdateCorrelationCommand struct {
|
|
// UID of the correlation to be updated.
|
|
UID string `json:"-"`
|
|
SourceUID string `json:"-"`
|
|
OrgId int64 `json:"-"`
|
|
|
|
// Optional label identifying the correlation
|
|
// example: My label
|
|
Label *string `json:"label"`
|
|
// Optional description of the correlation
|
|
// example: Logs to Traces
|
|
Description *string `json:"description"`
|
|
// Correlation Configuration
|
|
Config *CorrelationConfigUpdateDTO `json:"config"`
|
|
}
|
|
|
|
func (c UpdateCorrelationCommand) Validate() error {
|
|
if c.Config != nil {
|
|
if err := c.Config.Validate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if c.Label == nil && c.Description == nil && (c.Config == nil || (c.Config.Field == nil && c.Config.Type == nil && c.Config.Target == nil)) {
|
|
return ErrUpdateCorrelationEmptyParams
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetCorrelationQuery is the query to retrieve a single correlation
|
|
type GetCorrelationQuery struct {
|
|
// UID of the correlation
|
|
UID string `json:"-"`
|
|
// UID of the source data source
|
|
SourceUID string `json:"-"`
|
|
OrgId int64 `json:"-"`
|
|
}
|
|
|
|
// GetCorrelationsBySourceUIDQuery is the query to retrieve all correlations originating by the given Data Source
|
|
type GetCorrelationsBySourceUIDQuery struct {
|
|
SourceUID string `json:"-"`
|
|
OrgId int64 `json:"-"`
|
|
}
|
|
|
|
// GetCorrelationsQuery is the query to retrieve all correlations
|
|
type GetCorrelationsQuery struct {
|
|
OrgId int64 `json:"-"`
|
|
// Limit the maximum number of correlations to return per page
|
|
// in:query
|
|
// required:false
|
|
// default:100
|
|
Limit int64 `json:"limit"`
|
|
// Page index for starting fetching correlations
|
|
// in:query
|
|
// required:false
|
|
// default:1
|
|
Page int64 `json:"page"`
|
|
|
|
// Source datasource UID filter to be applied to correlations
|
|
// in:query
|
|
// required:false
|
|
SourceUIDs []string `json:"sourceuid"`
|
|
}
|
|
|
|
type DeleteCorrelationsBySourceUIDCommand struct {
|
|
SourceUID string
|
|
OrgId int64
|
|
OnlyProvisioned bool
|
|
}
|
|
|
|
type DeleteCorrelationsByTargetUIDCommand struct {
|
|
TargetUID string
|
|
OrgId int64
|
|
}
|