mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 11:42:36 +08:00
207 lines
6.5 KiB
Go
207 lines
6.5 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/rest"
|
|
|
|
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher"
|
|
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/search/sort"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
|
)
|
|
|
|
type K8sHandler interface {
|
|
GetNamespace(orgID int64) string
|
|
Get(ctx context.Context, name string, orgID int64, options v1.GetOptions, subresource ...string) (*unstructured.Unstructured, error)
|
|
Create(ctx context.Context, obj *unstructured.Unstructured, orgID int64, opts v1.CreateOptions) (*unstructured.Unstructured, error)
|
|
Update(ctx context.Context, obj *unstructured.Unstructured, orgID int64, opts v1.UpdateOptions) (*unstructured.Unstructured, error)
|
|
Delete(ctx context.Context, name string, orgID int64, options v1.DeleteOptions) error
|
|
DeleteCollection(ctx context.Context, orgID int64) error
|
|
List(ctx context.Context, orgID int64, options v1.ListOptions) (*unstructured.UnstructuredList, error)
|
|
Search(ctx context.Context, orgID int64, in *resourcepb.ResourceSearchRequest) (*resourcepb.ResourceSearchResponse, error)
|
|
GetStats(ctx context.Context, orgID int64) (*resourcepb.ResourceStatsResponse, error)
|
|
GetUsersFromMeta(ctx context.Context, userMeta []string) (map[string]*user.User, error)
|
|
}
|
|
|
|
var _ K8sHandler = (*k8sHandler)(nil)
|
|
|
|
type k8sHandler struct {
|
|
namespacer request.NamespaceMapper
|
|
gvr schema.GroupVersionResource
|
|
restConfig func(context.Context) (*rest.Config, error)
|
|
searcher resourcepb.ResourceIndexClient
|
|
userService user.Service
|
|
}
|
|
|
|
func NewK8sHandler(dual dualwrite.Service, namespacer request.NamespaceMapper, gvr schema.GroupVersionResource,
|
|
restConfig func(context.Context) (*rest.Config, error), dashStore dashboards.Store, userSvc user.Service, resourceClient resource.ResourceClient, sorter sort.Service) K8sHandler {
|
|
legacySearcher := legacysearcher.NewDashboardSearchClient(dashStore, sorter)
|
|
searchClient := resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), gvr.GroupResource(), resourceClient, legacySearcher)
|
|
|
|
return &k8sHandler{
|
|
namespacer: namespacer,
|
|
gvr: gvr,
|
|
restConfig: restConfig,
|
|
searcher: searchClient,
|
|
userService: userSvc,
|
|
}
|
|
}
|
|
|
|
func (h *k8sHandler) GetNamespace(orgID int64) string {
|
|
return h.namespacer(orgID)
|
|
}
|
|
|
|
func (h *k8sHandler) Get(ctx context.Context, name string, orgID int64, options v1.GetOptions, subresource ...string) (*unstructured.Unstructured, error) {
|
|
client, err := h.getClient(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return client.Get(ctx, name, options, subresource...)
|
|
}
|
|
|
|
func (h *k8sHandler) Create(ctx context.Context, obj *unstructured.Unstructured, orgID int64, opts v1.CreateOptions) (*unstructured.Unstructured, error) {
|
|
client, err := h.getClient(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return client.Create(ctx, obj, opts)
|
|
}
|
|
|
|
func (h *k8sHandler) Update(ctx context.Context, obj *unstructured.Unstructured, orgID int64, opts v1.UpdateOptions) (*unstructured.Unstructured, error) {
|
|
client, err := h.getClient(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return client.Update(ctx, obj, opts)
|
|
}
|
|
|
|
func (h *k8sHandler) Delete(ctx context.Context, name string, orgID int64, options v1.DeleteOptions) error {
|
|
client, err := h.getClient(ctx, orgID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return client.Delete(ctx, name, options)
|
|
}
|
|
|
|
func (h *k8sHandler) DeleteCollection(ctx context.Context, orgID int64) error {
|
|
client, err := h.getClient(ctx, orgID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return client.DeleteCollection(ctx, v1.DeleteOptions{}, v1.ListOptions{})
|
|
}
|
|
|
|
func (h *k8sHandler) List(ctx context.Context, orgID int64, options v1.ListOptions) (*unstructured.UnstructuredList, error) {
|
|
client, err := h.getClient(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return client.List(ctx, options)
|
|
}
|
|
|
|
func (h *k8sHandler) Search(ctx context.Context, orgID int64, in *resourcepb.ResourceSearchRequest) (*resourcepb.ResourceSearchResponse, error) {
|
|
// goes directly through grpc, so doesn't need the new context
|
|
if in.Options == nil {
|
|
in.Options = &resourcepb.ListOptions{}
|
|
}
|
|
|
|
if in.Options.Key == nil {
|
|
in.Options.Key = &resourcepb.ResourceKey{
|
|
Namespace: h.GetNamespace(orgID),
|
|
Group: h.gvr.Group,
|
|
Resource: h.gvr.Resource,
|
|
}
|
|
}
|
|
|
|
return h.searcher.Search(ctx, in)
|
|
}
|
|
|
|
func (h *k8sHandler) GetStats(ctx context.Context, orgID int64) (*resourcepb.ResourceStatsResponse, error) {
|
|
// goes directly through grpc, so doesn't need the new context
|
|
return h.searcher.GetStats(ctx, &resourcepb.ResourceStatsRequest{
|
|
Namespace: h.GetNamespace(orgID),
|
|
Kinds: []string{
|
|
h.gvr.Group + "/" + h.gvr.Resource,
|
|
},
|
|
})
|
|
}
|
|
|
|
// GetUsersFromMeta takes what meta accessor gives you from `GetCreatedBy` or `GetUpdatedBy` and returns the user(s), with the meta as the key
|
|
func (h *k8sHandler) GetUsersFromMeta(ctx context.Context, usersMeta []string) (map[string]*user.User, error) {
|
|
uids := []string{}
|
|
ids := []int64{}
|
|
metaToId := make(map[string]int64)
|
|
metaToUid := make(map[string]string)
|
|
userMap := make(map[string]*user.User)
|
|
|
|
for _, userMeta := range usersMeta {
|
|
parts := strings.Split(userMeta, ":")
|
|
if len(parts) < 2 {
|
|
return userMap, nil
|
|
}
|
|
meta := parts[1]
|
|
|
|
userId, err := strconv.ParseInt(meta, 10, 64)
|
|
if err == nil {
|
|
ids = append(ids, userId)
|
|
metaToId[userMeta] = userId
|
|
} else {
|
|
uids = append(uids, meta)
|
|
metaToUid[userMeta] = meta
|
|
}
|
|
}
|
|
|
|
users, err := h.userService.ListByIdOrUID(ctx, uids, ids)
|
|
if err != nil {
|
|
return userMap, nil
|
|
}
|
|
|
|
for _, u := range users {
|
|
for meta, id := range metaToId {
|
|
if u.ID == id {
|
|
userMap[meta] = u
|
|
break
|
|
}
|
|
}
|
|
for meta, uid := range metaToUid {
|
|
if u.UID == uid {
|
|
userMap[meta] = u
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return userMap, err
|
|
}
|
|
|
|
func (h *k8sHandler) getClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, error) {
|
|
cfg, err := h.restConfig(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dyn, err := dynamic.NewForConfig(cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create dynamic client: %w", err)
|
|
}
|
|
|
|
return dyn.Resource(h.gvr).Namespace(h.GetNamespace(orgID)), nil
|
|
}
|