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
@@ -0,0 +1,195 @@
package correlations
import (
"context"
"fmt"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apiserver/pkg/registry/rest"
correlationsV0 "github.com/grafana/grafana/apps/correlations/pkg/apis/correlation/v0alpha1"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/correlations"
)
var (
_ rest.Scoper = (*legacyStorage)(nil)
_ rest.SingularNameProvider = (*legacyStorage)(nil)
_ rest.Getter = (*legacyStorage)(nil)
_ rest.Storage = (*legacyStorage)(nil)
_ rest.Creater = (*legacyStorage)(nil)
_ rest.Updater = (*legacyStorage)(nil)
_ rest.GracefulDeleter = (*legacyStorage)(nil)
)
type legacyStorage struct {
service correlations.Service
namespacer request.NamespaceMapper
tableConverter rest.TableConvertor
}
func (s *legacyStorage) New() runtime.Object {
return correlationsV0.CorrelationKind().ZeroValue()
}
func (s *legacyStorage) Destroy() {}
func (s *legacyStorage) NamespaceScoped() bool {
return true // namespace == org
}
func (s *legacyStorage) GetSingularName() string {
return strings.ToLower(correlationsV0.CorrelationKind().Kind())
}
func (s *legacyStorage) NewList() runtime.Object {
return correlationsV0.CorrelationKind().ZeroListValue()
}
func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
orgID, err := request.OrgIDForList(ctx)
if err != nil {
return nil, err
}
uids := []string{}
if options.FieldSelector != nil {
for _, r := range options.FieldSelector.Requirements() {
switch r.Field {
case "spec.datasource.name":
switch r.Operator {
case selection.Equals, selection.DoubleEquals:
uids = []string{r.Value}
case selection.In:
uids = strings.Split(r.Value, ";") // ??? not sure how/if this supports multiple values
default:
return nil, fmt.Errorf("unsupported operation")
}
default:
return nil, fmt.Errorf("unsupported field")
}
}
}
if options.Continue != "" {
return nil, fmt.Errorf("paging not yet supported")
}
rsp, err := s.service.GetCorrelations(ctx, correlations.GetCorrelationsQuery{
OrgId: orgID,
Limit: 1000,
SourceUIDs: uids,
})
if err != nil {
return nil, err
}
list := &correlationsV0.CorrelationList{
Items: make([]correlationsV0.Correlation, len(rsp.Correlations)),
}
for i, orig := range rsp.Correlations {
c, err := correlations.ToResource(orig, s.namespacer)
if err != nil {
return nil, err
}
list.Items[i] = *c
}
return list, nil
}
func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
orgID, err := request.OrgIDForList(ctx)
if err != nil {
return nil, err
}
c, err := s.service.GetCorrelation(ctx, correlations.GetCorrelationQuery{
UID: name,
OrgId: orgID,
})
if err != nil {
return nil, err
}
return correlations.ToResource(c, s.namespacer)
}
func (s *legacyStorage) Create(ctx context.Context,
obj runtime.Object,
createValidation rest.ValidateObjectFunc,
options *metav1.CreateOptions,
) (runtime.Object, error) {
resource, ok := obj.(*correlationsV0.Correlation)
if !ok {
return nil, fmt.Errorf("expected correlation")
}
cmd, err := correlations.ToCreateCorrelationCommand(resource)
if err != nil {
return nil, err
}
out, err := s.service.CreateCorrelation(ctx, *cmd)
if err != nil {
return nil, err
}
return s.Get(ctx, out.UID, &metav1.GetOptions{})
}
func (s *legacyStorage) Update(ctx context.Context,
name string,
objInfo rest.UpdatedObjectInfo,
createValidation rest.ValidateObjectFunc,
updateValidation rest.ValidateObjectUpdateFunc,
forceAllowCreate bool,
options *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
before, err := s.Get(ctx, name, &metav1.GetOptions{})
if err != nil {
return nil, false, err
}
obj, err := objInfo.UpdatedObject(ctx, before)
if err != nil {
return nil, false, err
}
resource, ok := obj.(*correlationsV0.Correlation)
if !ok {
return nil, false, fmt.Errorf("expected correlation")
}
cmd, err := correlations.ToUpdateCorrelationCommand(resource)
if err != nil {
return nil, false, err
}
out, err := s.service.UpdateCorrelation(ctx, *cmd)
if err != nil {
return nil, false, err
}
obj, err = s.Get(ctx, out.UID, &metav1.GetOptions{})
return obj, false, err
}
// GracefulDeleter
func (s *legacyStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
orgID, err := request.OrgIDForList(ctx)
if err != nil {
return nil, false, err
}
err = s.service.DeleteCorrelation(ctx, correlations.DeleteCorrelationCommand{
OrgId: orgID,
UID: name,
})
return nil, (err == nil), err
}
// CollectionDeleter
func (s *legacyStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) {
return nil, fmt.Errorf("DeleteCollection for shorturl not implemented")
}
+61 -7
View File
@@ -1,33 +1,44 @@
package correlations
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"github.com/grafana/grafana-app-sdk/app"
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
"github.com/grafana/grafana-app-sdk/simple"
"github.com/grafana/grafana/apps/correlations/pkg/apis"
correlationsV0 "github.com/grafana/grafana/apps/correlations/pkg/apis/correlation/v0alpha1"
correlationsapp "github.com/grafana/grafana/apps/correlations/pkg/app"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/apiserver/appinstaller"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/correlations"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
)
var (
_ appsdkapiserver.AppInstaller = (*CorrelationsAppInstaller)(nil)
_ appsdkapiserver.AppInstaller = (*AppInstaller)(nil)
_ appinstaller.LegacyStorageProvider = (*AppInstaller)(nil)
)
type CorrelationsAppInstaller struct {
type AppInstaller struct {
appsdkapiserver.AppInstaller
cfg *setting.Cfg
legacy *legacyStorage
}
func RegisterAppInstaller(
cfg *setting.Cfg,
features featuremgmt.FeatureToggles,
) (*CorrelationsAppInstaller, error) {
installer := &CorrelationsAppInstaller{
cfg: cfg,
}
service correlations.Service,
) (*AppInstaller, error) {
installer := &AppInstaller{}
provider := simple.NewAppProvider(apis.LocalManifest(), nil, correlationsapp.New)
appConfig := app.Config{
@@ -40,5 +51,48 @@ func RegisterAppInstaller(
}
installer.AppInstaller = i
if service != nil {
installer.legacy = &legacyStorage{
service: service,
namespacer: request.GetNamespaceMapper(cfg),
}
}
return installer, nil
}
func (a *AppInstaller) GetLegacyStorage(requested schema.GroupVersionResource) rest.Storage {
kind := correlationsV0.CorrelationKind()
gvr := schema.GroupVersionResource{
Group: kind.Group(),
Version: kind.Version(),
Resource: kind.Plural(),
}
if requested.String() != gvr.String() {
return nil
}
a.legacy.tableConverter = utils.NewTableConverter(
gvr.GroupResource(),
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Source", Type: "string", Format: "string"},
{Name: "Target", Type: "string", Format: "string"},
{Name: "Description", Type: "string", Format: "string"},
},
Reader: func(obj any) ([]any, error) {
m, ok := obj.(*correlationsV0.Correlation)
if !ok {
return nil, fmt.Errorf("expected Correlation")
}
return []any{
m.Name,
m.Spec.Source.Name,
m.Spec.Target.Name,
m.Spec.Description,
}, nil
},
},
)
return a.legacy
}