Correlations: Add legacy storage (#112038)

This commit is contained in:
Ryan McKinley
2025-10-16 21:13:39 +03:00
committed by GitHub
parent bb08b2deea
commit bea45a94f0
17 changed files with 738 additions and 77 deletions
+151
View File
@@ -0,0 +1,151 @@
package correlations
import (
"encoding/json"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
authlib "github.com/grafana/authlib/types"
correlationsV0 "github.com/grafana/grafana/apps/correlations/pkg/apis/correlation/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
func ToResource(orig Correlation, namespacer authlib.NamespaceFormatter) (*correlationsV0.Correlation, error) {
cfg, err := ToSpecConfig(orig.Config)
if err != nil {
return nil, err
}
obj := &correlationsV0.Correlation{
ObjectMeta: v1.ObjectMeta{
Name: orig.UID,
Namespace: namespacer(orig.OrgID),
},
Spec: correlationsV0.CorrelationSpec{
Label: orig.Label,
Type: correlationsV0.CorrelationCorrelationType(orig.Type),
Source: correlationsV0.CorrelationDataSourceRef{
Group: ptr.Deref(orig.SourceType, ""),
Name: orig.SourceUID,
},
Config: *cfg,
},
}
if orig.TargetUID != nil {
obj.Spec.Target = &correlationsV0.CorrelationDataSourceRef{
Group: ptr.Deref(orig.TargetType, ""),
Name: *orig.TargetUID,
}
}
if orig.Description != "" {
obj.Spec.Description = &orig.Description
}
if orig.Provisioned {
tmp, _ := utils.MetaAccessor(obj)
tmp.SetManagerProperties(utils.ManagerProperties{
Kind: utils.ManagerKindClassicFP, // nolint:staticcheck
})
}
return obj, nil
}
func ToCorrelation(obj *correlationsV0.Correlation) (*Correlation, error) {
ns, err := authlib.ParseNamespace(obj.Namespace)
if err != nil {
return nil, err
}
cfg, err := ToConfig(obj.Spec.Config)
if err != nil {
return nil, err
}
result := &Correlation{
UID: obj.Name,
OrgID: ns.OrgID,
Label: obj.Spec.Label,
Description: ptr.Deref(obj.Spec.Description, ""),
SourceUID: obj.Spec.Source.Name,
SourceType: ptr.To(obj.Spec.Source.Group),
Type: CorrelationType(obj.Spec.Type),
Config: *cfg,
}
if obj.Annotations[utils.AnnoKeyManagerKind] != "" {
result.Provisioned = true
}
if obj.Spec.Target != nil {
result.TargetUID = &obj.Spec.Target.Name
result.TargetType = ptr.To(obj.Spec.Target.Group)
}
return result, nil
}
func ToSpecConfig(orig CorrelationConfig) (*correlationsV0.CorrelationConfigSpec, error) {
out := &correlationsV0.CorrelationConfigSpec{}
raw, err := json.Marshal(orig)
if err != nil {
return nil, err
}
err = json.Unmarshal(raw, out)
if err != nil {
return nil, err
}
if len(out.Target) == 0 {
out.Target = nil
}
return out, err
}
func ToConfig(orig correlationsV0.CorrelationConfigSpec) (*CorrelationConfig, error) {
out := &CorrelationConfig{}
raw, err := json.Marshal(orig)
if err != nil {
return nil, err
}
err = json.Unmarshal(raw, out)
if err != nil {
return nil, err
}
if len(out.Target) == 0 {
out.Target = nil
}
return out, err
}
func ToUpdateCorrelationCommand(obj *correlationsV0.Correlation) (*UpdateCorrelationCommand, error) {
tmp, err := ToCorrelation(obj)
if err != nil {
return nil, err
}
if tmp.Config.Target == nil {
tmp.Config.Target = map[string]any{} // replace it
}
return &UpdateCorrelationCommand{
UID: tmp.UID,
OrgId: tmp.OrgID,
SourceUID: tmp.SourceUID,
Label: &tmp.Label,
Description: &tmp.Description,
Type: &tmp.Type,
Config: &CorrelationConfigUpdateDTO{
Field: &tmp.Config.Field,
Target: &tmp.Config.Target,
Transformations: tmp.Config.Transformations,
},
}, nil
}
func ToCreateCorrelationCommand(obj *correlationsV0.Correlation) (*CreateCorrelationCommand, error) {
tmp, err := ToCorrelation(obj)
if err != nil {
return nil, err
}
return &CreateCorrelationCommand{
OrgId: tmp.OrgID,
SourceUID: tmp.SourceUID,
TargetUID: tmp.TargetUID,
Label: tmp.Label,
Description: tmp.Description,
Config: tmp.Config,
Type: tmp.Type,
Provisioned: tmp.Provisioned,
}, nil
}
@@ -0,0 +1,112 @@
package correlations
import (
"testing"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
authlib "github.com/grafana/authlib/types"
correlationsV0 "github.com/grafana/grafana/apps/correlations/pkg/apis/correlation/v0alpha1"
)
func TestConversion(t *testing.T) {
namespacer := authlib.OrgNamespaceFormatter
tests := []struct {
name string
input Correlation
expect correlationsV0.Correlation
create CreateCorrelationCommand
update UpdateCorrelationCommand
}{
{
name: "Basic fields",
input: Correlation{
UID: "uid",
OrgID: 2,
Label: "Test Label",
Type: query,
SourceUID: "source",
SourceType: ptr.To("source-type"),
TargetUID: ptr.To("target"),
TargetType: ptr.To("target-type"),
Description: "A test correlation",
Provisioned: true,
Config: CorrelationConfig{
Field: "test-field",
},
},
expect: correlationsV0.Correlation{
ObjectMeta: v1.ObjectMeta{
Name: "uid",
Namespace: "org-2",
Annotations: map[string]string{
"grafana.app/managedBy": "classic-file-provisioning",
},
},
Spec: correlationsV0.CorrelationSpec{
Description: ptr.To("A test correlation"),
Label: "Test Label",
Type: correlationsV0.CorrelationCorrelationTypeQuery,
Source: correlationsV0.CorrelationDataSourceRef{
Group: "source-type",
Name: "source",
},
Target: &correlationsV0.CorrelationDataSourceRef{
Group: "target-type",
Name: "target",
},
Config: correlationsV0.CorrelationConfigSpec{
Field: "test-field",
},
},
},
create: CreateCorrelationCommand{
OrgId: 2,
Label: "Test Label",
Type: query,
SourceUID: "source",
TargetUID: ptr.To("target"),
Description: "A test correlation",
Provisioned: true,
Config: CorrelationConfig{
Field: "test-field",
},
},
update: UpdateCorrelationCommand{
UID: "uid",
OrgId: 2,
Label: ptr.To("Test Label"),
Type: ptr.To(query),
SourceUID: "source",
Description: ptr.To("A test correlation"),
Config: &CorrelationConfigUpdateDTO{
Field: ptr.To("test-field"),
Target: &map[string]any{},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := ToResource(tt.input, namespacer)
require.NoError(t, err)
require.Equal(t, &tt.expect, res, "conversion")
roundtrip, err := ToCorrelation(res)
require.NoError(t, err)
require.Equal(t, &tt.input, roundtrip, "roundtrip")
create, err := ToCreateCorrelationCommand(res)
require.NoError(t, err)
require.Equal(t, &tt.create, create, "create")
update, err := ToUpdateCorrelationCommand(res)
require.NoError(t, err)
require.Equal(t, &tt.update, update, "update")
})
}
}
@@ -50,7 +50,10 @@ func ProvideService(sqlStore db.DB, routeRegister routing.RouteRegister, ds data
}
type Service interface {
GetCorrelation(ctx context.Context, cmd GetCorrelationQuery) (Correlation, error)
GetCorrelations(ctx context.Context, cmd GetCorrelationsQuery) (GetCorrelationsResponseBody, error)
CreateCorrelation(ctx context.Context, cmd CreateCorrelationCommand) (Correlation, error)
UpdateCorrelation(ctx context.Context, cmd UpdateCorrelationCommand) (Correlation, error)
CreateOrUpdateCorrelation(ctx context.Context, cmd CreateCorrelationCommand) error
DeleteCorrelation(ctx context.Context, cmd DeleteCorrelationCommand) error
DeleteCorrelationsBySourceUID(ctx context.Context, cmd DeleteCorrelationsBySourceUIDCommand) error
+32 -15
View File
@@ -54,7 +54,7 @@ func (s CorrelationsService) createCorrelation(ctx context.Context, cmd CreateCo
}
}
_, err = session.Insert(correlation)
_, err = session.Omit("source_type", "target_type").Insert(correlation)
if err != nil {
return err
}
@@ -121,13 +121,13 @@ func (s CorrelationsService) updateCorrelation(ctx context.Context, cmd UpdateCo
return ErrSourceDataSourceDoesNotExists
}
found, err := session.Get(&correlation)
if !found {
return ErrCorrelationNotFound
}
found, err := session.Omit("source_type", "target_type").Get(&correlation)
if err != nil {
return err
}
if !found {
return ErrCorrelationNotFound
}
if correlation.Provisioned {
return ErrCorrelationReadOnly
}
@@ -156,7 +156,11 @@ func (s CorrelationsService) updateCorrelation(ctx context.Context, cmd UpdateCo
}
}
updateCount, err := session.Where("uid = ? AND source_uid = ?", correlation.UID, correlation.SourceUID).Limit(1).Update(correlation)
updateCount, err := session.
Where("uid = ? AND source_uid = ?", correlation.UID, correlation.SourceUID).
Limit(1).
Omit("source_type", "target_type").
Update(correlation)
if err != nil {
return err
@@ -179,20 +183,31 @@ func (s CorrelationsService) updateCorrelation(ctx context.Context, cmd UpdateCo
func (s CorrelationsService) getCorrelation(ctx context.Context, cmd GetCorrelationQuery) (Correlation, error) {
correlation := Correlation{
UID: cmd.UID,
OrgID: cmd.OrgId,
SourceUID: cmd.SourceUID,
}
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *db.Session) error {
query := &datasources.GetDataSourceQuery{
if cmd.SourceUID != "" {
if _, err := s.DataSourceService.GetDataSource(ctx, &datasources.GetDataSourceQuery{
UID: correlation.SourceUID,
OrgID: cmd.OrgId,
UID: cmd.SourceUID,
}
if _, err := s.DataSourceService.GetDataSource(ctx, query); err != nil {
return ErrSourceDataSourceDoesNotExists
}); err != nil {
return Correlation{}, ErrSourceDataSourceDoesNotExists
}
}
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *db.Session) error {
// Correlations created before the fix #72498 may have org_id = 0, but it's deprecated and will be removed in #72325
found, err := session.Select("correlation.*").Join("", "data_source AS dss", "correlation.source_uid = dss.uid and (correlation.org_id = 0 or dss.org_id = correlation.org_id) and dss.org_id = ?", cmd.OrgId).Join("LEFT OUTER", "data_source AS dst", "correlation.target_uid = dst.uid and dst.org_id = ?", cmd.OrgId).Where("correlation.uid = ?", correlation.UID).And("correlation.source_uid = ?", correlation.SourceUID).And(VALID_TYPE_FILTER).Get(&correlation)
sql := session.Select("correlation.*, dss.type as source_type, dst.type as target_type").
Join("", "data_source AS dss", "correlation.source_uid = dss.uid and (correlation.org_id = 0 or dss.org_id = correlation.org_id) and dss.org_id = ?", cmd.OrgId).
Join("LEFT OUTER", "data_source AS dst", "correlation.target_uid = dst.uid and dst.org_id = ?", cmd.OrgId).
Where("correlation.uid = ?", correlation.UID).
And("correlation.org_id = ?", correlation.OrgID).
And(VALID_TYPE_FILTER)
if correlation.SourceUID != "" {
sql = sql.And("correlation.source_uid = ?", correlation.SourceUID)
}
found, err := sql.Get(&correlation)
if !found {
return ErrCorrelationNotFound
}
@@ -264,7 +279,9 @@ func (s CorrelationsService) getCorrelations(ctx context.Context, cmd GetCorrela
offset := cmd.Limit * (cmd.Page - 1)
// Correlations created before the fix #72498 may have org_id = 0, but it's deprecated and will be removed in #72325
q := session.Select("correlation.*").Join("", "data_source AS dss", "correlation.source_uid = dss.uid and (correlation.org_id = 0 or dss.org_id = correlation.org_id) and dss.org_id = ? ", cmd.OrgId).Join("LEFT OUTER", "data_source AS dst", "correlation.target_uid = dst.uid and dst.org_id = ?", cmd.OrgId)
q := session.Select("correlation.*, dss.type as source_type, dst.type as target_type").
Join("", "data_source AS dss", "correlation.source_uid = dss.uid and (correlation.org_id = 0 or dss.org_id = correlation.org_id) and dss.org_id = ? ", cmd.OrgId).
Join("LEFT OUTER", "data_source AS dst", "correlation.target_uid = dst.uid and dst.org_id = ?", cmd.OrgId)
if len(cmd.SourceUIDs) > 0 {
q.In("dss.uid", cmd.SourceUIDs)
@@ -331,7 +348,7 @@ func (s CorrelationsService) createOrUpdateCorrelation(ctx context.Context, cmd
found := false
err := s.SQLStore.WithDbSession(ctx, func(session *db.Session) error {
has, err := session.Get(&correlation)
has, err := session.Omit("source_type", "target_type").Get(&correlation)
found = has
return err
})
+4 -2
View File
@@ -110,13 +110,15 @@ type Correlation struct {
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'"`
SourceUID string `json:"sourceUID" xorm:"pk 'source_uid'"`
SourceType *string `json:"-" xorm:"source_type"`
// 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"`
TargetUID *string `json:"targetUID" xorm:"target_uid"`
TargetType *string `json:"-" xorm:"target_type"`
// Label identifying the correlation
// example: My Label
Label string `json:"label" xorm:"label"`