APIServer: Make TableConverter part of ResourceInfo (#91520)

This commit is contained in:
Ryan McKinley
2024-08-05 15:38:12 +03:00
committed by GitHub
parent f885cc7d9c
commit a223c46506
28 changed files with 479 additions and 506 deletions

View File

@@ -1,6 +1,7 @@
package v0alpha1
import (
"github.com/grafana/grafana/pkg/apimachinery/utils"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -18,12 +19,13 @@ type ResourceInfo struct {
kind string
newObj func() runtime.Object
newList func() runtime.Object
columns utils.TableColumns
}
func NewResourceInfo(group, version, resourceName, singularName, kind string,
newObj func() runtime.Object, newList func() runtime.Object) ResourceInfo {
newObj func() runtime.Object, newList func() runtime.Object, columns utils.TableColumns) ResourceInfo {
shortName := "" // an optional alias helpful in kubectl eg ("sa" for serviceaccounts)
return ResourceInfo{group, version, resourceName, singularName, shortName, kind, newObj, newList}
return ResourceInfo{group, version, resourceName, singularName, shortName, kind, newObj, newList, columns}
}
func (info *ResourceInfo) WithGroupAndShortName(group string, shortName string) ResourceInfo {
@@ -36,6 +38,7 @@ func (info *ResourceInfo) WithGroupAndShortName(group string, shortName string)
shortName: shortName,
newObj: info.newObj,
newList: info.newList,
columns: info.columns,
}
}
@@ -113,6 +116,10 @@ func (info *ResourceInfo) NewListFunc() runtime.Object {
return info.newList()
}
func (info *ResourceInfo) TableConverter() utils.TableConvertor {
return utils.NewTableConverter(info.GroupResource(), info.columns)
}
func (info *ResourceInfo) NewNotFound(name string) *errors.StatusError {
return errors.NewNotFound(info.SingularGroupResource(), name)
}

View File

@@ -1,10 +1,14 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -17,18 +21,78 @@ var UserResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"users", "user", "User",
func() runtime.Object { return &User{} },
func() runtime.Object { return &UserList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Login", Type: "string", Format: "string", Description: "The user login"},
{Name: "Email", Type: "string", Format: "string", Description: "The user email"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
u, ok := obj.(*User)
if ok {
return []interface{}{
u.Name,
u.Spec.Login,
u.Spec.Email,
u.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
return nil, fmt.Errorf("expected user")
},
},
)
var TeamResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"teams", "team", "Team",
func() runtime.Object { return &Team{} },
func() runtime.Object { return &TeamList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The team name"},
{Name: "Email", Type: "string", Format: "string", Description: "team email"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*Team)
if !ok {
return nil, fmt.Errorf("expected team")
}
return []interface{}{
m.Name,
m.Spec.Title,
m.Spec.Email,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
},
)
var ServiceAccountResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"serviceaccounts", "serviceaccount", "ServiceAccount",
func() runtime.Object { return &ServiceAccount{} },
func() runtime.Object { return &ServiceAccountList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Account", Type: "string", Format: "string", Description: "The service account email"},
{Name: "Email", Type: "string", Format: "string", Description: "The user email"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
u, ok := obj.(*ServiceAccount)
if ok {
return []interface{}{
u.Name,
u.Spec.Name,
u.Spec.Email,
u.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
return nil, fmt.Errorf("expected service account")
},
},
)
var (

View File

@@ -11,70 +11,74 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
)
type TableColumns struct {
Definition []metav1.TableColumnDefinition
Reader func(obj any) ([]interface{}, error)
}
type TableConvertor interface {
ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error)
}
// Based on https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go
type customTableConvertor struct {
gr schema.GroupResource
columns []metav1.TableColumnDefinition
reader func(obj any) ([]interface{}, error)
columns TableColumns
}
func NewTableConverter(gr schema.GroupResource, columns []metav1.TableColumnDefinition, reader func(obj any) ([]interface{}, error)) rest.TableConvertor {
converter := customTableConvertor{
gr: gr,
columns: columns,
reader: reader,
func NewTableConverter(gr schema.GroupResource, columns TableColumns) TableConvertor {
if columns.Reader == nil {
columns = TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
v, err := meta.Accessor(obj)
if err == nil && v != nil {
return []interface{}{
v.GetName(),
v.GetCreationTimestamp().UTC().Format(time.RFC3339),
}, nil
}
r := reflect.ValueOf(obj).Elem()
n := r.FieldByName("Name").String()
if n != "" {
return []interface{}{
n,
"",
}, nil
}
return []interface{}{
fmt.Sprintf("%v", obj),
"",
}, nil
},
}
}
// Replace the description on standard columns with the global values
for idx, column := range converter.columns {
for idx, column := range columns.Definition {
if column.Description == "" {
switch column.Name {
case "Name":
converter.columns[idx].Description = swaggerMetadataDescriptions["name"]
columns.Definition[idx].Description = swaggerMetadataDescriptions["name"]
case "Created At":
converter.columns[idx].Description = swaggerMetadataDescriptions["creationTimestamp"]
columns.Definition[idx].Description = swaggerMetadataDescriptions["creationTimestamp"]
}
}
}
return converter
return customTableConvertor{
gr: gr,
columns: columns,
}
}
func NewDefaultTableConverter(gr schema.GroupResource) rest.TableConvertor {
return NewTableConverter(gr,
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
v, err := meta.Accessor(obj)
if err == nil && v != nil {
return []interface{}{
v.GetName(),
v.GetCreationTimestamp().UTC().Format(time.RFC3339),
}, nil
}
r := reflect.ValueOf(obj).Elem()
n := r.FieldByName("Name").String()
if n != "" {
return []interface{}{
n,
"",
}, nil
}
return []interface{}{
fmt.Sprintf("%v", obj),
"",
}, nil
},
)
}
var _ rest.TableConvertor = &customTableConvertor{}
var _ TableConvertor = &customTableConvertor{}
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
func (c customTableConvertor) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
@@ -85,12 +89,9 @@ func (c customTableConvertor) ConvertToTable(ctx context.Context, object runtime
table = &metav1.Table{}
}
fn := func(obj runtime.Object) error {
cells, err := c.reader(obj)
cells, err := c.columns.Reader(obj)
if err != nil {
resource := c.gr
if info, ok := request.RequestInfoFrom(ctx); ok {
resource = schema.GroupResource{Group: info.APIGroup, Resource: info.Resource}
}
return errNotAcceptable{resource: resource}
}
table.Rows = append(table.Rows, metav1.TableRow{
@@ -119,7 +120,7 @@ func (c customTableConvertor) ConvertToTable(ctx context.Context, object runtime
}
}
if opt, ok := tableOptions.(*metav1.TableOptions); !ok || !opt.NoHeaders {
table.ColumnDefinitions = c.columns
table.ColumnDefinitions = c.columns.Definition
}
return table, nil
}

View File

@@ -7,33 +7,34 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/grafana/grafana/pkg/services/apiserver/utils"
)
func TestTableConverter(t *testing.T) {
// dummy converter
converter := utils.NewTableConverter(
schema.GroupResource{Group: "x", Resource: "y"},
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Dummy", Type: "string", Format: "string", Description: "Something here"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*metav1.APIGroup)
if !ok {
return nil, fmt.Errorf("expected status")
}
ts := metav1.NewTime(time.UnixMilli(10000000))
return []interface{}{
m.Name,
"dummy",
ts.Time.UTC().Format(time.RFC3339),
}, nil
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Dummy", Type: "string", Format: "string", Description: "Something here"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*metav1.APIGroup)
if !ok {
return nil, fmt.Errorf("expected status")
}
ts := metav1.NewTime(time.UnixMilli(10000000))
return []interface{}{
m.Name,
"dummy",
ts.Time.UTC().Format(time.RFC3339),
}, nil
},
},
)
@@ -97,7 +98,7 @@ func TestTableConverter(t *testing.T) {
// Default table converter
// Convert a single table
converter = utils.NewDefaultTableConverter(schema.GroupResource{Group: "x", Resource: "y"})
converter = utils.NewTableConverter(schema.GroupResource{Group: "x", Resource: "y"}, utils.TableColumns{})
table, err = converter.ConvertToTable(context.Background(), &metav1.APIGroup{
Name: "hello",
}, nil)

View File

@@ -10,6 +10,7 @@ import (
"k8s.io/apiserver/pkg/registry/generic"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
)
@@ -28,11 +29,44 @@ var (
"timeintervals", "timeinterval", "TimeInterval",
func() runtime.Object { return &TimeInterval{} },
func() runtime.Object { return &TimeIntervalList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
// {Name: "Intervals", Type: "string", Format: "string", Description: "The display name"},
},
Reader: func(obj any) ([]interface{}, error) {
r, ok := obj.(*TimeInterval)
if ok {
return []interface{}{
r.Name,
// r.Spec, //TODO implement formatting for Spec, same as UI?
}, nil
}
return nil, fmt.Errorf("expected resource or info")
},
},
)
ReceiverResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"receivers", "receiver", "Receiver",
func() runtime.Object { return &Receiver{} },
func() runtime.Object { return &ReceiverList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The receiver name"}, // TODO: Add integration types.
},
Reader: func(obj any) ([]interface{}, error) {
r, ok := obj.(*Receiver)
if ok {
return []interface{}{
r.Name,
r.Spec.Title,
// r.Spec, //TODO implement formatting for Spec, same as UI?
}, nil
}
return nil, fmt.Errorf("expected resource or info")
},
},
)
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}

View File

@@ -1,10 +1,14 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -17,6 +21,26 @@ var DashboardResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"dashboards", "dashboard", "Dashboard",
func() runtime.Object { return &Dashboard{} },
func() runtime.Object { return &DashboardList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
dash, ok := obj.(*Dashboard)
if ok {
if dash != nil {
return []interface{}{
dash.Name,
dash.Spec.GetNestedString("title"),
dash.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
}
return nil, fmt.Errorf("expected dashboard or summary")
},
},
)
var (

View File

@@ -1,10 +1,14 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -17,6 +21,24 @@ var DashboardSnapshotResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"dashboardsnapshots", "dashboardsnapshot", "DashboardSnapshot",
func() runtime.Object { return &DashboardSnapshot{} },
func() runtime.Object { return &DashboardSnapshotList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The snapshot name"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*DashboardSnapshot)
if ok {
return []interface{}{
m.Name,
m.Spec.Title,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
return nil, fmt.Errorf("expected snapshot")
},
}, // default table converter
)
var (

View File

@@ -1,9 +1,13 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
const (
@@ -15,4 +19,24 @@ var GenericConnectionResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"connections", "connection", "DataSourceConnection",
func() runtime.Object { return &DataSourceConnection{} },
func() runtime.Object { return &DataSourceConnectionList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The datasource title"},
{Name: "APIVersion", Type: "string", Format: "string", Description: "API Version"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*DataSourceConnection)
if !ok {
return nil, fmt.Errorf("expected connection")
}
return []interface{}{
m.Name,
m.Title,
m.APIVersion,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
},
)

View File

@@ -1,10 +1,13 @@
package v0alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -18,6 +21,24 @@ var FeatureResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"features", "feature", "Feature",
func() runtime.Object { return &Feature{} },
func() runtime.Object { return &FeatureList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Stage", Type: "string", Format: "string", Description: "Where is the flag in the dev cycle"},
{Name: "Owner", Type: "string", Format: "string", Description: "Which team owns the feature"},
},
Reader: func(obj any) ([]interface{}, error) {
r, ok := obj.(*Feature)
if ok {
return []interface{}{
r.Name,
r.Spec.Stage,
r.Spec.Owner,
}, nil
}
return nil, fmt.Errorf("expected resource or info")
},
},
)
// TogglesResourceInfo represents the actual configuration
@@ -25,6 +46,7 @@ var TogglesResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"featuretoggles", "featuretoggle", "FeatureToggles",
func() runtime.Object { return &FeatureToggles{} },
func() runtime.Object { return &FeatureTogglesList{} },
utils.TableColumns{}, // default table converter
)
var (

View File

@@ -1,10 +1,13 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -18,6 +21,25 @@ var FolderResourceInfo = common.NewResourceInfo(GROUP, VERSION,
RESOURCE, "folder", "Folder",
func() runtime.Object { return &Folder{} },
func() runtime.Object { return &FolderList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The display name"},
{Name: "Parent", Type: "string", Format: "string", Description: "Parent folder UID"},
},
Reader: func(obj any) ([]interface{}, error) {
r, ok := obj.(*Folder)
if ok {
accessor, _ := utils.MetaAccessor(r)
return []interface{}{
r.Name,
r.Spec.Title,
accessor.GetFolder(),
}, nil
}
return nil, fmt.Errorf("expected folder")
},
},
)
var (

View File

@@ -6,6 +6,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
const (
@@ -18,6 +19,7 @@ var QueryTemplateResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"querytemplates", "querytemplate", "QueryTemplate",
func() runtime.Object { return &QueryTemplate{} },
func() runtime.Object { return &QueryTemplateList{} },
utils.TableColumns{}, // default table converter
)
var (

View File

@@ -1,10 +1,14 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -19,6 +23,26 @@ var PlaylistResourceInfo = common.NewResourceInfo(GROUP, VERSION,
RESOURCE, "playlist", "Playlist",
func() runtime.Object { return &Playlist{} },
func() runtime.Object { return &PlaylistList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The playlist name"},
{Name: "Interval", Type: "string", Format: "string", Description: "How often the playlist will update"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*Playlist)
if !ok {
return nil, fmt.Errorf("expected playlist")
}
return []interface{}{
m.Name,
m.Spec.Title,
m.Spec.Interval,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
},
)
var (

View File

@@ -5,6 +5,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
const (
@@ -17,12 +18,14 @@ var DataSourceApiServerResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"datasourceapiservers", "datasourceapiserver", "DataSourceApiServer",
func() runtime.Object { return &DataSourceApiServer{} },
func() runtime.Object { return &DataSourceApiServerList{} },
utils.TableColumns{}, // default table converter
)
var QueryTypeDefinitionResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"querytypes", "querytype", "QueryTypeDefinition",
func() runtime.Object { return &QueryTypeDefinition{} },
func() runtime.Object { return &QueryTypeDefinitionList{} },
utils.TableColumns{}, // default table converter
)
var (

View File

@@ -1,10 +1,14 @@
package v0alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
@@ -17,18 +21,84 @@ var ScopeResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"scopes", "scope", "Scope",
func() runtime.Object { return &Scope{} },
func() runtime.Object { return &ScopeList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Title", Type: "string"},
{Name: "Filters", Type: "array"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*Scope)
if !ok {
return nil, fmt.Errorf("expected scope")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title,
m.Spec.Filters,
}, nil
},
}, // default table converter
)
var ScopeDashboardBindingResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"scopedashboardbindings", "scopedashboardbinding", "ScopeDashboardBinding",
func() runtime.Object { return &ScopeDashboardBinding{} },
func() runtime.Object { return &ScopeDashboardBindingList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Dashboard", Type: "string"},
{Name: "Scope", Type: "string"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*ScopeDashboardBinding)
if !ok {
return nil, fmt.Errorf("expected scope dashboard binding")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Dashboard,
m.Spec.Scope,
}, nil
},
},
)
var ScopeNodeResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"scopenodes", "scopenode", "ScopeNode",
func() runtime.Object { return &ScopeNode{} },
func() runtime.Object { return &ScopeNodeList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Title", Type: "string"},
{Name: "Parent Name", Type: "string"},
{Name: "Node Type", Type: "string"},
{Name: "Link Type", Type: "string"},
{Name: "Link ID", Type: "string"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*ScopeNode)
if !ok {
return nil, fmt.Errorf("expected scope node")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title,
m.Spec.ParentName,
m.Spec.NodeType,
m.Spec.LinkType,
m.Spec.LinkID,
}, nil
},
}, // default table converter
)
var (

View File

@@ -1,11 +1,14 @@
package v0alpha1
import (
"fmt"
"time"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
const (
@@ -18,6 +21,24 @@ var ExternalNameResourceInfo = common.NewResourceInfo(GROUP, VERSION,
"externalnames", "externalname", "ExternalName",
func() runtime.Object { return &ExternalName{} },
func() runtime.Object { return &ExternalNameList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Host", Type: "string", Format: "string", Description: "The service host"},
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*ExternalName)
if !ok {
return nil, fmt.Errorf("expected external name")
}
return []interface{}{
m.Name,
m.Spec.Host,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
}, // default table converter
)
var (

View File

@@ -1,19 +1,14 @@
package receiver
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
model "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/apiserver/utils"
)
var _ grafanarest.Storage = (*storage)(nil)
@@ -35,25 +30,9 @@ func NewStorage(
dualWriteBuilder grafanarest.DualWriteBuilder,
) (rest.Storage, error) {
legacyStore := &legacyStorage{
service: legacySvc,
namespacer: namespacer,
tableConverter: utils.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The receiver name"}, // TODO: Add integration types.
},
func(obj any) ([]interface{}, error) {
r, ok := obj.(*model.Receiver)
if ok {
return []interface{}{
r.Name,
r.Spec.Title,
// r.Spec, //TODO implement formatting for Spec, same as UI?
}, nil
}
return nil, fmt.Errorf("expected resource or info")
}),
service: legacySvc,
namespacer: namespacer,
tableConverter: resourceInfo.TableConverter(),
}
if optsGetter != nil && dualWriteBuilder != nil {
strategy := grafanaregistry.NewStrategy(scheme)

View File

@@ -3,7 +3,6 @@ package timeinterval
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
@@ -16,7 +15,6 @@ import (
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/apiserver/utils"
)
var _ grafanarest.Storage = (*storage)(nil)
@@ -38,24 +36,9 @@ func NewStorage(
dualWriteBuilder grafanarest.DualWriteBuilder,
) (rest.Storage, error) {
legacyStore := &legacyStorage{
service: legacySvc,
namespacer: namespacer,
tableConverter: utils.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
// {Name: "Intervals", Type: "string", Format: "string", Description: "The display name"},
},
func(obj any) ([]interface{}, error) {
r, ok := obj.(*model.TimeInterval)
if ok {
return []interface{}{
r.Name,
// r.Spec, //TODO implement formatting for Spec, same as UI?
}, nil
}
return nil, fmt.Errorf("expected resource or info")
}),
service: legacySvc,
namespacer: namespacer,
tableConverter: resourceInfo.TableConverter(),
}
if optsGetter != nil && dualWriteBuilder != nil {
strategy := grafanaregistry.NewStrategy(scheme)

View File

@@ -1,9 +1,6 @@
package dashboard
import (
"fmt"
"time"
"github.com/prometheus/client_golang/prometheus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -25,7 +22,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/provisioning"
@@ -67,28 +63,9 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
accessControl: accessControl,
legacy: &dashboardStorage{
resource: dashboard.DashboardResourceInfo,
access: legacy.NewDashboardAccess(sql, namespacer, dashStore, provisioning),
tableConverter: gapiutil.NewTableConverter(
dashboard.DashboardResourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
dash, ok := obj.(*dashboard.Dashboard)
if ok {
if dash != nil {
return []interface{}{
dash.Name,
dash.Spec.GetNestedString("title"),
dash.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
}
return nil, fmt.Errorf("expected dashboard or summary")
}),
resource: dashboard.DashboardResourceInfo,
access: legacy.NewDashboardAccess(sql, namespacer, dashStore, provisioning),
tableConverter: dashboard.DashboardResourceInfo.TableConverter(),
},
}
apiregistration.RegisterAPI(builder)

View File

@@ -1,17 +1,12 @@
package dashboard
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
)
var _ grafanarest.Storage = (*storage)(nil)
@@ -34,27 +29,8 @@ func newStorage(scheme *runtime.Scheme) (*storage, error) {
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
TableConvertor: resourceInfo.TableConverter(),
}
store.TableConvertor = gapiutil.NewTableConverter(
store.DefaultQualifiedResource,
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
dash, ok := obj.(*v0alpha1.Dashboard)
if ok {
if dash != nil {
return []interface{}{
dash.Name,
dash.Spec.GetNestedString("title"),
dash.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
}
return nil, fmt.Errorf("expected dashboard or summary")
})
return &storage{Store: store}, nil
}

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus"
@@ -28,7 +27,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -135,25 +133,7 @@ func (b *SnapshotsAPIBuilder) GetAPIGroupInfo(
namespacer: b.namespacer,
options: b.options,
}
legacyStore.tableConverter = gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The snapshot name"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*dashboardsnapshot.DashboardSnapshot)
if ok {
return []interface{}{
m.Name,
m.Spec.Title,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
return nil, fmt.Errorf("expected snapshot")
},
)
legacyStore.tableConverter = resourceInfo.TableConverter()
storage[resourceInfo.StoragePath()] = legacyStore
storage[resourceInfo.StoragePath("body")] = &subBodyREST{
service: b.service,

View File

@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/prometheus/client_golang/prometheus"
@@ -29,7 +28,6 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/query/queryschema"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource/kinds"
@@ -209,29 +207,9 @@ func (b *DataSourceAPIBuilder) GetAPIGroupInfo(
conn := b.connectionResourceInfo
storage[conn.StoragePath()] = &connectionAccess{
datasources: b.datasources,
resourceInfo: conn,
tableConverter: gapiutil.NewTableConverter(
conn.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The datasource title"},
{Name: "APIVersion", Type: "string", Format: "string", Description: "API Version"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*datasource.DataSourceConnection)
if !ok {
return nil, fmt.Errorf("expected connection")
}
return []interface{}{
m.Name,
m.Title,
m.APIVersion,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
),
datasources: b.datasources,
resourceInfo: conn,
tableConverter: conn.TableConverter(),
}
storage[conn.StoragePath("query")] = &subQueryREST{builder: b}
storage[conn.StoragePath("health")] = &subHealthREST{builder: b}

View File

@@ -12,7 +12,6 @@ import (
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apis/featuretoggle/v0alpha1"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
@@ -36,25 +35,8 @@ type featuresStorage struct {
func NewFeaturesStorage() *featuresStorage {
resourceInfo := v0alpha1.FeatureResourceInfo
return &featuresStorage{
resource: &resourceInfo,
tableConverter: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Stage", Type: "string", Format: "string", Description: "Where is the flag in the dev cycle"},
{Name: "Owner", Type: "string", Format: "string", Description: "Which team owns the feature"},
},
func(obj any) ([]interface{}, error) {
r, ok := obj.(*v0alpha1.Feature)
if ok {
return []interface{}{
r.Name,
r.Spec.Stage,
r.Spec.Owner,
}, nil
}
return nil, fmt.Errorf("expected resource or info")
}),
resource: &resourceInfo,
tableConverter: resourceInfo.TableConverter(),
}
}

View File

@@ -2,7 +2,6 @@ package folders
import (
"context"
"fmt"
"github.com/prometheus/client_golang/prometheus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -17,13 +16,11 @@ import (
"k8s.io/kube-openapi/pkg/spec3"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
@@ -107,27 +104,9 @@ func (b *FolderAPIBuilder) GetAPIGroupInfo(
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v0alpha1.GROUP, scheme, metav1.ParameterCodec, codecs)
legacyStore := &legacyStorage{
service: b.folderSvc,
namespacer: b.namespacer,
tableConverter: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The display name"},
{Name: "Parent", Type: "string", Format: "string", Description: "Parent folder UID"},
},
func(obj any) ([]interface{}, error) {
r, ok := obj.(*v0alpha1.Folder)
if ok {
accessor, _ := utils.MetaAccessor(r)
return []interface{}{
r.Name,
r.Spec.Title,
accessor.GetFolder(),
}, nil
}
return nil, fmt.Errorf("expected resource or info")
}),
service: b.folderSvc,
namespacer: b.namespacer,
tableConverter: resourceInfo.TableConverter(),
}
storage := map[string]rest.Storage{}

View File

@@ -2,8 +2,6 @@ package identity
import (
"context"
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -19,7 +17,6 @@ import (
identityapi "github.com/grafana/grafana/pkg/apimachinery/identity"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/user"
@@ -87,85 +84,25 @@ func (b *IdentityAPIBuilder) GetAPIGroupInfo(
team := identity.TeamResourceInfo
teamStore := &legacyTeamStorage{
service: b.svcTeam,
resourceInfo: team,
tableConverter: gapiutil.NewTableConverter(
team.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The team name"},
{Name: "Email", Type: "string", Format: "string", Description: "team email"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*identity.Team)
if !ok {
return nil, fmt.Errorf("expected playlist")
}
return []interface{}{
m.Name,
m.Spec.Title,
m.Spec.Email,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
),
service: b.svcTeam,
resourceInfo: team,
tableConverter: team.TableConverter(),
}
storage[team.StoragePath()] = teamStore
user := identity.UserResourceInfo
userStore := &legacyUserStorage{
service: b.svcUser,
resourceInfo: user,
tableConverter: gapiutil.NewTableConverter(
user.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Login", Type: "string", Format: "string", Description: "The user login"},
{Name: "Email", Type: "string", Format: "string", Description: "The user email"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
u, ok := obj.(*identity.User)
if ok {
return []interface{}{
u.Name,
u.Spec.Login,
u.Spec.Email,
u.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
return nil, fmt.Errorf("expected user")
},
),
service: b.svcUser,
resourceInfo: user,
tableConverter: user.TableConverter(),
}
storage[user.StoragePath()] = userStore
sa := identity.ServiceAccountResourceInfo
saStore := &legacyServiceAccountStorage{
service: b.svcUser,
resourceInfo: sa,
tableConverter: gapiutil.NewTableConverter(
user.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Account", Type: "string", Format: "string", Description: "The service account email"},
{Name: "Email", Type: "string", Format: "string", Description: "The user email"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
u, ok := obj.(*identity.ServiceAccount)
if ok {
return []interface{}{
u.Name,
u.Spec.Name,
u.Spec.Email,
u.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
return nil, fmt.Errorf("expected user")
},
),
service: b.svcUser,
resourceInfo: sa,
tableConverter: sa.TableConverter(),
}
storage[sa.StoragePath()] = saStore

View File

@@ -1,10 +1,6 @@
package peakq
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
@@ -12,7 +8,6 @@ import (
peakq "github.com/grafana/grafana/pkg/apis/peakq/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
)
var _ grafanarest.Storage = (*storage)(nil)
@@ -33,28 +28,10 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
PredicateFunc: grafanaregistry.Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*peakq.QueryTemplate)
if !ok {
return nil, fmt.Errorf("expected query template")
}
return []interface{}{
m.Name,
m.Spec.Title,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: grafanaregistry.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {

View File

@@ -1,9 +1,6 @@
package playlist
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -21,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
playlistsvc "github.com/grafana/grafana/pkg/services/playlist"
"github.com/grafana/grafana/pkg/setting"
)
@@ -94,27 +90,7 @@ func (b *PlaylistAPIBuilder) GetAPIGroupInfo(
service: b.service,
namespacer: b.namespacer,
}
legacyStore.tableConverter = gapiutil.NewTableConverter(
resource.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The playlist name"},
{Name: "Interval", Type: "string", Format: "string", Description: "How often the playlist will update"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*playlist.Playlist)
if !ok {
return nil, fmt.Errorf("expected playlist")
}
return []interface{}{
m.Name,
m.Spec.Title,
m.Spec.Interval,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
)
legacyStore.tableConverter = resource.TableConverter()
storage[resource.StoragePath()] = legacyStore
// enable dual writes if a RESTOptionsGetter is provided

View File

@@ -2,9 +2,7 @@ package scope
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
@@ -15,7 +13,6 @@ import (
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
)
var _ grafanarest.Storage = (*storage)(nil)
@@ -36,30 +33,10 @@ func newScopeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGette
PredicateFunc: Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Title", Type: "string"},
{Name: "Filters", Type: "array"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*scope.Scope)
if !ok {
return nil, fmt.Errorf("expected scope")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title,
m.Spec.Filters,
}, nil
},
),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
@@ -80,30 +57,10 @@ func newScopeDashboardBindingStorage(scheme *runtime.Scheme, optsGetter generic.
PredicateFunc: Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Dashboard", Type: "string"},
{Name: "Scope", Type: "string"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*scope.ScopeDashboardBinding)
if !ok {
return nil, fmt.Errorf("expected scope dashboard binding")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Dashboard,
m.Spec.Scope,
}, nil
},
),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
@@ -124,36 +81,10 @@ func newScopeNodeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsG
PredicateFunc: Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Title", Type: "string"},
{Name: "Parent Name", Type: "string"},
{Name: "Node Type", Type: "string"},
{Name: "Link Type", Type: "string"},
{Name: "Link ID", Type: "string"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*scope.ScopeNode)
if !ok {
return nil, fmt.Errorf("expected scope node")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title,
m.Spec.ParentName,
m.Spec.NodeType,
m.Spec.LinkType,
m.Spec.LinkID,
}, nil
},
),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {

View File

@@ -1,10 +1,6 @@
package service
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
@@ -12,7 +8,6 @@ import (
service "github.com/grafana/grafana/pkg/apis/service/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
)
var _ grafanarest.Storage = (*storage)(nil)
@@ -33,28 +28,10 @@ func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*
PredicateFunc: grafanaregistry.Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: gapiutil.NewTableConverter(
resourceInfo.GroupResource(),
[]metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Host", Type: "string", Format: "string", Description: "The service host"},
{Name: "Created At", Type: "date"},
},
func(obj any) ([]interface{}, error) {
m, ok := obj.(*service.ExternalName)
if !ok {
return nil, fmt.Errorf("expected playlist")
}
return []interface{}{
m.Name,
m.Spec.Host,
m.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
},
),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: grafanaregistry.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {