From 9b6edc5cd5e699a5164579291aba98111ae42c3a Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Wed, 16 Sep 2020 08:52:42 -0600 Subject: [PATCH] Allow legacy imports size respect `savedObjects.maxImportPayloadBytes` config (#77409) --- docs/api/dashboard/import-dashboard.asciidoc | 2 +- ...ana-plugin-core-server.sharedglobalconfig.md | 1 + src/core/server/mocks.ts | 4 ++++ src/core/server/plugins/plugin_context.test.ts | 2 ++ src/core/server/plugins/plugin_context.ts | 11 +++++++---- src/core/server/plugins/types.ts | 3 +++ src/core/server/server.api.md | 6 ++++-- src/plugins/legacy_export/server/plugin.ts | 17 +++++++++++------ .../legacy_export/server/routes/import.ts | 5 ++++- .../legacy_export/server/routes/index.ts | 8 ++++++-- 10 files changed, 43 insertions(+), 16 deletions(-) diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 020ec8018b85b..56bd4abbc8023 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -23,7 +23,7 @@ experimental[] Import dashboards and corresponding saved objects. [[dashboard-api-import-request-body]] ==== Request body -Use the complete response body from the <> as the request body. Do not manually construct a payload to the endpoint. +Use the complete response body from the <> as the request body. Do not manually construct a payload to the endpoint. The max payload size is determined by the `savedObjects.maxImportPayloadBytes` configuration key. [[dashboard-api-import-response-body]] ==== Response body diff --git a/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md b/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md index 7f306919101ef..ec2e1b227a2d7 100644 --- a/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md @@ -12,5 +12,6 @@ export declare type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; ``` diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 5d6bf41fec3f3..52dccb6880882 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -18,6 +18,7 @@ */ import { of } from 'rxjs'; import { duration } from 'moment'; +import { ByteSizeValue } from '@kbn/config-schema'; import { PluginInitializerContext, CoreSetup, CoreStart, StartServicesAccessor } from '.'; import { loggingSystemMock } from './logging/logging_system.mock'; import { loggingServiceMock } from './logging/logging_service.mock'; @@ -66,6 +67,9 @@ export function pluginInitializerContextConfigMock(config: T) { startupTimeout: duration('30s'), }, path: { data: '/tmp' }, + savedObjects: { + maxImportPayloadBytes: new ByteSizeValue(10485760), + }, }; const mock: jest.Mocked['config']> = { diff --git a/src/core/server/plugins/plugin_context.test.ts b/src/core/server/plugins/plugin_context.test.ts index 5ce91c9a623dc..cb4e8f20be982 100644 --- a/src/core/server/plugins/plugin_context.test.ts +++ b/src/core/server/plugins/plugin_context.test.ts @@ -28,6 +28,7 @@ import { rawConfigServiceMock, getEnvOptions } from '../config/mocks'; import { PluginManifest } from './types'; import { Server } from '../server'; import { fromRoot } from '../utils'; +import { ByteSizeValue } from '@kbn/config-schema'; const logger = loggingSystemMock.create(); @@ -93,6 +94,7 @@ describe('createPluginInitializerContext', () => { startupTimeout: duration(5, 's'), }, path: { data: fromRoot('data') }, + savedObjects: { maxImportPayloadBytes: new ByteSizeValue(10485760) }, }); }); diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 8d17300965680..6529e83b4d818 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -35,6 +35,7 @@ import { ElasticsearchConfigType, config as elasticsearchConfig, } from '../elasticsearch/elasticsearch_config'; +import { SavedObjectsConfigType, savedObjectsConfig } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; export interface InstanceInfo { @@ -91,16 +92,18 @@ export function createPluginInitializerContext( * Note: naming not final here, it will be renamed in a near future (https://github.com/elastic/kibana/issues/46240) * @deprecated */ - globalConfig$: combineLatest( + globalConfig$: combineLatest([ coreContext.configService.atPath(kibanaConfig.path), coreContext.configService.atPath(elasticsearchConfig.path), - coreContext.configService.atPath(pathConfig.path) - ).pipe( - map(([kibana, elasticsearch, path]) => + coreContext.configService.atPath(pathConfig.path), + coreContext.configService.atPath(savedObjectsConfig.path), + ]).pipe( + map(([kibana, elasticsearch, path, savedObjects]) => deepFreeze({ kibana: pick(kibana, SharedGlobalConfigKeys.kibana), elasticsearch: pick(elasticsearch, SharedGlobalConfigKeys.elasticsearch), path: pick(path, SharedGlobalConfigKeys.path), + savedObjects: pick(savedObjects, SharedGlobalConfigKeys.savedObjects), }) ) ), diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index 34d5e044222eb..9de181124a349 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -26,6 +26,7 @@ import { ConfigPath, EnvironmentMode, PackageInfo, ConfigDeprecationProvider } f import { LoggerFactory } from '../logging'; import { KibanaConfigType } from '../kibana_config'; import { ElasticsearchConfigType } from '../elasticsearch/elasticsearch_config'; +import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; /** @@ -263,6 +264,7 @@ export const SharedGlobalConfigKeys = { kibana: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const, elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout', 'startupTimeout'] as const, path: ['data'] as const, + savedObjects: ['maxImportPayloadBytes'] as const, }; /** @@ -272,6 +274,7 @@ export type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index ab26f29dce3af..ef5eb55632ed1 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2661,6 +2661,7 @@ export type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; // @public @@ -2745,7 +2746,8 @@ export const validBodyOutput: readonly ["data", "stream"]; // // src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:135:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:272:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:272:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:277:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts ``` diff --git a/src/plugins/legacy_export/server/plugin.ts b/src/plugins/legacy_export/server/plugin.ts index 22c7c1a05dddb..9e1e631903240 100644 --- a/src/plugins/legacy_export/server/plugin.ts +++ b/src/plugins/legacy_export/server/plugin.ts @@ -18,18 +18,23 @@ */ import { Plugin, CoreSetup, PluginInitializerContext } from 'kibana/server'; +import { first } from 'rxjs/operators'; import { registerRoutes } from './routes'; export class LegacyExportPlugin implements Plugin<{}, {}> { - private readonly kibanaVersion: string; + constructor(private readonly initContext: PluginInitializerContext) {} - constructor(context: PluginInitializerContext) { - this.kibanaVersion = context.env.packageInfo.version; - } + public async setup({ http }: CoreSetup) { + const globalConfig = await this.initContext.config.legacy.globalConfig$ + .pipe(first()) + .toPromise(); - public setup({ http }: CoreSetup) { const router = http.createRouter(); - registerRoutes(router, this.kibanaVersion); + registerRoutes( + router, + this.initContext.env.packageInfo.version, + globalConfig.savedObjects.maxImportPayloadBytes.getValueInBytes() + ); return {}; } diff --git a/src/plugins/legacy_export/server/routes/import.ts b/src/plugins/legacy_export/server/routes/import.ts index cf6f28683be17..8d33983ad7e3c 100644 --- a/src/plugins/legacy_export/server/routes/import.ts +++ b/src/plugins/legacy_export/server/routes/import.ts @@ -21,7 +21,7 @@ import { schema } from '@kbn/config-schema'; import { IRouter, SavedObject } from 'src/core/server'; import { importDashboards } from '../lib'; -export const registerImportRoute = (router: IRouter) => { +export const registerImportRoute = (router: IRouter, maxImportPayloadBytes: number) => { router.post( { path: '/api/kibana/dashboards/import', @@ -39,6 +39,9 @@ export const registerImportRoute = (router: IRouter) => { }, options: { tags: ['api'], + body: { + maxBytes: maxImportPayloadBytes, + }, }, }, async (ctx, req, res) => { diff --git a/src/plugins/legacy_export/server/routes/index.ts b/src/plugins/legacy_export/server/routes/index.ts index 7b9de7f016b6b..cac405ce9bdf9 100644 --- a/src/plugins/legacy_export/server/routes/index.ts +++ b/src/plugins/legacy_export/server/routes/index.ts @@ -21,7 +21,11 @@ import { IRouter } from 'src/core/server'; import { registerImportRoute } from './import'; import { registerExportRoute } from './export'; -export const registerRoutes = (router: IRouter, kibanaVersion: string) => { +export const registerRoutes = ( + router: IRouter, + kibanaVersion: string, + maxImportPayloadBytes: number +) => { registerExportRoute(router, kibanaVersion); - registerImportRoute(router); + registerImportRoute(router, maxImportPayloadBytes); };