K8s/Dashboard: Read DTO from the registered storage, not just legacy (#93406)

This commit is contained in:
Ryan McKinley 2024-09-19 12:45:32 +03:00 committed by GitHub
parent b48f288583
commit 1e3816a6f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 42 deletions

View File

@ -41,6 +41,7 @@ type DashboardsAPIBuilder struct {
accessControl accesscontrol.AccessControl
legacy *dashboardStorage
unified resource.ResourceClient
log log.Logger
}
@ -68,6 +69,7 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
dashboardService: dashboardService,
accessControl: accessControl,
unified: unified,
legacy: &dashboardStorage{
resource: dashboard.DashboardResourceInfo,
@ -138,9 +140,6 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo(
storage := map[string]rest.Storage{}
storage[dash.StoragePath()] = legacyStore
storage[dash.StoragePath("dto")] = &DTOConnector{
builder: b,
}
storage[dash.StoragePath("history")] = apistore.NewHistoryConnector(
b.legacy.server, // as client???
dashboard.DashboardResourceInfo.GroupResource(),
@ -163,6 +162,12 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo(
}
}
// Register the DTO endpoint that will consolidate all dashboard bits
storage[dash.StoragePath("dto")], err = newDTOConnector(storage[dash.StoragePath()], b)
if err != nil {
return nil, err
}
// Expose read only library panels
storage[dashboard.LibraryPanelResourceInfo.StoragePath()] = &libraryPanelStore{
access: b.legacy.access,

View File

@ -1,12 +1,11 @@
package dashboard
import (
"k8s.io/apimachinery/pkg/runtime"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
dashboard "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"
"k8s.io/apimachinery/pkg/runtime"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
)
var _ grafanarest.Storage = (*storage)(nil)

View File

@ -2,28 +2,49 @@ package dashboard
import (
"context"
"encoding/json"
"fmt"
"net/http"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
"strconv"
"github.com/grafana/authlib/claims"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/slugify"
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/storage/unified/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
)
// The DTO returns everything the UI needs in a single request
type DTOConnector struct {
builder *DashboardsAPIBuilder
getter rest.Getter
legacy legacy.DashboardAccess
unified resource.ResourceClient
accessControl accesscontrol.AccessControl
log log.Logger
}
func newDTOConnector(dash rest.Storage, builder *DashboardsAPIBuilder) (rest.Storage, error) {
ok := false
v := &DTOConnector{
legacy: builder.legacy.access,
accessControl: builder.accessControl,
unified: builder.unified,
log: builder.log,
}
v.getter, ok = dash.(rest.Getter)
if !ok {
return nil, fmt.Errorf("dashboard storage must implement getter")
}
return v, nil
}
var (
@ -65,14 +86,35 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob
return nil, err
}
dto, err := r.builder.dashboardService.GetDashboard(ctx, &dashboards.GetDashboardQuery{
UID: name,
OrgID: info.OrgID,
})
rawobj, err := r.getter.Get(ctx, name, &v1.GetOptions{})
if err != nil {
return nil, err
}
dash, ok := rawobj.(*dashboard.Dashboard)
if !ok {
return nil, fmt.Errorf("expecting dashboard, not %t", rawobj)
}
obj, err := utils.MetaAccessor(dash)
if err != nil {
return nil, err
}
dto := &dashboards.Dashboard{
UID: name,
OrgID: info.OrgID,
}
origin, err := obj.GetOriginInfo()
if err != nil {
return nil, err
}
if origin != nil && origin.Name == "SQL" {
dto.ID, err = strconv.ParseInt(origin.Path, 10, 64)
if err == nil {
return nil, err
}
}
guardian, err := guardian.NewByDashboard(ctx, dto, info.OrgID, user)
if err != nil {
return nil, err
@ -93,27 +135,7 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob
r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Dashboard, accesscontrol.ScopeAnnotationsTypeDashboard)
r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Organization, accesscontrol.ScopeAnnotationsTypeOrganization)
key := &resource.ResourceKey{
Namespace: info.Value,
Group: dashboard.GROUP,
Resource: dashboard.DashboardResourceInfo.GroupResource().Resource,
Name: name,
}
store := r.builder.legacy.access
rsp := store.ReadResource(ctx, &resource.ReadRequest{Key: key})
if rsp.Error != nil {
return nil, resource.GetError(rsp.Error)
}
dash := &dashboard.Dashboard{}
err = json.Unmarshal(rsp.Value, dash)
if err != nil {
return nil, err
}
obj, err := utils.MetaAccessor(dash)
if err != nil {
return nil, err
}
// Check for blob info
blobInfo := obj.GetBlob()
if blobInfo != nil {
fmt.Printf("TODO, load full blob from storage %+v\n", blobInfo)
@ -134,20 +156,20 @@ func (r *DTOConnector) getAnnotationPermissionsByScope(ctx context.Context, user
var err error
evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope)
actions.CanAdd, err = r.builder.accessControl.Evaluate(ctx, user, evaluate)
actions.CanAdd, err = r.accessControl.Evaluate(ctx, user, evaluate)
if err != nil {
r.builder.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsCreate, "scope", scope)
r.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsCreate, "scope", scope)
}
evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsDelete, scope)
actions.CanDelete, err = r.builder.accessControl.Evaluate(ctx, user, evaluate)
actions.CanDelete, err = r.accessControl.Evaluate(ctx, user, evaluate)
if err != nil {
r.builder.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsDelete, "scope", scope)
r.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsDelete, "scope", scope)
}
evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsWrite, scope)
actions.CanEdit, err = r.builder.accessControl.Evaluate(ctx, user, evaluate)
actions.CanEdit, err = r.accessControl.Evaluate(ctx, user, evaluate)
if err != nil {
r.builder.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsWrite, "scope", scope)
r.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsWrite, "scope", scope)
}
}