K8s: Register apiserver as background service, and list real playlists (#75338)
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
co-authored by
Ryan McKinley
parent
60922e5f77
commit
440f9a6ffb
@@ -1,63 +0,0 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
)
|
||||
|
||||
// TODO: this (or something like it) belongs in grafana-app-sdk,
|
||||
// but lets keep it here while we iterate on a few simple examples
|
||||
type APIGroupBuilder interface {
|
||||
// Add the kinds to the server scheme
|
||||
InstallSchema(scheme *runtime.Scheme) error
|
||||
|
||||
// Build the group+version behavior
|
||||
GetAPIGroupInfo(
|
||||
scheme *runtime.Scheme,
|
||||
codecs serializer.CodecFactory, // pointer?
|
||||
) *genericapiserver.APIGroupInfo
|
||||
|
||||
// Get OpenAPI definitions
|
||||
GetOpenAPIDefinitions() common.GetOpenAPIDefinitions
|
||||
|
||||
// Register additional routes with the server
|
||||
GetOpenAPIPostProcessor() func(*spec3.OpenAPI) (*spec3.OpenAPI, error)
|
||||
}
|
||||
|
||||
func OrgIdToNamespace(orgId int64) string {
|
||||
if orgId > 1 {
|
||||
return fmt.Sprintf("org-%d", orgId)
|
||||
}
|
||||
return "default"
|
||||
}
|
||||
|
||||
func NamespaceToOrgID(ns string) (int64, error) {
|
||||
parts := strings.Split(ns, "-")
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
if parts[0] == "default" {
|
||||
return 1, nil
|
||||
}
|
||||
if parts[0] == "" {
|
||||
return 0, nil // no orgId, cluster scope
|
||||
}
|
||||
return 0, fmt.Errorf("invalid namespace (expected default)")
|
||||
case 2:
|
||||
if !(parts[0] == "org" || parts[0] == "tenant") {
|
||||
return 0, fmt.Errorf("invalid namespace (org|tenant)")
|
||||
}
|
||||
n, err := strconv.ParseInt(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid namepscae (%w)", err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
return 0, fmt.Errorf("invalid namespace (%d parts)", len(parts))
|
||||
}
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apis"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
var _ rest.Scoper = (*handler)(nil)
|
||||
@@ -47,54 +48,83 @@ func (r *handler) ConvertToTable(ctx context.Context, object runtime.Object, tab
|
||||
|
||||
func (r *handler) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ns, ok := request.NamespaceFrom(ctx)
|
||||
if ok && ns != "" {
|
||||
orgId, err := apis.NamespaceToOrgID(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("OrgID: %d\n", orgId)
|
||||
if !ok || ns == "" {
|
||||
return nil, fmt.Errorf("namespace required")
|
||||
}
|
||||
|
||||
// TODO: replace
|
||||
return &PlaylistList{
|
||||
orgId, err := grafanaapiserver.NamespaceToOrgID(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
limit := 100
|
||||
if options.Limit > 0 {
|
||||
limit = int(options.Limit)
|
||||
}
|
||||
res, err := r.service.Search(ctx, &playlist.GetPlaylistsQuery{
|
||||
OrgId: orgId,
|
||||
Limit: limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := &PlaylistList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PlaylistList",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
Items: []Playlist{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
},
|
||||
Name: "test",
|
||||
}
|
||||
for _, v := range res {
|
||||
p := Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: v.UID,
|
||||
},
|
||||
}
|
||||
p.Name = v.Name + " // " + v.Interval
|
||||
list.Items = append(list.Items, p)
|
||||
// TODO?? if table... we don't need the body of each, otherwise full lookup!
|
||||
}
|
||||
if len(list.Items) == limit {
|
||||
list.Continue = "<more>" // TODO?
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (r *handler) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ns, ok := request.NamespaceFrom(ctx)
|
||||
if ok && ns != "" {
|
||||
orgId, err := apis.NamespaceToOrgID(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("OrgID: %d\n", orgId)
|
||||
if !ok || ns == "" {
|
||||
return nil, fmt.Errorf("namespace required")
|
||||
}
|
||||
|
||||
orgId, err := grafanaapiserver.NamespaceToOrgID(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := r.service.Get(ctx, &playlist.GetPlaylistByUidQuery{
|
||||
UID: name,
|
||||
OrgId: orgId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("not found?")
|
||||
}
|
||||
|
||||
// TODO: replace
|
||||
return &Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Name: p.Uid,
|
||||
},
|
||||
Name: "test",
|
||||
Name: p.Name + "//" + p.Interval,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/apis"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -10,6 +9,9 @@ import (
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
|
||||
grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
// GroupName is the group name for this API.
|
||||
@@ -17,14 +19,22 @@ const GroupName = "playlist.x.grafana.com"
|
||||
const VersionID = "v0-alpha" //
|
||||
const APIVersion = GroupName + "/" + VersionID
|
||||
|
||||
type builder struct{}
|
||||
var _ grafanaapiserver.APIGroupBuilder = (*PlaylistAPIBuilder)(nil)
|
||||
|
||||
// TODO.. this will have wire dependencies
|
||||
func GetAPIGroupBuilder() apis.APIGroupBuilder {
|
||||
return &builder{}
|
||||
// This is used just so wire has something unique to return
|
||||
type PlaylistAPIBuilder struct {
|
||||
service playlist.Service
|
||||
}
|
||||
|
||||
func (b *builder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
func RegisterAPIService(p playlist.Service, apiregistration grafanaapiserver.APIRegistrar) *PlaylistAPIBuilder {
|
||||
builder := &PlaylistAPIBuilder{
|
||||
service: p,
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (b *PlaylistAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
err := AddToScheme(scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -32,26 +42,26 @@ func (b *builder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
return scheme.SetVersionPriority(SchemeGroupVersion)
|
||||
}
|
||||
|
||||
func (b *builder) GetAPIGroupInfo(
|
||||
func (b *PlaylistAPIBuilder) GetAPIGroupInfo(
|
||||
scheme *runtime.Scheme,
|
||||
codecs serializer.CodecFactory, // pointer?
|
||||
) *genericapiserver.APIGroupInfo {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(GroupName, scheme, metav1.ParameterCodec, codecs)
|
||||
storage := map[string]rest.Storage{}
|
||||
storage["playlists"] = &handler{
|
||||
service: nil, // TODO!!!!!
|
||||
service: b.service,
|
||||
}
|
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap[VersionID] = storage
|
||||
return &apiGroupInfo
|
||||
}
|
||||
|
||||
func (b *builder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
func (b *PlaylistAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
return getOpenAPIDefinitions
|
||||
}
|
||||
|
||||
// Register additional routes with the server
|
||||
func (b *builder) GetOpenAPIPostProcessor() func(*spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
func (b *PlaylistAPIBuilder) GetOpenAPIPostProcessor() func(*spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,8 @@ type Playlist struct {
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type PlaylistList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Playlist `json:"playlists,omitempty"`
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func (in *Playlist) DeepCopyObject() runtime.Object {
|
||||
func (in *PlaylistList) DeepCopyInto(out *PlaylistList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Playlist, len(*in))
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
playlistv1 "github.com/grafana/grafana/pkg/apis/playlist/v1"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
playlistv1.RegisterAPIService,
|
||||
)
|
||||
Reference in New Issue
Block a user