Dashboard Versions: Make compatible with app platform (#99327)

This commit is contained in:
Stephanie Hingtgen
2025-01-28 08:17:52 -06:00
committed by GitHub
parent 05905a5069
commit 0cef2b9ae7
44 changed files with 887 additions and 413 deletions
+33 -4
View File
@@ -2,7 +2,10 @@ package client
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -16,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/services/apiserver"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/unified/resource"
k8sUser "k8s.io/apiserver/pkg/authentication/user"
@@ -24,7 +28,7 @@ import (
type K8sHandler interface {
GetNamespace(orgID int64) string
Get(ctx context.Context, name string, orgID int64, subresource ...string) (*unstructured.Unstructured, error)
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) (*unstructured.Unstructured, error)
Update(ctx context.Context, obj *unstructured.Unstructured, orgID int64) (*unstructured.Unstructured, error)
Delete(ctx context.Context, name string, orgID int64, options v1.DeleteOptions) error
@@ -32,6 +36,7 @@ type K8sHandler interface {
List(ctx context.Context, orgID int64, options v1.ListOptions) (*unstructured.UnstructuredList, error)
Search(ctx context.Context, orgID int64, in *resource.ResourceSearchRequest) (*resource.ResourceSearchResponse, error)
GetStats(ctx context.Context, orgID int64) (*resource.ResourceStatsResponse, error)
GetUserFromMeta(ctx context.Context, userMeta string) (*user.User, error)
}
var _ K8sHandler = (*k8sHandler)(nil)
@@ -41,9 +46,10 @@ type k8sHandler struct {
gvr schema.GroupVersionResource
restConfigProvider apiserver.RestConfigProvider
searcher resource.ResourceIndexClient
userService user.Service
}
func NewK8sHandler(cfg *setting.Cfg, namespacer request.NamespaceMapper, gvr schema.GroupVersionResource, restConfigProvider apiserver.RestConfigProvider, searcher resource.ResourceIndexClient, dashStore dashboards.Store) K8sHandler {
func NewK8sHandler(cfg *setting.Cfg, namespacer request.NamespaceMapper, gvr schema.GroupVersionResource, restConfigProvider apiserver.RestConfigProvider, searcher resource.ResourceIndexClient, dashStore dashboards.Store, userSvc user.Service) K8sHandler {
legacySearcher := legacysearcher.NewDashboardSearchClient(dashStore)
searchClient := resource.NewSearchClient(cfg, setting.UnifiedStorageConfigKeyDashboard, searcher, legacySearcher)
return &k8sHandler{
@@ -51,6 +57,7 @@ func NewK8sHandler(cfg *setting.Cfg, namespacer request.NamespaceMapper, gvr sch
gvr: gvr,
restConfigProvider: restConfigProvider,
searcher: searchClient,
userService: userSvc,
}
}
@@ -58,7 +65,7 @@ func (h *k8sHandler) GetNamespace(orgID int64) string {
return h.namespacer(orgID)
}
func (h *k8sHandler) Get(ctx context.Context, name string, orgID int64, subresource ...string) (*unstructured.Unstructured, error) {
func (h *k8sHandler) Get(ctx context.Context, name string, orgID int64, options v1.GetOptions, subresource ...string) (*unstructured.Unstructured, error) {
// create a new context - prevents issues when the request stems from the k8s api itself
// otherwise the context goes through the handlers twice and causes issues
newCtx, cancel, err := h.getK8sContext(ctx)
@@ -73,7 +80,7 @@ func (h *k8sHandler) Get(ctx context.Context, name string, orgID int64, subresou
return nil, nil
}
return client.Get(newCtx, name, v1.GetOptions{}, subresource...)
return client.Get(newCtx, name, options, subresource...)
}
func (h *k8sHandler) Create(ctx context.Context, obj *unstructured.Unstructured, orgID int64) (*unstructured.Unstructured, error) {
@@ -191,6 +198,28 @@ func (h *k8sHandler) GetStats(ctx context.Context, orgID int64) (*resource.Resou
})
}
// GetUserFromMeta takes what meta accessor gives you from `GetCreatedBy` or `GetUpdatedBy` and returns the user
func (h *k8sHandler) GetUserFromMeta(ctx context.Context, userMeta string) (*user.User, error) {
parts := strings.Split(userMeta, ":")
if len(parts) < 2 {
return &user.User{}, nil
}
meta := parts[1]
userId, err := strconv.ParseInt(meta, 10, 64)
var u *user.User
if err == nil {
u, err = h.userService.GetByID(ctx, &user.GetUserByIDQuery{ID: userId})
} else {
u, err = h.userService.GetByUID(ctx, &user.GetUserByUIDQuery{UID: meta})
}
if err != nil && errors.Is(err, user.ErrUserNotFound) {
return &user.User{}, nil
}
return u, err
}
func (h *k8sHandler) getClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) {
cfg := h.restConfigProvider.GetRestConfig(ctx)
if cfg == nil {
+11 -2
View File
@@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/storage/unified/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -21,8 +22,8 @@ func (m *MockK8sHandler) GetNamespace(orgID int64) string {
return args.String(0)
}
func (m *MockK8sHandler) Get(ctx context.Context, name string, orgID int64, subresource ...string) (*unstructured.Unstructured, error) {
args := m.Called(ctx, name, orgID, subresource)
func (m *MockK8sHandler) Get(ctx context.Context, name string, orgID int64, options v1.GetOptions, subresource ...string) (*unstructured.Unstructured, error) {
args := m.Called(ctx, name, orgID, options, subresource)
if args.Get(0) == nil {
return nil, args.Error(1)
}
@@ -79,3 +80,11 @@ func (m *MockK8sHandler) GetStats(ctx context.Context, orgID int64) (*resource.R
}
return args.Get(0).(*resource.ResourceStatsResponse), args.Error(1)
}
func (m *MockK8sHandler) GetUserFromMeta(ctx context.Context, userMeta string) (*user.User, error) {
args := m.Called(ctx, userMeta)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*user.User), args.Error(1)
}
@@ -0,0 +1,34 @@
package client
import (
"context"
"testing"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/stretchr/testify/require"
)
func TestGetUserFromMeta(t *testing.T) {
userSvcTest := usertest.NewUserServiceFake()
userSvcTest.ExpectedUser = &user.User{
ID: 1,
UID: "uid-value",
}
client := &k8sHandler{
userService: userSvcTest,
}
t.Run("returns user with valid UID", func(t *testing.T) {
result, err := client.GetUserFromMeta(context.Background(), "user:uid-value")
require.NoError(t, err)
require.Equal(t, "uid-value", result.UID)
require.Equal(t, int64(1), result.ID)
})
t.Run("returns user when id is passed in", func(t *testing.T) {
result, err := client.GetUserFromMeta(context.Background(), "user:1")
require.NoError(t, err)
require.Equal(t, "uid-value", result.UID)
require.Equal(t, int64(1), result.ID)
})
}