Perf: Disable core kind registry (#78568)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Todd Treece 2023-11-28 03:09:54 -05:00 committed by GitHub
parent b022ddeee8
commit 529271d7a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 24 additions and 250 deletions

View File

@ -466,7 +466,6 @@ func (hs *HTTPServer) registerRoutes() {
})
dashboardRoute.Post("/calculate-diff", authorize(ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.CalculateDashboardDiff))
dashboardRoute.Post("/validate", authorize(ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.ValidateDashboard))
dashboardRoute.Post("/db", authorize(ac.EvalAny(ac.EvalPermission(dashboards.ActionDashboardsCreate), ac.EvalPermission(dashboards.ActionDashboardsWrite))), routing.Wrap(hs.PostDashboard))
dashboardRoute.Get("/home", routing.Wrap(hs.GetHomeDashboard))

View File

@ -17,7 +17,6 @@ import (
"github.com/grafana/grafana/pkg/components/dashdiffs"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/kinds/dashboard"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/auth/identity"
@ -792,72 +791,6 @@ func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.R
return response.JSON(http.StatusOK, dashVersionMeta)
}
// swagger:route POST /dashboards/validate dashboards alpha validateDashboard
//
// Validates a dashboard JSON against the schema.
//
// Produces:
// - application/json
//
// Responses:
// 200: validateDashboardResponse
// 412: validateDashboardResponse
// 422: validateDashboardResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) ValidateDashboard(c *contextmodel.ReqContext) response.Response {
cmd := dashboards.ValidateDashboardCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "Bad request data", err)
}
dk := hs.Kinds.Dashboard()
dashboardBytes := []byte(cmd.Dashboard)
// POST api receives dashboard as a string of json (so line numbers for errors stay consistent),
// but we need to parse the schema version out of it
dashboardJson, err := simplejson.NewJson(dashboardBytes)
if err != nil {
return response.Error(http.StatusBadRequest, "unable to parse dashboard", err)
}
schemaVersion, err := dashboardJson.Get("schemaVersion").Int()
isValid := false
statusCode := http.StatusOK
validationMessage := ""
// Only try to validate if the schemaVersion is at least the handoff version
// (the minimum schemaVersion against which the dashboard schema is known to
// work), or if schemaVersion is absent (which will happen once the Thema
// schema becomes canonical).
if err != nil || schemaVersion >= dashboard.HandoffSchemaVersion {
// Schemas expect the dashboard to live in the spec field
k8sResource := `{"spec": ` + cmd.Dashboard + "}"
_, _, validationErr := dk.JSONValueMux([]byte(k8sResource))
if validationErr == nil {
isValid = true
} else {
validationMessage = validationErr.Error()
statusCode = http.StatusUnprocessableEntity
}
} else {
validationMessage = "invalid schema version"
statusCode = http.StatusPreconditionFailed
}
respData := &ValidateDashboardResponse{
IsValid: isValid,
Message: validationMessage,
}
return response.JSON(statusCode, respData)
}
// swagger:route POST /dashboards/calculate-diff dashboards calculateDashboardDiff
//
// Perform diff on two dashboards.
@ -1303,9 +1236,3 @@ type DashboardVersionResponse struct {
// in: body
Body *dashver.DashboardVersionMeta `json:"body"`
}
// swagger:response validateDashboardResponse
type ValidateDashboardResponse struct {
IsValid bool `json:"isValid"`
Message string `json:"message,omitempty"`
}

View File

@ -24,7 +24,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/registry/corekind"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
@ -79,7 +78,6 @@ func TestGetHomeDashboard(t *testing.T) {
SQLStore: dbtest.NewFakeDB(),
preferenceService: prefService,
dashboardVersionService: dashboardVersionService,
Kinds: corekind.NewBase(nil),
log: log.New("test-logger"),
}
@ -515,60 +513,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
})
})
t.Run("Given a dashboard to validate", func(t *testing.T) {
sqlmock := dbtest.NewFakeDB()
t.Run("When an invalid dashboard json is posted", func(t *testing.T) {
cmd := dashboards.ValidateDashboardCommand{
Dashboard: "{\"hello\": \"world\"}",
}
role := org.RoleAdmin
postValidateScenario(t, "When calling POST on", "/api/dashboards/validate", "/api/dashboards/validate", cmd, role, func(sc *scenarioContext) {
callPostDashboard(sc)
result := sc.ToJSON()
assert.Equal(t, http.StatusUnprocessableEntity, sc.resp.Code)
assert.False(t, result.Get("isValid").MustBool())
assert.NotEmpty(t, result.Get("message").MustString())
}, sqlmock)
})
t.Run("When a dashboard with a too-low schema version is posted", func(t *testing.T) {
cmd := dashboards.ValidateDashboardCommand{
Dashboard: "{\"schemaVersion\": 1}",
}
role := org.RoleAdmin
postValidateScenario(t, "When calling POST on", "/api/dashboards/validate", "/api/dashboards/validate", cmd, role, func(sc *scenarioContext) {
callPostDashboard(sc)
result := sc.ToJSON()
assert.Equal(t, http.StatusPreconditionFailed, sc.resp.Code)
assert.False(t, result.Get("isValid").MustBool())
assert.Equal(t, "invalid schema version", result.Get("message").MustString())
}, sqlmock)
})
t.Run("When a valid dashboard is posted", func(t *testing.T) {
devenvDashboard, readErr := os.ReadFile("../../devenv/dev-dashboards/home.json")
assert.Empty(t, readErr)
cmd := dashboards.ValidateDashboardCommand{
Dashboard: string(devenvDashboard),
}
role := org.RoleAdmin
postValidateScenario(t, "When calling POST on", "/api/dashboards/validate", "/api/dashboards/validate", cmd, role, func(sc *scenarioContext) {
callPostDashboard(sc)
result := sc.ToJSON()
assert.Equal(t, http.StatusOK, sc.resp.Code)
assert.True(t, result.Get("isValid").MustBool())
}, sqlmock)
})
})
t.Run("Given two dashboards being compared", func(t *testing.T) {
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersionDTO{
@ -742,7 +686,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
AccessControl: accesscontrolmock.New(),
DashboardService: dashboardService,
Features: featuremgmt.WithFeatures(),
Kinds: corekind.NewBase(nil),
starService: startest.NewStarServiceFake(),
}
hs.callGetDashboard(sc)
@ -781,7 +724,6 @@ func TestDashboardVersionsAPIEndpoint(t *testing.T) {
Features: featuremgmt.WithFeatures(),
DashboardService: dashboardService,
dashboardVersionService: fakeDashboardVersionService,
Kinds: corekind.NewBase(nil),
QuotaService: quotatest.New(false, nil),
userService: userSvc,
CacheService: localcache.New(5*time.Minute, 10*time.Minute),
@ -922,7 +864,6 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
dashboardProvisioningService: dashboardProvisioningService,
DashboardService: dashboardService,
Features: featuremgmt.WithFeatures(),
Kinds: corekind.NewBase(nil),
starService: startest.NewStarServiceFake(),
}
@ -975,7 +916,6 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
DashboardService: dashboardService,
folderService: folderService,
Features: featuremgmt.WithFeatures(),
Kinds: corekind.NewBase(nil),
accesscontrolService: actest.FakeService{},
log: log.New("test-logger"),
}
@ -996,42 +936,6 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
})
}
func postValidateScenario(t *testing.T, desc string, url string, routePattern string, cmd dashboards.ValidateDashboardCommand,
role org.RoleType, fn scenarioFunc, sqlmock db.DB) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
cfg := setting.NewCfg()
hs := HTTPServer{
Cfg: cfg,
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
Live: newTestLive(t, db.InitTestDB(t)),
QuotaService: quotatest.New(false, nil),
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
SQLStore: sqlmock,
Features: featuremgmt.WithFeatures(),
Kinds: corekind.NewBase(nil),
}
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
c.Req.Header.Add("Content-Type", "application/json")
sc.context = c
sc.context.SignedInUser = &user.SignedInUser{
OrgID: testOrgID,
UserID: testUserID,
}
sc.context.OrgRole = role
return hs.ValidateDashboard(c)
})
sc.m.Post(routePattern, sc.defaultHandler)
fn(sc)
})
}
func postDiffScenario(t *testing.T, desc string, url string, routePattern string, cmd dtos.CalculateDiffOptions,
role org.RoleType, fn scenarioFunc, sqlmock db.DB, fakeDashboardVersionService *dashvertest.FakeDashboardVersionService) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
@ -1048,7 +952,6 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
SQLStore: sqlmock,
dashboardVersionService: fakeDashboardVersionService,
Features: featuremgmt.WithFeatures(),
Kinds: corekind.NewBase(nil),
DashboardService: dashSvc,
}
@ -1091,7 +994,6 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
SQLStore: sqlStore,
Features: featuremgmt.WithFeatures(),
dashboardVersionService: fakeDashboardVersionService,
Kinds: corekind.NewBase(nil),
accesscontrolService: actest.FakeService{},
folderService: folderSvc,
}

View File

@ -37,7 +37,6 @@ import (
"github.com/grafana/grafana/pkg/middleware/requestmeta"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
"github.com/grafana/grafana/pkg/registry/corekind"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/annotations"
@ -187,7 +186,6 @@ type HTTPServer struct {
dashboardVersionService dashver.Service
PublicDashboardsApi *publicdashboardsApi.Api
starService star.Service
Kinds *corekind.Base
playlistService playlist.Service
apiKeyService apikey.Service
kvStore kvstore.KVStore
@ -240,7 +238,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service,
folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
starService star.Service, csrfService csrf.Service, basekinds *corekind.Base,
starService star.Service, csrfService csrf.Service,
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore,
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
@ -329,7 +327,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
dashboardPermissionsService: dashboardPermissionsService,
dashboardVersionService: dashboardVersionService,
starService: starService,
Kinds: basekinds,
playlistService: playlistService,
apiKeyService: apiKeyService,
kvStore: kvStore,

View File

@ -6,6 +6,7 @@ package cuectx
import (
"path/filepath"
"sync"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
@ -26,14 +27,25 @@ var GoCoreKindParentPath = filepath.Join("pkg", "kinds")
// contains one directory per kind, full of generated TS kind output: types and default consts.
var TSCoreKindParentPath = filepath.Join("packages", "grafana-schema", "src", "raw")
var ctx = cuecontext.New()
var rt = thema.NewRuntime(ctx)
var (
ctx *cue.Context
rt *thema.Runtime
once sync.Once
)
func initContext() {
once.Do(func() {
ctx = cuecontext.New()
rt = thema.NewRuntime(ctx)
})
}
// GrafanaCUEContext returns Grafana's singleton instance of [cue.Context].
//
// All code within grafana/grafana that needs a *cue.Context should get it
// from this function, when one was not otherwise provided.
func GrafanaCUEContext() *cue.Context {
initContext()
return ctx
}
@ -42,6 +54,7 @@ func GrafanaCUEContext() *cue.Context {
// All code within grafana/grafana that needs a *thema.Runtime should get it
// from this function, when one was not otherwise provided.
func GrafanaThemaRuntime() *thema.Runtime {
initContext()
return rt
}
@ -56,5 +69,5 @@ func GrafanaThemaRuntime() *thema.Runtime {
// call it repeatedly. Most use cases should probably prefer making
// their own Thema/CUE decoders.
func JSONtoCUE(path string, b []byte) (cue.Value, error) {
return vmux.NewJSONCodec(path).Decode(ctx, b)
return vmux.NewJSONCodec(path).Decode(GrafanaCUEContext(), b)
}

View File

@ -3,18 +3,12 @@ package corekind
import (
"sync"
"github.com/google/wire"
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/grafana/pkg/cuectx"
)
// KindSet contains all of the wire-style providers related to kinds.
var KindSet = wire.NewSet(
NewBase,
)
var (
baseOnce sync.Once
defaultBase *Base

View File

@ -34,7 +34,6 @@ import (
"github.com/grafana/grafana/pkg/middleware/csrf"
"github.com/grafana/grafana/pkg/middleware/loggermw"
apiregistry "github.com/grafana/grafana/pkg/registry/apis"
"github.com/grafana/grafana/pkg/registry/corekind"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
@ -314,7 +313,6 @@ var wireBasicSet = wire.NewSet(
secretsStore.ProvideService,
avatar.ProvideAvatarCacheServer,
statscollector.ProvideService,
corekind.KindSet,
cuectx.GrafanaCUEContext,
cuectx.GrafanaThemaRuntime,
csrf.ProvideCSRFFilter,

View File

@ -260,15 +260,6 @@ type SaveDashboardCommand struct {
UpdatedAt time.Time
}
type ValidateDashboardCommand struct {
Dashboard string `json:"dashboard" binding:"Required"`
}
type TrimDashboardCommand struct {
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
Meta *simplejson.Json `json:"meta"`
}
type DashboardProvisioning struct {
ID int64 `xorm:"pk autoincr 'id'"`
DashboardID int64 `xorm:"dashboard_id"`

View File

@ -9158,17 +9158,6 @@
"$ref": "#/definitions/UserProfileDTO"
}
},
"validateDashboardResponse": {
"description": "",
"headers": {
"isValid": {
"type": "boolean"
},
"message": {
"type": "string"
}
}
},
"viewPublicDashboardResponse": {
"description": "",
"schema": {

View File

@ -22312,17 +22312,6 @@
"$ref": "#/definitions/UserProfileDTO"
}
},
"validateDashboardResponse": {
"description": "(empty)",
"headers": {
"isValid": {
"type": "boolean"
},
"message": {
"type": "string"
}
}
},
"viewPublicDashboardResponse": {
"description": "(empty)",
"schema": {

View File

@ -516,16 +516,13 @@ export class BackendSrv implements BackendService {
return this.get<DashboardDTO>(`/api/dashboards/uid/${uid}`);
}
validateDashboard(dashboard: DashboardModel) {
// We want to send the dashboard as a JSON string (in the JSON body payload) so we can get accurate error line numbers back
const dashboardJson = JSON.stringify(dashboard, replaceJsonNulls, 2);
return this.request<ValidateDashboardResponse>({
method: 'POST',
url: `/api/dashboards/validate`,
data: { dashboard: dashboardJson },
showSuccessAlert: false,
showErrorAlert: false,
validateDashboard(dashboard: DashboardModel): Promise<ValidateDashboardResponse> {
// support for this function will be implemented in the k8s flavored api-server
// hidden by experimental feature flag:
// config.featureToggles.showDashboardValidationWarnings
return Promise.resolve({
isValid: false,
message: 'dashboard validation is supported',
});
}
@ -553,10 +550,3 @@ interface ValidateDashboardResponse {
isValid: boolean;
message?: string;
}
function replaceJsonNulls<T extends unknown>(key: string, value: T): T | undefined {
if (typeof value === 'number' && !Number.isFinite(value)) {
return undefined;
}
return value;
}

View File

@ -1911,21 +1911,6 @@
},
"description": "(empty)"
},
"validateDashboardResponse": {
"description": "(empty)",
"headers": {
"isValid": {
"schema": {
"type": "boolean"
}
},
"message": {
"schema": {
"type": "string"
}
}
}
},
"viewPublicDashboardResponse": {
"content": {
"application/json": {