K8s: Add resource type helper to avoid so many hardcoded names (#79344)

This commit is contained in:
Ryan McKinley 2023-12-11 12:03:48 -08:00 committed by GitHub
parent 5147bdeb4b
commit f69516bf47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 190 additions and 71 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
internalplaylist "github.com/grafana/grafana/pkg/registry/apis/playlist" internalplaylist "github.com/grafana/grafana/pkg/registry/apis/playlist"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
@ -329,11 +330,7 @@ type playlistK8sHandler struct {
func newPlaylistK8sHandler(hs *HTTPServer) *playlistK8sHandler { func newPlaylistK8sHandler(hs *HTTPServer) *playlistK8sHandler {
return &playlistK8sHandler{ return &playlistK8sHandler{
gvr: schema.GroupVersionResource{ gvr: v0alpha1.PlaylistResourceInfo.GroupVersionResource(),
Group: internalplaylist.GroupName,
Version: "v0alpha1",
Resource: "playlists",
},
namespacer: request.GetNamespaceMapper(hs.Cfg), namespacer: request.GetNamespaceMapper(hs.Cfg),
clientConfigProvider: hs.clientConfigProvider, clientConfigProvider: hs.clientConfigProvider,
} }

View File

@ -2,6 +2,26 @@ package v0alpha1
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"github.com/grafana/grafana/pkg/apis"
)
const (
GROUP = "example.grafana.app"
VERSION = "v0alpha1"
APIVERSION = GROUP + "/" + VERSION
)
var RuntimeResourceInfo = apis.NewResourceInfo(GROUP, VERSION,
"runtime", "runtime", "RuntimeInfo",
func() runtime.Object { return &RuntimeInfo{} },
func() runtime.Object { return &RuntimeInfo{} },
)
var DummyResourceInfo = apis.NewResourceInfo(GROUP, VERSION,
"dummy", "dummy", "DummyResource",
func() runtime.Object { return &DummyResource{} },
func() runtime.Object { return &DummyResourceList{} },
) )
// Mirrors the info exposed in "github.com/grafana/grafana/pkg/setting" // Mirrors the info exposed in "github.com/grafana/grafana/pkg/setting"

View File

@ -2,6 +2,21 @@ package v0alpha1
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"github.com/grafana/grafana/pkg/apis"
)
const (
GROUP = "playlist.grafana.app"
VERSION = "v0alpha1"
APIVERSION = GROUP + "/" + VERSION
)
var PlaylistResourceInfo = apis.NewResourceInfo(GROUP, VERSION,
"playlists", "playlist", "Playlist",
func() runtime.Object { return &Playlist{} },
func() runtime.Object { return &PlaylistList{} },
) )
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

87
pkg/apis/types.go Normal file
View File

@ -0,0 +1,87 @@
package apis
import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// ResourceInfo helps define a k8s resource
type ResourceInfo struct {
group string
version string
resourceName string
singularName string
kind string
newObj func() runtime.Object
newList func() runtime.Object
}
func NewResourceInfo(group, version, resourceName, singularName, kind string,
newObj func() runtime.Object, newList func() runtime.Object) ResourceInfo {
return ResourceInfo{group, version, resourceName, singularName, kind, newObj, newList}
}
func (info *ResourceInfo) GetSingularName() string {
return info.singularName
}
// TypeMeta returns k8s type
func (info *ResourceInfo) TypeMeta() metav1.TypeMeta {
return metav1.TypeMeta{
Kind: info.kind,
APIVersion: info.group + "/" + info.version,
}
}
func (info *ResourceInfo) GroupVersion() schema.GroupVersion {
return schema.GroupVersion{
Group: info.group,
Version: info.version,
}
}
func (info *ResourceInfo) GroupResource() schema.GroupResource {
return schema.GroupResource{
Group: info.group,
Resource: info.resourceName,
}
}
func (info *ResourceInfo) SingularGroupResource() schema.GroupResource {
return schema.GroupResource{
Group: info.group,
Resource: info.singularName,
}
}
func (info *ResourceInfo) GroupVersionResource() schema.GroupVersionResource {
return schema.GroupVersionResource{
Group: info.group,
Version: info.version,
Resource: info.resourceName,
}
}
func (info *ResourceInfo) StoragePath(sub ...string) string {
switch len(sub) {
case 0:
return info.resourceName
case 1:
return info.resourceName + "/" + sub[0]
}
panic("invalid subresource path")
}
func (info *ResourceInfo) NewFunc() runtime.Object {
return info.newObj()
}
func (info *ResourceInfo) NewListFunc() runtime.Object {
return info.newList()
}
func (info *ResourceInfo) NewNotFound(name string) *errors.StatusError {
return errors.NewNotFound(info.SingularGroupResource(), name)
}

View File

@ -32,18 +32,19 @@ type dummyStorage struct {
} }
func newDummyStorage(gv schema.GroupVersion, scheme *runtime.Scheme, names ...string) *dummyStorage { func newDummyStorage(gv schema.GroupVersion, scheme *runtime.Scheme, names ...string) *dummyStorage {
var resourceInfo = example.DummyResourceInfo
strategy := grafanaregistry.NewStrategy(scheme) strategy := grafanaregistry.NewStrategy(scheme)
store := &genericregistry.Store{ store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &example.DummyResource{} }, // getter not supported NewFunc: resourceInfo.NewFunc,
NewListFunc: func() runtime.Object { return &example.DummyResourceList{} }, // both list and get return the same thing NewListFunc: resourceInfo.NewListFunc,
PredicateFunc: grafanaregistry.Matcher, PredicateFunc: grafanaregistry.Matcher,
DefaultQualifiedResource: gv.WithResource("dummy").GroupResource(), DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: gv.WithResource("dummy").GroupResource(), SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: rest.NewDefaultTableConvertor(resourceInfo.GroupResource()),
CreateStrategy: strategy, CreateStrategy: strategy,
UpdateStrategy: strategy, UpdateStrategy: strategy,
DeleteStrategy: strategy, DeleteStrategy: strategy,
} }
store.TableConvertor = rest.NewDefaultTableConvertor(store.DefaultQualifiedResource)
return &dummyStorage{ return &dummyStorage{
store: store, store: store,
@ -63,7 +64,7 @@ func (s *dummyStorage) NamespaceScoped() bool {
} }
func (s *dummyStorage) GetSingularName() string { func (s *dummyStorage) GetSingularName() string {
return "dummy" return example.DummyResourceInfo.GetSingularName()
} }
func (s *dummyStorage) NewList() runtime.Object { func (s *dummyStorage) NewList() runtime.Object {

View File

@ -23,11 +23,6 @@ import (
grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver"
) )
// GroupName is the group name for this API.
const GroupName = "example.grafana.app"
const VersionID = "v0alpha1" //
const APIVersion = GroupName + "/" + VersionID
var _ grafanaapiserver.APIGroupBuilder = (*TestingAPIBuilder)(nil) var _ grafanaapiserver.APIGroupBuilder = (*TestingAPIBuilder)(nil)
// This is used just so wire has something unique to return // This is used just so wire has something unique to return
@ -38,7 +33,7 @@ type TestingAPIBuilder struct {
func NewTestingAPIBuilder() *TestingAPIBuilder { func NewTestingAPIBuilder() *TestingAPIBuilder {
return &TestingAPIBuilder{ return &TestingAPIBuilder{
gv: schema.GroupVersion{Group: GroupName, Version: VersionID}, gv: schema.GroupVersion{Group: example.GROUP, Version: example.VERSION},
} }
} }
@ -89,13 +84,13 @@ func (b *TestingAPIBuilder) GetAPIGroupInfo(
optsGetter generic.RESTOptionsGetter, optsGetter generic.RESTOptionsGetter,
) (*genericapiserver.APIGroupInfo, error) { ) (*genericapiserver.APIGroupInfo, error) {
b.codecs = codecs b.codecs = codecs
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(GroupName, scheme, metav1.ParameterCodec, codecs) apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(b.gv.Group, scheme, metav1.ParameterCodec, codecs)
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
storage["runtime"] = newDeploymentInfoStorage(b.gv, scheme) storage[example.RuntimeResourceInfo.StoragePath()] = newDeploymentInfoStorage(b.gv, scheme)
storage["dummy"] = newDummyStorage(b.gv, scheme, "test1", "test2", "test3") storage[example.DummyResourceInfo.StoragePath()] = newDummyStorage(b.gv, scheme, "test1", "test2", "test3")
storage["dummy/sub"] = &dummySubresourceREST{} storage[example.DummyResourceInfo.StoragePath("sub")] = &dummySubresourceREST{}
apiGroupInfo.VersionedResourcesStorageMap[VersionID] = storage apiGroupInfo.VersionedResourcesStorageMap[b.gv.Version] = storage
return &apiGroupInfo, nil return &apiGroupInfo, nil
} }

View File

@ -29,26 +29,24 @@ type staticStorage struct {
} }
func newDeploymentInfoStorage(gv schema.GroupVersion, scheme *runtime.Scheme) *staticStorage { func newDeploymentInfoStorage(gv schema.GroupVersion, scheme *runtime.Scheme) *staticStorage {
var resourceInfo = example.RuntimeResourceInfo
strategy := grafanaregistry.NewStrategy(scheme) strategy := grafanaregistry.NewStrategy(scheme)
store := &genericregistry.Store{ store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &example.RuntimeInfo{} }, // getter not supported NewFunc: resourceInfo.NewFunc,
NewListFunc: func() runtime.Object { return &example.RuntimeInfo{} }, // both list and get return the same thing NewListFunc: resourceInfo.NewListFunc,
PredicateFunc: grafanaregistry.Matcher, PredicateFunc: grafanaregistry.Matcher,
DefaultQualifiedResource: gv.WithResource("runtime").GroupResource(), DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: gv.WithResource("runtime").GroupResource(), SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: rest.NewDefaultTableConvertor(resourceInfo.GroupResource()),
CreateStrategy: strategy, CreateStrategy: strategy,
UpdateStrategy: strategy, UpdateStrategy: strategy,
DeleteStrategy: strategy, DeleteStrategy: strategy,
} }
store.TableConvertor = rest.NewDefaultTableConvertor(store.DefaultQualifiedResource)
return &staticStorage{ return &staticStorage{
Store: store, Store: store,
info: example.RuntimeInfo{ info: example.RuntimeInfo{
TypeMeta: metav1.TypeMeta{ TypeMeta: example.RuntimeResourceInfo.TypeMeta(),
APIVersion: APIVersion,
Kind: "DeploymentInfo",
},
BuildVersion: setting.BuildVersion, BuildVersion: setting.BuildVersion,
BuildCommit: setting.BuildCommit, BuildCommit: setting.BuildCommit,
BuildBranch: setting.BuildBranch, BuildBranch: setting.BuildBranch,
@ -72,7 +70,7 @@ func (s *staticStorage) NamespaceScoped() bool {
} }
func (s *staticStorage) GetSingularName() string { func (s *staticStorage) GetSingularName() string {
return "runtime" return example.RuntimeResourceInfo.GetSingularName()
} }
func (s *staticStorage) NewList() runtime.Object { func (s *staticStorage) NewList() runtime.Object {

View File

@ -5,11 +5,9 @@ import (
"errors" "errors"
"fmt" "fmt"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion" "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
playlist "github.com/grafana/grafana/pkg/apis/playlist/v0alpha1" playlist "github.com/grafana/grafana/pkg/apis/playlist/v0alpha1"
@ -28,17 +26,16 @@ var (
_ rest.GracefulDeleter = (*legacyStorage)(nil) _ rest.GracefulDeleter = (*legacyStorage)(nil)
) )
var resourceInfo = playlist.PlaylistResourceInfo
type legacyStorage struct { type legacyStorage struct {
service playlistsvc.Service service playlistsvc.Service
namespacer request.NamespaceMapper namespacer request.NamespaceMapper
tableConverter rest.TableConvertor tableConverter rest.TableConvertor
DefaultQualifiedResource schema.GroupResource
SingularQualifiedResource schema.GroupResource
} }
func (s *legacyStorage) New() runtime.Object { func (s *legacyStorage) New() runtime.Object {
return &playlist.Playlist{} return resourceInfo.NewFunc()
} }
func (s *legacyStorage) Destroy() {} func (s *legacyStorage) Destroy() {}
@ -48,11 +45,11 @@ func (s *legacyStorage) NamespaceScoped() bool {
} }
func (s *legacyStorage) GetSingularName() string { func (s *legacyStorage) GetSingularName() string {
return "playlist" return resourceInfo.GetSingularName()
} }
func (s *legacyStorage) NewList() runtime.Object { func (s *legacyStorage) NewList() runtime.Object {
return &playlist.PlaylistList{} return resourceInfo.NewListFunc()
} }
func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
@ -60,9 +57,7 @@ func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Objec
} }
func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
// TODO: handle fetching all available orgs when no namespace is specified orgId, err := request.OrgIDForList(ctx)
// To test: kubectl get playlists --all-namespaces
info, err := request.NamespaceInfoFrom(ctx, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -72,7 +67,7 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
limit = int(options.Limit) limit = int(options.Limit)
} }
res, err := s.service.Search(ctx, &playlistsvc.GetPlaylistsQuery{ res, err := s.service.Search(ctx, &playlistsvc.GetPlaylistsQuery{
OrgId: info.OrgID, OrgId: orgId,
Limit: limit, Limit: limit,
}) })
if err != nil { if err != nil {
@ -83,7 +78,7 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
for _, v := range res { for _, v := range res {
p, err := s.service.Get(ctx, &playlistsvc.GetPlaylistByUidQuery{ p, err := s.service.Get(ctx, &playlistsvc.GetPlaylistByUidQuery{
UID: v.UID, UID: v.UID,
OrgId: info.OrgID, OrgId: orgId,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -108,7 +103,7 @@ func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.Ge
}) })
if err != nil || dto == nil { if err != nil || dto == nil {
if errors.Is(err, playlistsvc.ErrPlaylistNotFound) || err == nil { if errors.Is(err, playlistsvc.ErrPlaylistNotFound) || err == nil {
err = k8serrors.NewNotFound(s.SingularQualifiedResource, name) err = resourceInfo.NewNotFound(name)
} }
return nil, err return nil, err
} }

View File

@ -22,10 +22,6 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
// GroupName is the group name for this API.
const GroupName = "playlist.grafana.app"
const VersionID = "v0alpha1"
var _ grafanaapiserver.APIGroupBuilder = (*PlaylistAPIBuilder)(nil) var _ grafanaapiserver.APIGroupBuilder = (*PlaylistAPIBuilder)(nil)
// This is used just so wire has something unique to return // This is used just so wire has something unique to return
@ -42,7 +38,7 @@ func RegisterAPIService(p playlistsvc.Service,
builder := &PlaylistAPIBuilder{ builder := &PlaylistAPIBuilder{
service: p, service: p,
namespacer: request.GetNamespaceMapper(cfg), namespacer: request.GetNamespaceMapper(cfg),
gv: schema.GroupVersion{Group: GroupName, Version: VersionID}, gv: playlist.PlaylistResourceInfo.GroupVersion(),
} }
apiregistration.RegisterAPI(builder) apiregistration.RegisterAPI(builder)
return builder return builder
@ -52,22 +48,23 @@ func (b *PlaylistAPIBuilder) GetGroupVersion() schema.GroupVersion {
return b.gv return b.gv
} }
func (b *PlaylistAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) {
scheme.AddKnownTypes(b.gv, scheme.AddKnownTypes(gv,
&playlist.Playlist{}, &playlist.Playlist{},
&playlist.PlaylistList{}, &playlist.PlaylistList{},
) )
}
func (b *PlaylistAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
addKnownTypes(scheme, b.gv)
// Link this version to the internal representation. // Link this version to the internal representation.
// This is used for server-side-apply (PATCH), and avoids the error: // This is used for server-side-apply (PATCH), and avoids the error:
// "no kind is registered for the type" // "no kind is registered for the type"
scheme.AddKnownTypes(schema.GroupVersion{ addKnownTypes(scheme, schema.GroupVersion{
Group: b.gv.Group, Group: b.gv.Group,
Version: runtime.APIVersionInternal, Version: runtime.APIVersionInternal,
}, })
&playlist.Playlist{},
&playlist.PlaylistList{},
)
// If multiple versions exist, then register conversions from zz_generated.conversion.go // If multiple versions exist, then register conversions from zz_generated.conversion.go
// if err := playlist.RegisterConversions(scheme); err != nil { // if err := playlist.RegisterConversions(scheme); err != nil {
@ -82,17 +79,16 @@ func (b *PlaylistAPIBuilder) GetAPIGroupInfo(
codecs serializer.CodecFactory, // pointer? codecs serializer.CodecFactory, // pointer?
optsGetter generic.RESTOptionsGetter, optsGetter generic.RESTOptionsGetter,
) (*genericapiserver.APIGroupInfo, error) { ) (*genericapiserver.APIGroupInfo, error) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(GroupName, scheme, metav1.ParameterCodec, codecs) apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(playlist.GROUP, scheme, metav1.ParameterCodec, codecs)
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
resource := playlist.PlaylistResourceInfo
legacyStore := &legacyStorage{ legacyStore := &legacyStorage{
service: b.service, service: b.service,
namespacer: b.namespacer, namespacer: b.namespacer,
DefaultQualifiedResource: b.gv.WithResource("playlists").GroupResource(),
SingularQualifiedResource: b.gv.WithResource("playlist").GroupResource(),
} }
legacyStore.tableConverter = utils.NewTableConverter( legacyStore.tableConverter = utils.NewTableConverter(
legacyStore.DefaultQualifiedResource, resource.GroupResource(),
[]metav1.TableColumnDefinition{ []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"}, {Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "The playlist name"}, {Name: "Title", Type: "string", Format: "string", Description: "The playlist name"},
@ -112,7 +108,7 @@ func (b *PlaylistAPIBuilder) GetAPIGroupInfo(
}, nil }, nil
}, },
) )
storage["playlists"] = legacyStore storage[resource.StoragePath()] = legacyStore
// enable dual writes if a RESTOptionsGetter is provided // enable dual writes if a RESTOptionsGetter is provided
if optsGetter != nil { if optsGetter != nil {
@ -120,10 +116,10 @@ func (b *PlaylistAPIBuilder) GetAPIGroupInfo(
if err != nil { if err != nil {
return nil, err return nil, err
} }
storage["playlists"] = grafanarest.NewDualWriter(legacyStore, store) storage[resource.StoragePath()] = grafanarest.NewDualWriter(legacyStore, store)
} }
apiGroupInfo.VersionedResourcesStorageMap[VersionID] = storage apiGroupInfo.VersionedResourcesStorageMap[playlist.VERSION] = storage
return &apiGroupInfo, nil return &apiGroupInfo, nil
} }

View File

@ -19,12 +19,13 @@ type storage struct {
func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, legacy *legacyStorage) (*storage, error) { func newStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter, legacy *legacyStorage) (*storage, error) {
strategy := grafanaregistry.NewStrategy(scheme) strategy := grafanaregistry.NewStrategy(scheme)
resource := playlist.PlaylistResourceInfo
store := &genericregistry.Store{ store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &playlist.Playlist{} }, NewFunc: resource.NewFunc,
NewListFunc: func() runtime.Object { return &playlist.PlaylistList{} }, NewListFunc: resource.NewListFunc,
PredicateFunc: grafanaregistry.Matcher, PredicateFunc: grafanaregistry.Matcher,
DefaultQualifiedResource: legacy.DefaultQualifiedResource, DefaultQualifiedResource: resource.GroupResource(),
SingularQualifiedResource: legacy.SingularQualifiedResource, SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: legacy.tableConverter, TableConvertor: legacy.tableConverter,
CreateStrategy: strategy, CreateStrategy: strategy,

View File

@ -8,6 +8,7 @@ import (
"k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/endpoints/request"
"github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -75,3 +76,16 @@ func ParseNamespace(ns string) (NamespaceInfo, error) {
} }
return info, nil return info, nil
} }
func OrgIDForList(ctx context.Context) (int64, error) {
ns := request.NamespaceValue(ctx)
if ns == "" {
user, err := appcontext.User(ctx)
if user != nil {
return user.OrgID, err
}
return -1, err
}
info, err := ParseNamespace(ns)
return info.OrgID, err
}