mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Upgrade Assistant] Move out of legacy folder (#58034)
* Create x-pack/plugins skeleton for upgrade assistant * Move public folder contents Move the public folder of Upgrade Assistant and migrate public to use HttpSetup (remove axios) * Include stylesheets in public * Move server side out of legacy Begin migration of Reindex worker to new platform Move imports around so that it satsifies new platform constraints like not importing server side code (even types) in client. * Updated the routes with new dependencies and removed server shim * Fix router unit tests * Fix server lib tests After changing function signatures for the reindex server factory (and others) all of the tests needed to be revisited and brought in line with the new APIs. Also used core/server mocks where appropriate * Clean up types issues * Fix setting credentials on request header * Removed the security plugin from Upgrade Assistant The security plugin is a potential future consumer of the Upgrade Assistant's deprecation feature and we would therefore not want to create a circular depedency here. We pull in the licensing plugin rather (as it is less likely that we will depend on that) and use it to determine whether security is available and enabled. * Migrate to config to new platform config xpack.upgrade_assistant.enabled * Remove unused types * Fix import issue * Move upgrade assistant back to Elasticsearch management section * Update dotfiles Added elasticsearch ui team as upgrade assistant code owner Updated i18nrc.json path * Alphabetical ordering in xpack/i18nrc.json * Implemented PR feedback Renamed callCluster -> callAsUser to be more consistent with platform naming. Added comment about why we are not using security plugin directly inside of Upgrade Assistant. Fixed long path imports and use 'src/core/..' throughout. Fixed import ordering. Renamed variables inside of telemetry lib. * Revert to longer import path In plugin.ts importing from 'kibana/server' or 'src/core/server' results in a module not found error. Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
d339740c2d
commit
899270a108
129 changed files with 1233 additions and 1342 deletions
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -179,6 +179,8 @@
|
|||
/x-pack/legacy/plugins/rollup/ @elastic/es-ui
|
||||
/x-pack/plugins/searchprofiler/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui
|
||||
/x-pack/legacy/plugins/upgrade_assistant/ @elastic/es-ui
|
||||
/x-pack/plugins/upgrade_assistant/ @elastic/es-ui
|
||||
/x-pack/plugins/watcher/ @elastic/es-ui
|
||||
|
||||
# Endpoint
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
"xpack.taskManager": "legacy/plugins/task_manager",
|
||||
"xpack.transform": "legacy/plugins/transform",
|
||||
"xpack.triggersActionsUI": "plugins/triggers_actions_ui",
|
||||
"xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant",
|
||||
"xpack.upgradeAssistant": "plugins/upgrade_assistant",
|
||||
"xpack.uptime": "legacy/plugins/uptime",
|
||||
"xpack.watcher": "plugins/watcher"
|
||||
},
|
||||
|
|
|
@ -3,25 +3,14 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import _ from 'lodash';
|
||||
import Joi from 'joi';
|
||||
import { Legacy } from 'kibana';
|
||||
import { resolve } from 'path';
|
||||
import mappings from './mappings.json';
|
||||
import { plugin } from './server/np_ready';
|
||||
import { CloudSetup } from '../../../plugins/cloud/server';
|
||||
|
||||
export function upgradeAssistant(kibana: any) {
|
||||
const publicSrc = resolve(__dirname, 'public');
|
||||
const npSrc = resolve(publicSrc, 'np_ready');
|
||||
|
||||
const config: Legacy.PluginSpecOptions = {
|
||||
id: 'upgrade_assistant',
|
||||
configPrefix: 'xpack.upgrade_assistant',
|
||||
require: ['elasticsearch'],
|
||||
uiExports: {
|
||||
// @ts-ignore
|
||||
managementSections: ['plugins/upgrade_assistant'],
|
||||
savedObjectSchemas: {
|
||||
'upgrade-assistant-reindex-operation': {
|
||||
isNamespaceAgnostic: true,
|
||||
|
@ -30,41 +19,10 @@ export function upgradeAssistant(kibana: any) {
|
|||
isNamespaceAgnostic: true,
|
||||
},
|
||||
},
|
||||
styleSheetPaths: resolve(npSrc, 'application/index.scss'),
|
||||
mappings,
|
||||
},
|
||||
publicDir: publicSrc,
|
||||
|
||||
config() {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
}).default();
|
||||
},
|
||||
|
||||
init(server: Legacy.Server) {
|
||||
// Add server routes and initialize the plugin here
|
||||
const instance = plugin({} as any);
|
||||
|
||||
const { usageCollection, cloud } = server.newPlatform.setup.plugins;
|
||||
instance.setup(server.newPlatform.setup.core, {
|
||||
usageCollection,
|
||||
cloud: cloud as CloudSetup,
|
||||
__LEGACY: {
|
||||
// Legacy objects
|
||||
events: server.events,
|
||||
savedObjects: server.savedObjects,
|
||||
|
||||
// Legacy functions
|
||||
log: server.log.bind(server),
|
||||
|
||||
// Legacy plugins
|
||||
plugins: {
|
||||
elasticsearch: server.plugins.elasticsearch,
|
||||
xpack_main: server.plugins.xpack_main,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
init() {},
|
||||
};
|
||||
return new kibana.Plugin(config);
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './legacy';
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
/* LEGACY IMPORTS */
|
||||
import { npSetup } from 'ui/new_platform';
|
||||
import { wrapInI18nContext } from 'ui/i18n';
|
||||
import { management } from 'ui/management';
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
import routes from 'ui/routes';
|
||||
import chrome from 'ui/chrome';
|
||||
/* LEGACY IMPORTS */
|
||||
|
||||
import { NEXT_MAJOR_VERSION } from '../common/version';
|
||||
import { plugin } from './np_ready';
|
||||
import { CloudSetup } from '../../../../plugins/cloud/public';
|
||||
|
||||
const BASE_PATH = `/management/elasticsearch/upgrade_assistant`;
|
||||
|
||||
export interface LegacyAppMountParameters {
|
||||
__LEGACY: { renderToElement: (RootComponent: ComponentType<any>) => void };
|
||||
}
|
||||
|
||||
export interface LegacyApp {
|
||||
mount(ctx: any, params: LegacyAppMountParameters): void;
|
||||
}
|
||||
|
||||
export interface LegacyManagementPlugin {
|
||||
sections: {
|
||||
get(
|
||||
name: string
|
||||
): {
|
||||
registerApp(app: LegacyApp): void;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Based on /rfcs/text/0006_management_section_service.md
|
||||
export interface LegacyPlugins {
|
||||
cloud?: CloudSetup;
|
||||
management: LegacyManagementPlugin;
|
||||
__LEGACY: {
|
||||
XSRF: string;
|
||||
};
|
||||
}
|
||||
|
||||
function startApp() {
|
||||
routes.when(`${BASE_PATH}/:view?`, {
|
||||
template:
|
||||
'<kbn-management-app section="elasticsearch/upgrade_assistant"><upgrade-assistant /></kbn-management-app>',
|
||||
});
|
||||
const { cloud } = npSetup.plugins as any;
|
||||
const legacyPluginsShim: LegacyPlugins = {
|
||||
cloud: cloud as CloudSetup,
|
||||
__LEGACY: {
|
||||
XSRF: chrome.getXsrfToken(),
|
||||
},
|
||||
management: {
|
||||
sections: {
|
||||
get(_: string) {
|
||||
return {
|
||||
registerApp(app) {
|
||||
management.getSection('elasticsearch').register('upgrade_assistant', {
|
||||
visible: true,
|
||||
display: i18n.translate('xpack.upgradeAssistant.appTitle', {
|
||||
defaultMessage: '{version} Upgrade Assistant',
|
||||
values: { version: `${NEXT_MAJOR_VERSION}.0` },
|
||||
}),
|
||||
order: 100,
|
||||
url: `#${BASE_PATH}`,
|
||||
});
|
||||
|
||||
app.mount(
|
||||
{},
|
||||
{
|
||||
__LEGACY: {
|
||||
// While there is not an NP API for registering management section apps yet
|
||||
renderToElement: RootComponent => {
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.directive('upgradeAssistant', (reactDirective: any) => {
|
||||
return reactDirective(wrapInI18nContext(RootComponent));
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const pluginInstance = plugin();
|
||||
|
||||
pluginInstance.setup(npSetup.core, legacyPluginsShim);
|
||||
}
|
||||
|
||||
startApp();
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { NEXT_MAJOR_VERSION } from '../../../common/version';
|
||||
import { UpgradeAssistantTabs } from './components/tabs';
|
||||
import { AppContextProvider, ContextValue, AppContext } from './app_context';
|
||||
|
||||
type AppDependencies = ContextValue;
|
||||
|
||||
export const RootComponent = (deps: AppDependencies) => {
|
||||
return (
|
||||
<AppContextProvider value={deps}>
|
||||
<div data-test-subj="upgradeAssistantRoot">
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.appTitle"
|
||||
defaultMessage="{version} Upgrade Assistant"
|
||||
values={{ version: `${NEXT_MAJOR_VERSION}.0` }}
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<AppContext.Consumer>
|
||||
{({ http }) => <UpgradeAssistantTabs http={http} />}
|
||||
</AppContext.Consumer>
|
||||
</div>
|
||||
</AppContextProvider>
|
||||
);
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../../common/types';
|
||||
|
||||
export const mockClient = {
|
||||
post: jest.fn().mockResolvedValue({
|
||||
lastCompletedStep: ReindexStep.created,
|
||||
status: ReindexStatus.inProgress,
|
||||
}),
|
||||
get: jest.fn().mockResolvedValue({
|
||||
status: 200,
|
||||
data: {
|
||||
warnings: [],
|
||||
reindexOp: null,
|
||||
},
|
||||
}),
|
||||
};
|
||||
jest.mock('axios', () => ({
|
||||
create: jest.fn().mockReturnValue(mockClient),
|
||||
}));
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Plugin, CoreSetup } from 'src/core/public';
|
||||
import { RootComponent } from './application/app';
|
||||
import { LegacyPlugins } from '../legacy';
|
||||
|
||||
export class UpgradeAssistantUIPlugin implements Plugin {
|
||||
async setup({ http }: CoreSetup, { cloud, management, __LEGACY: { XSRF } }: LegacyPlugins) {
|
||||
const appRegistrar = management.sections.get('kibana');
|
||||
const isCloudEnabled = !!(cloud && cloud.isCloudEnabled);
|
||||
|
||||
return appRegistrar.registerApp({
|
||||
mount(__, { __LEGACY: { renderToElement } }) {
|
||||
return renderToElement(() => RootComponent({ http, XSRF, isCloudEnabled }));
|
||||
},
|
||||
});
|
||||
}
|
||||
async start() {}
|
||||
async stop() {}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { plugin } from './np_ready';
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
UIOpen,
|
||||
UIOpenOption,
|
||||
UPGRADE_ASSISTANT_DOC_ID,
|
||||
UPGRADE_ASSISTANT_TYPE,
|
||||
} from '../../../../common/types';
|
||||
import { RequestShim, ServerShim } from '../../types';
|
||||
|
||||
async function incrementUIOpenOptionCounter(server: ServerShim, uiOpenOptionCounter: UIOpenOption) {
|
||||
const { getSavedObjectsRepository } = server.savedObjects;
|
||||
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
|
||||
const internalRepository = getSavedObjectsRepository(callWithInternalUser);
|
||||
|
||||
await internalRepository.incrementCounter(
|
||||
UPGRADE_ASSISTANT_TYPE,
|
||||
UPGRADE_ASSISTANT_DOC_ID,
|
||||
`ui_open.${uiOpenOptionCounter}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function upsertUIOpenOption(server: ServerShim, req: RequestShim): Promise<UIOpen> {
|
||||
const { overview, cluster, indices } = req.payload as UIOpen;
|
||||
|
||||
if (overview) {
|
||||
await incrementUIOpenOptionCounter(server, 'overview');
|
||||
}
|
||||
|
||||
if (cluster) {
|
||||
await incrementUIOpenOptionCounter(server, 'cluster');
|
||||
}
|
||||
|
||||
if (indices) {
|
||||
await incrementUIOpenOptionCounter(server, 'indices');
|
||||
}
|
||||
|
||||
return {
|
||||
overview,
|
||||
cluster,
|
||||
indices,
|
||||
};
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
UIReindex,
|
||||
UIReindexOption,
|
||||
UPGRADE_ASSISTANT_DOC_ID,
|
||||
UPGRADE_ASSISTANT_TYPE,
|
||||
} from '../../../../common/types';
|
||||
import { RequestShim, ServerShim } from '../../types';
|
||||
|
||||
async function incrementUIReindexOptionCounter(
|
||||
server: ServerShim,
|
||||
uiOpenOptionCounter: UIReindexOption
|
||||
) {
|
||||
const { getSavedObjectsRepository } = server.savedObjects;
|
||||
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
|
||||
const internalRepository = getSavedObjectsRepository(callWithInternalUser);
|
||||
|
||||
await internalRepository.incrementCounter(
|
||||
UPGRADE_ASSISTANT_TYPE,
|
||||
UPGRADE_ASSISTANT_DOC_ID,
|
||||
`ui_reindex.${uiOpenOptionCounter}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function upsertUIReindexOption(
|
||||
server: ServerShim,
|
||||
req: RequestShim
|
||||
): Promise<UIReindex> {
|
||||
const { close, open, start, stop } = req.payload as UIReindex;
|
||||
|
||||
if (close) {
|
||||
await incrementUIReindexOptionCounter(server, 'close');
|
||||
}
|
||||
|
||||
if (open) {
|
||||
await incrementUIReindexOptionCounter(server, 'open');
|
||||
}
|
||||
|
||||
if (start) {
|
||||
await incrementUIReindexOptionCounter(server, 'start');
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
await incrementUIReindexOptionCounter(server, 'stop');
|
||||
}
|
||||
|
||||
return {
|
||||
close,
|
||||
open,
|
||||
start,
|
||||
stop,
|
||||
};
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { Plugin, CoreSetup, CoreStart } from 'src/core/server';
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { ServerShim, ServerShimWithRouter } from './types';
|
||||
import { credentialStoreFactory } from './lib/reindexing/credential_store';
|
||||
import { registerUpgradeAssistantUsageCollector } from './lib/telemetry';
|
||||
import { registerClusterCheckupRoutes } from './routes/cluster_checkup';
|
||||
import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging';
|
||||
import { registerReindexIndicesRoutes, registerReindexWorker } from './routes/reindex_indices';
|
||||
import { CloudSetup } from '../../../../../plugins/cloud/server';
|
||||
import { registerTelemetryRoutes } from './routes/telemetry';
|
||||
|
||||
interface PluginsSetup {
|
||||
__LEGACY: ServerShim;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
cloud?: CloudSetup;
|
||||
}
|
||||
|
||||
export class UpgradeAssistantServerPlugin implements Plugin {
|
||||
setup({ http }: CoreSetup, { __LEGACY, usageCollection, cloud }: PluginsSetup) {
|
||||
const router = http.createRouter();
|
||||
const shimWithRouter: ServerShimWithRouter = { ...__LEGACY, router };
|
||||
registerClusterCheckupRoutes(shimWithRouter, { cloud });
|
||||
registerDeprecationLoggingRoutes(shimWithRouter);
|
||||
|
||||
// The ReindexWorker uses a map of request headers that contain the authentication credentials
|
||||
// for a given reindex. We cannot currently store these in an the .kibana index b/c we do not
|
||||
// want to expose these credentials to any unauthenticated users. We also want to avoid any need
|
||||
// to add a user for a special index just for upgrading. This in-memory cache allows us to
|
||||
// process jobs without the browser staying on the page, but will require that jobs go into
|
||||
// a paused state if no Kibana nodes have the required credentials.
|
||||
const credentialStore = credentialStoreFactory();
|
||||
|
||||
const worker = registerReindexWorker(__LEGACY, credentialStore);
|
||||
registerReindexIndicesRoutes(shimWithRouter, worker, credentialStore);
|
||||
|
||||
// Bootstrap the needed routes and the collector for the telemetry
|
||||
registerTelemetryRoutes(shimWithRouter);
|
||||
registerUpgradeAssistantUsageCollector(usageCollection, __LEGACY);
|
||||
}
|
||||
|
||||
start(core: CoreStart, plugins: any) {}
|
||||
|
||||
stop(): void {}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { ServerShimWithRouter } from '../types';
|
||||
import { getUpgradeAssistantStatus } from '../lib/es_migration_apis';
|
||||
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
|
||||
import { CloudSetup } from '../../../../../../plugins/cloud/server';
|
||||
import { createRequestShim } from './create_request_shim';
|
||||
|
||||
interface PluginsSetup {
|
||||
cloud?: CloudSetup;
|
||||
}
|
||||
|
||||
export function registerClusterCheckupRoutes(
|
||||
server: ServerShimWithRouter,
|
||||
pluginsSetup: PluginsSetup
|
||||
) {
|
||||
const { cloud } = pluginsSetup;
|
||||
const isCloudEnabled = !!(cloud && cloud.isCloudEnabled);
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
|
||||
|
||||
server.router.get(
|
||||
{
|
||||
path: '/api/upgrade_assistant/status',
|
||||
validate: false,
|
||||
},
|
||||
versionCheckHandlerWrapper(async (ctx, request, response) => {
|
||||
const reqShim = createRequestShim(request);
|
||||
try {
|
||||
return response.ok({
|
||||
body: await getUpgradeAssistantStatus(callWithRequest, reqShim, isCloudEnabled),
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.status === 403) {
|
||||
return response.forbidden(e.message);
|
||||
}
|
||||
|
||||
return response.internalError({ body: e });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { KibanaRequest } from 'kibana/server';
|
||||
import { RequestShim } from '../types';
|
||||
|
||||
export const createRequestShim = (req: KibanaRequest): RequestShim => {
|
||||
return {
|
||||
headers: req.headers as Record<string, string>,
|
||||
payload: req.body || (req as any).payload,
|
||||
params: req.params,
|
||||
};
|
||||
};
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import {
|
||||
getDeprecationLoggingStatus,
|
||||
setDeprecationLogging,
|
||||
} from '../lib/es_deprecation_logging_apis';
|
||||
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
|
||||
import { ServerShimWithRouter } from '../types';
|
||||
import { createRequestShim } from './create_request_shim';
|
||||
|
||||
export function registerDeprecationLoggingRoutes(server: ServerShimWithRouter) {
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
|
||||
|
||||
server.router.get(
|
||||
{
|
||||
path: '/api/upgrade_assistant/deprecation_logging',
|
||||
validate: false,
|
||||
},
|
||||
versionCheckHandlerWrapper(async (ctx, request, response) => {
|
||||
const reqShim = createRequestShim(request);
|
||||
try {
|
||||
const result = await getDeprecationLoggingStatus(callWithRequest, reqShim);
|
||||
return response.ok({ body: result });
|
||||
} catch (e) {
|
||||
return response.internalError({ body: e });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
server.router.put(
|
||||
{
|
||||
path: '/api/upgrade_assistant/deprecation_logging',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
isEnabled: schema.boolean(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
versionCheckHandlerWrapper(async (ctx, request, response) => {
|
||||
const reqShim = createRequestShim(request);
|
||||
try {
|
||||
const { isEnabled } = reqShim.payload as { isEnabled: boolean };
|
||||
return response.ok({
|
||||
body: await setDeprecationLogging(callWithRequest, reqShim, isEnabled),
|
||||
});
|
||||
} catch (e) {
|
||||
return response.internalError({ body: e });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { CallCluster } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { SavedObjectsClientContract } from 'kibana/server';
|
||||
import { ReindexStatus } from '../../../common/types';
|
||||
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
|
||||
import { reindexServiceFactory, ReindexWorker } from '../lib/reindexing';
|
||||
import { CredentialStore } from '../lib/reindexing/credential_store';
|
||||
import { reindexActionsFactory } from '../lib/reindexing/reindex_actions';
|
||||
import { ServerShim, ServerShimWithRouter } from '../types';
|
||||
import { createRequestShim } from './create_request_shim';
|
||||
|
||||
export function registerReindexWorker(server: ServerShim, credentialStore: CredentialStore) {
|
||||
const { callWithRequest, callWithInternalUser } = server.plugins.elasticsearch.getCluster(
|
||||
'admin'
|
||||
);
|
||||
const xpackInfo = server.plugins.xpack_main.info;
|
||||
const savedObjectsRepository = server.savedObjects.getSavedObjectsRepository(
|
||||
callWithInternalUser
|
||||
);
|
||||
const savedObjectsClient = new server.savedObjects.SavedObjectsClient(
|
||||
savedObjectsRepository
|
||||
) as SavedObjectsClientContract;
|
||||
|
||||
// Cannot pass server.log directly because it's value changes during startup (?).
|
||||
// Use this function to proxy through.
|
||||
const log = (tags: string | string[], data?: string | object | (() => any), timestamp?: number) =>
|
||||
server.log(tags, data, timestamp);
|
||||
|
||||
const worker = new ReindexWorker(
|
||||
savedObjectsClient,
|
||||
credentialStore,
|
||||
callWithRequest,
|
||||
callWithInternalUser,
|
||||
xpackInfo,
|
||||
log
|
||||
);
|
||||
|
||||
// Wait for ES connection before starting the polling loop.
|
||||
server.plugins.elasticsearch.waitUntilReady().then(() => {
|
||||
worker.start();
|
||||
server.events.on('stop', () => worker.stop());
|
||||
});
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
export function registerReindexIndicesRoutes(
|
||||
server: ServerShimWithRouter,
|
||||
worker: ReindexWorker,
|
||||
credentialStore: CredentialStore
|
||||
) {
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
|
||||
const xpackInfo = server.plugins.xpack_main.info;
|
||||
const BASE_PATH = '/api/upgrade_assistant/reindex';
|
||||
|
||||
// Start reindex for an index
|
||||
server.router.post(
|
||||
{
|
||||
path: `${BASE_PATH}/{indexName}`,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
versionCheckHandlerWrapper(async (ctx, request, response) => {
|
||||
const reqShim = createRequestShim(request);
|
||||
const { indexName } = reqShim.params;
|
||||
const { client } = ctx.core.savedObjects;
|
||||
const callCluster = callWithRequest.bind(null, reqShim) as CallCluster;
|
||||
const reindexActions = reindexActionsFactory(client, callCluster);
|
||||
const reindexService = reindexServiceFactory(
|
||||
callCluster,
|
||||
xpackInfo,
|
||||
reindexActions,
|
||||
server.log
|
||||
);
|
||||
|
||||
try {
|
||||
if (!(await reindexService.hasRequiredPrivileges(indexName))) {
|
||||
return response.forbidden({
|
||||
body: `You do not have adequate privileges to reindex this index.`,
|
||||
});
|
||||
}
|
||||
|
||||
const existingOp = await reindexService.findReindexOperation(indexName);
|
||||
|
||||
// If the reindexOp already exists and it's paused, resume it. Otherwise create a new one.
|
||||
const reindexOp =
|
||||
existingOp && existingOp.attributes.status === ReindexStatus.paused
|
||||
? await reindexService.resumeReindexOperation(indexName)
|
||||
: await reindexService.createReindexOperation(indexName);
|
||||
|
||||
// Add users credentials for the worker to use
|
||||
credentialStore.set(reindexOp, reqShim.headers);
|
||||
|
||||
// Kick the worker on this node to immediately pickup the new reindex operation.
|
||||
worker.forceRefresh();
|
||||
|
||||
return response.ok({ body: reindexOp.attributes });
|
||||
} catch (e) {
|
||||
return response.internalError({ body: e });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Get status
|
||||
server.router.get(
|
||||
{
|
||||
path: `${BASE_PATH}/{indexName}`,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
versionCheckHandlerWrapper(async (ctx, request, response) => {
|
||||
const reqShim = createRequestShim(request);
|
||||
const { client } = ctx.core.savedObjects;
|
||||
const { indexName } = reqShim.params;
|
||||
const callCluster = callWithRequest.bind(null, reqShim) as CallCluster;
|
||||
const reindexActions = reindexActionsFactory(client, callCluster);
|
||||
const reindexService = reindexServiceFactory(
|
||||
callCluster,
|
||||
xpackInfo,
|
||||
reindexActions,
|
||||
server.log
|
||||
);
|
||||
|
||||
try {
|
||||
const hasRequiredPrivileges = await reindexService.hasRequiredPrivileges(indexName);
|
||||
const reindexOp = await reindexService.findReindexOperation(indexName);
|
||||
// If the user doesn't have privileges than querying for warnings is going to fail.
|
||||
const warnings = hasRequiredPrivileges
|
||||
? await reindexService.detectReindexWarnings(indexName)
|
||||
: [];
|
||||
const indexGroup = reindexService.getIndexGroup(indexName);
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
reindexOp: reindexOp ? reindexOp.attributes : null,
|
||||
warnings,
|
||||
indexGroup,
|
||||
hasRequiredPrivileges,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (!e.isBoom) {
|
||||
return response.internalError({ body: e });
|
||||
}
|
||||
return response.customError({
|
||||
body: {
|
||||
message: e.message,
|
||||
},
|
||||
statusCode: e.statusCode,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Cancel reindex
|
||||
server.router.post(
|
||||
{
|
||||
path: `${BASE_PATH}/{indexName}/cancel`,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
versionCheckHandlerWrapper(async (ctx, request, response) => {
|
||||
const reqShim = createRequestShim(request);
|
||||
const { indexName } = reqShim.params;
|
||||
const { client } = ctx.core.savedObjects;
|
||||
const callCluster = callWithRequest.bind(null, reqShim) as CallCluster;
|
||||
const reindexActions = reindexActionsFactory(client, callCluster);
|
||||
const reindexService = reindexServiceFactory(
|
||||
callCluster,
|
||||
xpackInfo,
|
||||
reindexActions,
|
||||
server.log
|
||||
);
|
||||
|
||||
try {
|
||||
await reindexService.cancelReindexing(indexName);
|
||||
|
||||
return response.ok({ body: { acknowledged: true } });
|
||||
} catch (e) {
|
||||
if (!e.isBoom) {
|
||||
return response.internalError({ body: e });
|
||||
}
|
||||
return response.customError({
|
||||
body: {
|
||||
message: e.message,
|
||||
},
|
||||
statusCode: e.statusCode,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { Legacy } from 'kibana';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { XPackMainPlugin } from '../../../xpack_main/server/xpack_main';
|
||||
|
||||
export interface ServerShim {
|
||||
plugins: {
|
||||
elasticsearch: ElasticsearchPlugin;
|
||||
xpack_main: XPackMainPlugin;
|
||||
};
|
||||
log: any;
|
||||
events: any;
|
||||
savedObjects: Legacy.SavedObjectsService;
|
||||
}
|
||||
|
||||
export interface ServerShimWithRouter extends ServerShim {
|
||||
router: IRouter;
|
||||
}
|
||||
|
||||
export interface RequestShim {
|
||||
headers: Record<string, string>;
|
||||
payload: any;
|
||||
params: any;
|
||||
}
|
|
@ -4,8 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { UpgradeAssistantUIPlugin } from './plugin';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
|
||||
export const plugin = () => {
|
||||
return new UpgradeAssistantUIPlugin();
|
||||
};
|
||||
export const configSchema = schema.object({
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
});
|
||||
|
||||
export type Config = TypeOf<typeof configSchema>;
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { SavedObject, SavedObjectAttributes } from 'src/core/public';
|
||||
|
||||
export enum ReindexStep {
|
||||
|
@ -114,3 +115,15 @@ export interface UpgradeAssistantTelemetry {
|
|||
export interface UpgradeAssistantTelemetrySavedObjectAttributes {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface EnrichedDeprecationInfo extends DeprecationInfo {
|
||||
index?: string;
|
||||
node?: string;
|
||||
reindex?: boolean;
|
||||
}
|
||||
|
||||
export interface UpgradeAssistantStatus {
|
||||
readyForUpgrade: boolean;
|
||||
cluster: EnrichedDeprecationInfo[];
|
||||
indices: EnrichedDeprecationInfo[];
|
||||
}
|
9
x-pack/plugins/upgrade_assistant/kibana.json
Normal file
9
x-pack/plugins/upgrade_assistant/kibana.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"id": "upgradeAssistant",
|
||||
"version": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"configPath": ["xpack", "upgrade_assistant"],
|
||||
"requiredPlugins": ["management", "licensing"],
|
||||
"optionalPlugins": ["cloud", "usageCollection"]
|
||||
}
|
44
x-pack/plugins/upgrade_assistant/public/application/app.tsx
Normal file
44
x-pack/plugins/upgrade_assistant/public/application/app.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { I18nStart } from 'src/core/public';
|
||||
import { EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { NEXT_MAJOR_VERSION } from '../../common/version';
|
||||
import { UpgradeAssistantTabs } from './components/tabs';
|
||||
import { AppContextProvider, ContextValue, AppContext } from './app_context';
|
||||
|
||||
export interface AppDependencies extends ContextValue {
|
||||
i18n: I18nStart;
|
||||
}
|
||||
|
||||
export const RootComponent = ({ i18n, ...contexValue }: AppDependencies) => {
|
||||
return (
|
||||
<i18n.Context>
|
||||
<AppContextProvider value={contexValue}>
|
||||
<div data-test-subj="upgradeAssistantRoot">
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.appTitle"
|
||||
defaultMessage="{version} Upgrade Assistant"
|
||||
values={{ version: `${NEXT_MAJOR_VERSION}.0` }}
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<AppContext.Consumer>
|
||||
{({ http }) => <UpgradeAssistantTabs http={http} />}
|
||||
</AppContext.Consumer>
|
||||
</div>
|
||||
</AppContextProvider>
|
||||
</i18n.Context>
|
||||
);
|
||||
};
|
|
@ -9,7 +9,6 @@ import React, { createContext, useContext } from 'react';
|
|||
export interface ContextValue {
|
||||
http: HttpSetup;
|
||||
isCloudEnabled: boolean;
|
||||
XSRF: string;
|
||||
}
|
||||
|
||||
export const AppContext = createContext<ContextValue>({} as any);
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { EuiCallOut, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../common/version';
|
||||
import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../common/version';
|
||||
|
||||
export const LatestMinorBanner: React.FunctionComponent = () => (
|
||||
<EuiCallOut
|
|
@ -6,51 +6,39 @@
|
|||
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
|
||||
jest.mock('axios', () => ({
|
||||
get: jest.fn(),
|
||||
create: jest.fn(),
|
||||
}));
|
||||
|
||||
import { httpServiceMock } from 'src/core/public/mocks';
|
||||
import { UpgradeAssistantTabs } from './tabs';
|
||||
import { LoadingState } from './types';
|
||||
|
||||
import axios from 'axios';
|
||||
import { OverviewTab } from './tabs/overview';
|
||||
|
||||
// Used to wait for promises to resolve and renders to finish before reading updates
|
||||
const promisesToResolve = () => new Promise(resolve => setTimeout(resolve, 0));
|
||||
|
||||
const mockHttp = {
|
||||
basePath: {
|
||||
prepend: () => 'test',
|
||||
},
|
||||
fetch() {},
|
||||
};
|
||||
const mockHttp = httpServiceMock.createSetupContract();
|
||||
|
||||
describe('UpgradeAssistantTabs', () => {
|
||||
test('renders loading state', async () => {
|
||||
// @ts-ignore
|
||||
axios.get.mockReturnValue(
|
||||
mockHttp.get.mockReturnValue(
|
||||
new Promise(resolve => {
|
||||
/* never resolve */
|
||||
})
|
||||
);
|
||||
const wrapper = mountWithIntl(<UpgradeAssistantTabs http={mockHttp as any} />);
|
||||
const wrapper = mountWithIntl(<UpgradeAssistantTabs http={mockHttp} />);
|
||||
// Should pass down loading status to child component
|
||||
expect(wrapper.find(OverviewTab).prop('loadingState')).toEqual(LoadingState.Loading);
|
||||
});
|
||||
|
||||
test('successful data fetch', async () => {
|
||||
// @ts-ignore
|
||||
axios.get.mockResolvedValue({
|
||||
mockHttp.get.mockResolvedValue({
|
||||
data: {
|
||||
cluster: [],
|
||||
indices: [],
|
||||
},
|
||||
});
|
||||
const wrapper = mountWithIntl(<UpgradeAssistantTabs http={mockHttp as any} />);
|
||||
expect(axios.get).toHaveBeenCalled();
|
||||
expect(mockHttp.get).toHaveBeenCalled();
|
||||
await promisesToResolve();
|
||||
wrapper.update();
|
||||
// Should pass down success status to child component
|
||||
|
@ -59,7 +47,7 @@ describe('UpgradeAssistantTabs', () => {
|
|||
|
||||
test('network failure', async () => {
|
||||
// @ts-ignore
|
||||
axios.get.mockRejectedValue(new Error(`oh no!`));
|
||||
mockHttp.get.mockRejectedValue(new Error(`oh no!`));
|
||||
const wrapper = mountWithIntl(<UpgradeAssistantTabs http={mockHttp as any} />);
|
||||
await promisesToResolve();
|
||||
wrapper.update();
|
||||
|
@ -69,7 +57,7 @@ describe('UpgradeAssistantTabs', () => {
|
|||
|
||||
it('upgrade error', async () => {
|
||||
// @ts-ignore
|
||||
axios.get.mockRejectedValue({ response: { status: 426 } });
|
||||
mockHttp.get.mockRejectedValue({ response: { status: 426 } });
|
||||
const wrapper = mountWithIntl(<UpgradeAssistantTabs http={mockHttp as any} />);
|
||||
await promisesToResolve();
|
||||
wrapper.update();
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { findIndex, get, set } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
|
@ -18,7 +17,7 @@ import {
|
|||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import { HttpSetup } from 'src/core/public';
|
||||
|
||||
import { UpgradeAssistantStatus } from '../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { UpgradeAssistantStatus } from '../../../common/types';
|
||||
import { LatestMinorBanner } from './latest_minor_banner';
|
||||
import { CheckupTab } from './tabs/checkup';
|
||||
import { OverviewTab } from './tabs/overview';
|
||||
|
@ -153,12 +152,10 @@ export class UpgradeAssistantTabsUI extends React.Component<Props, TabsState> {
|
|||
private loadData = async () => {
|
||||
try {
|
||||
this.setState({ loadingState: LoadingState.Loading });
|
||||
const resp = await axios.get(
|
||||
this.props.http.basePath.prepend('/api/upgrade_assistant/status')
|
||||
);
|
||||
const resp = await this.props.http.get('/api/upgrade_assistant/status');
|
||||
this.setState({
|
||||
loadingState: LoadingState.Success,
|
||||
checkupData: resp.data,
|
||||
checkupData: resp,
|
||||
});
|
||||
} catch (e) {
|
||||
if (get(e, 'response.status') === 426) {
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { NEXT_MAJOR_VERSION } from '../../../../../../common/version';
|
||||
import { NEXT_MAJOR_VERSION } from '../../../../../common/version';
|
||||
import { LoadingErrorBanner } from '../../error_banner';
|
||||
import {
|
||||
GroupByOption,
|
|
@ -79,9 +79,7 @@ export const DeprecationCell: FunctionComponent<DeprecationCellProps> = ({
|
|||
{reindexIndexName && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<AppContext.Consumer>
|
||||
{({ http, XSRF }) => (
|
||||
<ReindexButton indexName={reindexIndexName} http={http} xsrf={XSRF} />
|
||||
)}
|
||||
{({ http }) => <ReindexButton indexName={reindexIndexName} http={http} />}
|
||||
</AppContext.Consumer>
|
||||
</EuiFlexItem>
|
||||
)}
|
|
@ -8,7 +8,7 @@ import React, { Fragment, FunctionComponent } from 'react';
|
|||
import { EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
|
||||
export const DeprecationCountSummary: FunctionComponent<{
|
||||
deprecations: EnrichedDeprecationInfo[];
|
|
@ -11,7 +11,7 @@ import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
|||
import { EuiBadge, EuiPagination } from '@elastic/eui';
|
||||
|
||||
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { GroupByOption, LevelFilterOption } from '../../../types';
|
||||
import { DeprecationAccordion, filterDeps, GroupedDeprecations } from './grouped';
|
||||
|
|
@ -19,7 +19,7 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { GroupByOption, LevelFilterOption } from '../../../types';
|
||||
|
||||
import { DeprecationCountSummary } from './count_summary';
|
|
@ -148,9 +148,7 @@ export class IndexDeprecationTableUI extends React.Component<
|
|||
render(indexDep: IndexDeprecationDetails) {
|
||||
return (
|
||||
<AppContext.Consumer>
|
||||
{({ XSRF, http }) => (
|
||||
<ReindexButton indexName={indexDep.index!} http={http} xsrf={XSRF} />
|
||||
)}
|
||||
{({ http }) => <ReindexButton indexName={indexDep.index!} http={http} />}
|
||||
</AppContext.Consumer>
|
||||
);
|
||||
},
|
|
@ -7,7 +7,7 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { GroupByOption } from '../../../types';
|
||||
import { DeprecationList } from './list';
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { EnrichedDeprecationInfo } from '../../../../../../common/types';
|
||||
import { GroupByOption } from '../../../types';
|
||||
|
||||
import { COLOR_MAP, LEVEL_MAP } from '../constants';
|
|
@ -11,14 +11,13 @@ import { Subscription } from 'rxjs';
|
|||
import { EuiButton, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { HttpSetup } from 'src/core/public';
|
||||
import { ReindexStatus, UIReindexOption } from '../../../../../../../../common/types';
|
||||
import { ReindexStatus, UIReindexOption } from '../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../types';
|
||||
import { ReindexFlyout } from './flyout';
|
||||
import { ReindexPollingService, ReindexState } from './polling_service';
|
||||
|
||||
interface ReindexButtonProps {
|
||||
indexName: string;
|
||||
xsrf: string;
|
||||
http: HttpSetup;
|
||||
}
|
||||
|
||||
|
@ -154,8 +153,8 @@ export class ReindexButton extends React.Component<ReindexButtonProps, ReindexBu
|
|||
}
|
||||
|
||||
private newService() {
|
||||
const { indexName, xsrf, http } = this.props;
|
||||
return new ReindexPollingService(indexName, xsrf, http);
|
||||
const { indexName, http } = this.props;
|
||||
return new ReindexPollingService(indexName, http);
|
||||
}
|
||||
|
||||
private subscribeToUpdates() {
|
|
@ -8,7 +8,7 @@ import { shallow } from 'enzyme';
|
|||
import { cloneDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { ReindexStatus, ReindexWarning } from '../../../../../../../../../common/types';
|
||||
import { ReindexStatus, ReindexWarning } from '../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../types';
|
||||
import { ReindexState } from '../polling_service';
|
||||
import { ChecklistFlyoutStep } from './checklist_step';
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { ReindexStatus } from '../../../../../../../../../common/types';
|
||||
import { ReindexStatus } from '../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../types';
|
||||
import { ReindexState } from '../polling_service';
|
||||
import { ReindexProgress } from './progress';
|
|
@ -7,7 +7,7 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { IndexGroup, ReindexStatus, ReindexStep } from '../../../../../../../../../common/types';
|
||||
import { IndexGroup, ReindexStatus, ReindexStep } from '../../../../../../../../common/types';
|
||||
import { ReindexState } from '../polling_service';
|
||||
import { ReindexProgress } from './progress';
|
||||
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { IndexGroup, ReindexStatus, ReindexStep } from '../../../../../../../../../common/types';
|
||||
import { IndexGroup, ReindexStatus, ReindexStep } from '../../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../../types';
|
||||
import { ReindexState } from '../polling_service';
|
||||
import { StepProgress, StepProgressStep } from './step_progress';
|
|
@ -8,7 +8,7 @@ import { I18nProvider } from '@kbn/i18n/react';
|
|||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { ReindexWarning } from '../../../../../../../../../common/types';
|
||||
import { ReindexWarning } from '../../../../../../../../common/types';
|
||||
import { idForWarning, WarningsFlyoutStep } from './warnings_step';
|
||||
|
||||
describe('WarningsFlyoutStep', () => {
|
|
@ -21,7 +21,7 @@ import {
|
|||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { ReindexWarning } from '../../../../../../../../../common/types';
|
||||
import { ReindexWarning } from '../../../../../../../../common/types';
|
||||
|
||||
interface CheckedIds {
|
||||
[id: string]: boolean;
|
|
@ -4,12 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mockClient } from './polling_service.test.mocks';
|
||||
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../../common/types';
|
||||
import { ReindexStatus, ReindexStep } from '../../../../../../../common/types';
|
||||
import { ReindexPollingService } from './polling_service';
|
||||
import { httpServiceMock } from 'src/core/public/http/http_service.mock';
|
||||
|
||||
const mockClient = httpServiceMock.createSetupContract();
|
||||
|
||||
describe('ReindexPollingService', () => {
|
||||
beforeEach(() => {
|
||||
mockClient.post.mockReset();
|
||||
|
@ -18,18 +18,11 @@ describe('ReindexPollingService', () => {
|
|||
|
||||
it('does not poll when reindexOp is null', async () => {
|
||||
mockClient.get.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {
|
||||
warnings: [],
|
||||
reindexOp: null,
|
||||
},
|
||||
warnings: [],
|
||||
reindexOp: null,
|
||||
});
|
||||
|
||||
const service = new ReindexPollingService(
|
||||
'myIndex',
|
||||
'myXsrf',
|
||||
httpServiceMock.createSetupContract()
|
||||
);
|
||||
const service = new ReindexPollingService('myIndex', mockClient);
|
||||
service.updateStatus();
|
||||
await new Promise(resolve => setTimeout(resolve, 1200)); // wait for poll interval
|
||||
|
||||
|
@ -39,22 +32,15 @@ describe('ReindexPollingService', () => {
|
|||
|
||||
it('does not poll when first check is a 200 and status is failed', async () => {
|
||||
mockClient.get.mockResolvedValue({
|
||||
status: 200,
|
||||
data: {
|
||||
warnings: [],
|
||||
reindexOp: {
|
||||
lastCompletedStep: ReindexStep.created,
|
||||
status: ReindexStatus.failed,
|
||||
errorMessage: `Oh no!`,
|
||||
},
|
||||
warnings: [],
|
||||
reindexOp: {
|
||||
lastCompletedStep: ReindexStep.created,
|
||||
status: ReindexStatus.failed,
|
||||
errorMessage: `Oh no!`,
|
||||
},
|
||||
});
|
||||
|
||||
const service = new ReindexPollingService(
|
||||
'myIndex',
|
||||
'myXsrf',
|
||||
httpServiceMock.createSetupContract()
|
||||
);
|
||||
const service = new ReindexPollingService('myIndex', mockClient);
|
||||
service.updateStatus();
|
||||
await new Promise(resolve => setTimeout(resolve, 1200)); // wait for poll interval
|
||||
|
||||
|
@ -65,21 +51,14 @@ describe('ReindexPollingService', () => {
|
|||
|
||||
it('begins to poll when first check is a 200 and status is inProgress', async () => {
|
||||
mockClient.get.mockResolvedValue({
|
||||
status: 200,
|
||||
data: {
|
||||
warnings: [],
|
||||
reindexOp: {
|
||||
lastCompletedStep: ReindexStep.created,
|
||||
status: ReindexStatus.inProgress,
|
||||
},
|
||||
warnings: [],
|
||||
reindexOp: {
|
||||
lastCompletedStep: ReindexStep.created,
|
||||
status: ReindexStatus.inProgress,
|
||||
},
|
||||
});
|
||||
|
||||
const service = new ReindexPollingService(
|
||||
'myIndex',
|
||||
'myXsrf',
|
||||
httpServiceMock.createSetupContract()
|
||||
);
|
||||
const service = new ReindexPollingService('myIndex', mockClient);
|
||||
service.updateStatus();
|
||||
await new Promise(resolve => setTimeout(resolve, 1200)); // wait for poll interval
|
||||
|
||||
|
@ -89,11 +68,7 @@ describe('ReindexPollingService', () => {
|
|||
|
||||
describe('startReindex', () => {
|
||||
it('posts to endpoint', async () => {
|
||||
const service = new ReindexPollingService(
|
||||
'myIndex',
|
||||
'myXsrf',
|
||||
httpServiceMock.createSetupContract()
|
||||
);
|
||||
const service = new ReindexPollingService('myIndex', mockClient);
|
||||
await service.startReindex();
|
||||
|
||||
expect(mockClient.post).toHaveBeenCalledWith('/api/upgrade_assistant/reindex/myIndex');
|
||||
|
@ -102,11 +77,7 @@ describe('ReindexPollingService', () => {
|
|||
|
||||
describe('cancelReindex', () => {
|
||||
it('posts to cancel endpoint', async () => {
|
||||
const service = new ReindexPollingService(
|
||||
'myIndex',
|
||||
'myXsrf',
|
||||
httpServiceMock.createSetupContract()
|
||||
);
|
||||
const service = new ReindexPollingService('myIndex', mockClient);
|
||||
await service.cancelReindex();
|
||||
|
||||
expect(mockClient.post).toHaveBeenCalledWith('/api/upgrade_assistant/reindex/myIndex/cancel');
|
|
@ -3,8 +3,6 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { HttpSetup } from 'src/core/public';
|
||||
|
@ -14,7 +12,7 @@ import {
|
|||
ReindexStatus,
|
||||
ReindexStep,
|
||||
ReindexWarning,
|
||||
} from '../../../../../../../../common/types';
|
||||
} from '../../../../../../../common/types';
|
||||
import { LoadingState } from '../../../../types';
|
||||
|
||||
const POLL_INTERVAL = 1000;
|
||||
|
@ -45,24 +43,13 @@ interface StatusResponse {
|
|||
export class ReindexPollingService {
|
||||
public status$: BehaviorSubject<ReindexState>;
|
||||
private pollTimeout?: NodeJS.Timeout;
|
||||
private APIClient: AxiosInstance;
|
||||
|
||||
constructor(private indexName: string, private xsrf: string, private http: HttpSetup) {
|
||||
constructor(private indexName: string, private http: HttpSetup) {
|
||||
this.status$ = new BehaviorSubject<ReindexState>({
|
||||
loadingState: LoadingState.Loading,
|
||||
errorMessage: null,
|
||||
reindexTaskPercComplete: null,
|
||||
});
|
||||
|
||||
this.APIClient = axios.create({
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
credentials: 'same-origin',
|
||||
'Content-Type': 'application/json',
|
||||
'kbn-version': this.xsrf,
|
||||
'kbn-xsrf': this.xsrf,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public updateStatus = async () => {
|
||||
|
@ -70,8 +57,8 @@ export class ReindexPollingService {
|
|||
this.stopPolling();
|
||||
|
||||
try {
|
||||
const { data } = await this.APIClient.get<StatusResponse>(
|
||||
this.http.basePath.prepend(`/api/upgrade_assistant/reindex/${this.indexName}`)
|
||||
const data = await this.http.get<StatusResponse>(
|
||||
`/api/upgrade_assistant/reindex/${this.indexName}`
|
||||
);
|
||||
this.updateWithResponse(data);
|
||||
|
||||
|
@ -107,8 +94,8 @@ export class ReindexPollingService {
|
|||
errorMessage: null,
|
||||
cancelLoadingState: undefined,
|
||||
});
|
||||
const { data } = await this.APIClient.post<ReindexOperation>(
|
||||
this.http.basePath.prepend(`/api/upgrade_assistant/reindex/${this.indexName}`)
|
||||
const data = await this.http.post<ReindexOperation>(
|
||||
`/api/upgrade_assistant/reindex/${this.indexName}`
|
||||
);
|
||||
|
||||
this.updateWithResponse({ reindexOp: data });
|
||||
|
@ -125,9 +112,7 @@ export class ReindexPollingService {
|
|||
cancelLoadingState: LoadingState.Loading,
|
||||
});
|
||||
|
||||
await this.APIClient.post(
|
||||
this.http.basePath.prepend(`/api/upgrade_assistant/reindex/${this.indexName}/cancel`)
|
||||
);
|
||||
await this.http.post(`/api/upgrade_assistant/reindex/${this.indexName}/cancel`);
|
||||
} catch (e) {
|
||||
this.status$.next({
|
||||
...this.status$.value,
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import React from 'react';
|
||||
|
||||
import { EuiLoadingSpinner, EuiSwitch } from '@elastic/eui';
|
||||
|
@ -15,7 +14,6 @@ import { HttpSetup } from 'src/core/public';
|
|||
import { LoadingState } from '../../types';
|
||||
|
||||
interface DeprecationLoggingTabProps extends ReactIntl.InjectedIntlProps {
|
||||
xsrf: string;
|
||||
http: HttpSetup;
|
||||
}
|
||||
|
||||
|
@ -88,12 +86,10 @@ export class DeprecationLoggingToggleUI extends React.Component<
|
|||
private loadData = async () => {
|
||||
try {
|
||||
this.setState({ loadingState: LoadingState.Loading });
|
||||
const resp = await axios.get(
|
||||
this.props.http.basePath.prepend('/api/upgrade_assistant/deprecation_logging')
|
||||
);
|
||||
const resp = await this.props.http.get('/api/upgrade_assistant/deprecation_logging');
|
||||
this.setState({
|
||||
loadingState: LoadingState.Success,
|
||||
loggingEnabled: resp.data.isEnabled,
|
||||
loggingEnabled: resp.isEnabled,
|
||||
});
|
||||
} catch (e) {
|
||||
this.setState({ loadingState: LoadingState.Error });
|
||||
|
@ -102,26 +98,19 @@ export class DeprecationLoggingToggleUI extends React.Component<
|
|||
|
||||
private toggleLogging = async () => {
|
||||
try {
|
||||
const { http, xsrf } = this.props;
|
||||
// Optimistically toggle the UI
|
||||
const newEnabled = !this.state.loggingEnabled;
|
||||
this.setState({ loadingState: LoadingState.Loading, loggingEnabled: newEnabled });
|
||||
|
||||
const resp = await axios.put(
|
||||
http.basePath.prepend('/api/upgrade_assistant/deprecation_logging'),
|
||||
{
|
||||
const resp = await this.props.http.put('/api/upgrade_assistant/deprecation_logging', {
|
||||
body: JSON.stringify({
|
||||
isEnabled: newEnabled,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'kbn-xsrf': xsrf,
|
||||
},
|
||||
}
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loadingState: LoadingState.Success,
|
||||
loggingEnabled: resp.data.isEnabled,
|
||||
loggingEnabled: resp.isEnabled,
|
||||
});
|
||||
} catch (e) {
|
||||
this.setState({ loadingState: LoadingState.Error });
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { NEXT_MAJOR_VERSION } from '../../../../../../common/version';
|
||||
import { NEXT_MAJOR_VERSION } from '../../../../../common/version';
|
||||
import { LoadingErrorBanner } from '../../error_banner';
|
||||
import { LoadingState, UpgradeAssistantTabProps } from '../../types';
|
||||
import { Steps } from './steps';
|
|
@ -19,7 +19,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../../../common/version';
|
||||
import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../../common/version';
|
||||
import { UpgradeAssistantTabProps } from '../../types';
|
||||
import { DeprecationLoggingToggle } from './deprecation_logging_toggle';
|
||||
import { useAppContext } from '../../../app_context';
|
||||
|
@ -104,7 +104,7 @@ export const StepsUI: FunctionComponent<UpgradeAssistantTabProps & ReactIntl.Inj
|
|||
}, {} as { [checkupType: string]: number });
|
||||
|
||||
// Uncomment when START_UPGRADE_STEP is in use!
|
||||
const { http, XSRF /* , isCloudEnabled */ } = useAppContext();
|
||||
const { http /* , isCloudEnabled */ } = useAppContext();
|
||||
|
||||
return (
|
||||
<EuiSteps
|
||||
|
@ -262,7 +262,7 @@ export const StepsUI: FunctionComponent<UpgradeAssistantTabProps & ReactIntl.Inj
|
|||
})}
|
||||
describedByIds={['deprecation-logging']}
|
||||
>
|
||||
<DeprecationLoggingToggle http={http} xsrf={XSRF} />
|
||||
<DeprecationLoggingToggle http={http} />
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
),
|
|
@ -6,10 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
EnrichedDeprecationInfo,
|
||||
UpgradeAssistantStatus,
|
||||
} from '../../../../server/np_ready/lib/es_migration_apis';
|
||||
import { EnrichedDeprecationInfo, UpgradeAssistantStatus } from '../../../common/types';
|
||||
|
||||
export interface UpgradeAssistantTabProps {
|
||||
alertBanner?: React.ReactNode;
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { AppDependencies, RootComponent } from './app';
|
||||
|
||||
interface BootDependencies extends AppDependencies {
|
||||
element: HTMLElement;
|
||||
}
|
||||
|
||||
export const renderApp = (deps: BootDependencies) => {
|
||||
const { element, ...appDependencies } = deps;
|
||||
render(<RootComponent {...appDependencies} />, element);
|
||||
return () => {
|
||||
unmountComponentAtNode(element);
|
||||
};
|
||||
};
|
1
x-pack/plugins/upgrade_assistant/public/index.scss
Normal file
1
x-pack/plugins/upgrade_assistant/public/index.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import './application/index';
|
|
@ -3,9 +3,10 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { PluginInitializerContext } from 'src/core/server';
|
||||
import { UpgradeAssistantServerPlugin } from './plugin';
|
||||
import './index.scss';
|
||||
import { PluginInitializerContext } from 'src/core/public';
|
||||
import { UpgradeAssistantUIPlugin } from './plugin';
|
||||
|
||||
export const plugin = (ctx: PluginInitializerContext) => {
|
||||
return new UpgradeAssistantServerPlugin();
|
||||
return new UpgradeAssistantUIPlugin(ctx);
|
||||
};
|
48
x-pack/plugins/upgrade_assistant/public/plugin.ts
Normal file
48
x-pack/plugins/upgrade_assistant/public/plugin.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public';
|
||||
|
||||
import { CloudSetup } from '../../cloud/public';
|
||||
import { ManagementSetup } from '../../../../src/plugins/management/public';
|
||||
|
||||
import { NEXT_MAJOR_VERSION } from '../common/version';
|
||||
import { Config } from '../common/config';
|
||||
|
||||
import { renderApp } from './application/render_app';
|
||||
|
||||
interface Dependencies {
|
||||
cloud: CloudSetup;
|
||||
management: ManagementSetup;
|
||||
}
|
||||
|
||||
export class UpgradeAssistantUIPlugin implements Plugin {
|
||||
constructor(private ctx: PluginInitializerContext) {}
|
||||
setup({ http, getStartServices }: CoreSetup, { cloud, management }: Dependencies) {
|
||||
const { enabled } = this.ctx.config.get<Config>();
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
const appRegistrar = management.sections.getSection('elasticsearch')!;
|
||||
const isCloudEnabled = Boolean(cloud?.isCloudEnabled);
|
||||
|
||||
appRegistrar.registerApp({
|
||||
id: 'upgrade_assistant',
|
||||
title: i18n.translate('xpack.upgradeAssistant.appTitle', {
|
||||
defaultMessage: '{version} Upgrade Assistant',
|
||||
values: { version: `${NEXT_MAJOR_VERSION}.0` },
|
||||
}),
|
||||
order: 1000,
|
||||
async mount({ element }) {
|
||||
const [{ i18n: i18nDep }] = await getStartServices();
|
||||
return renderApp({ element, isCloudEnabled, http, i18n: i18nDep });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
start() {}
|
||||
stop() {}
|
||||
}
|
19
x-pack/plugins/upgrade_assistant/server/index.ts
Normal file
19
x-pack/plugins/upgrade_assistant/server/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server';
|
||||
import { UpgradeAssistantServerPlugin } from './plugin';
|
||||
import { configSchema } from '../common/config';
|
||||
|
||||
export const plugin = (ctx: PluginInitializerContext) => {
|
||||
return new UpgradeAssistantServerPlugin(ctx);
|
||||
};
|
||||
|
||||
export const config: PluginConfigDescriptor = {
|
||||
schema: configSchema,
|
||||
exposeToBrowser: {
|
||||
enabled: true,
|
||||
},
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { elasticsearchServiceMock } from 'src/core/server/mocks';
|
||||
import {
|
||||
getDeprecationLoggingStatus,
|
||||
isDeprecationLoggingEnabled,
|
||||
|
@ -12,9 +12,9 @@ import {
|
|||
|
||||
describe('getDeprecationLoggingStatus', () => {
|
||||
it('calls cluster.getSettings', async () => {
|
||||
const callWithRequest = jest.fn();
|
||||
await getDeprecationLoggingStatus(callWithRequest, {} as any);
|
||||
expect(callWithRequest).toHaveBeenCalledWith({}, 'cluster.getSettings', {
|
||||
const dataClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
await getDeprecationLoggingStatus(dataClient);
|
||||
expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('cluster.getSettings', {
|
||||
includeDefaults: true,
|
||||
});
|
||||
});
|
||||
|
@ -23,9 +23,9 @@ describe('getDeprecationLoggingStatus', () => {
|
|||
describe('setDeprecationLogging', () => {
|
||||
describe('isEnabled = true', () => {
|
||||
it('calls cluster.putSettings with logger.deprecation = WARN', async () => {
|
||||
const callWithRequest = jest.fn();
|
||||
await setDeprecationLogging(callWithRequest, {} as any, true);
|
||||
expect(callWithRequest).toHaveBeenCalledWith({}, 'cluster.putSettings', {
|
||||
const dataClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
await setDeprecationLogging(dataClient, true);
|
||||
expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('cluster.putSettings', {
|
||||
body: { transient: { 'logger.deprecation': 'WARN' } },
|
||||
});
|
||||
});
|
||||
|
@ -33,9 +33,9 @@ describe('setDeprecationLogging', () => {
|
|||
|
||||
describe('isEnabled = false', () => {
|
||||
it('calls cluster.putSettings with logger.deprecation = ERROR', async () => {
|
||||
const callWithRequest = jest.fn();
|
||||
await setDeprecationLogging(callWithRequest, {} as any, false);
|
||||
expect(callWithRequest).toHaveBeenCalledWith({}, 'cluster.putSettings', {
|
||||
const dataClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
await setDeprecationLogging(dataClient, false);
|
||||
expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('cluster.putSettings', {
|
||||
body: { transient: { 'logger.deprecation': 'ERROR' } },
|
||||
});
|
||||
});
|
|
@ -4,19 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { CallClusterWithRequest } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { RequestShim } from '../types';
|
||||
import { IScopedClusterClient } from 'src/core/server';
|
||||
|
||||
interface DeprecationLoggingStatus {
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
export async function getDeprecationLoggingStatus(
|
||||
callWithRequest: CallClusterWithRequest,
|
||||
req: RequestShim
|
||||
dataClient: IScopedClusterClient
|
||||
): Promise<DeprecationLoggingStatus> {
|
||||
const response = await callWithRequest(req, 'cluster.getSettings', {
|
||||
const response = await dataClient.callAsCurrentUser('cluster.getSettings', {
|
||||
includeDefaults: true,
|
||||
});
|
||||
|
||||
|
@ -26,11 +23,10 @@ export async function getDeprecationLoggingStatus(
|
|||
}
|
||||
|
||||
export async function setDeprecationLogging(
|
||||
callWithRequest: CallClusterWithRequest,
|
||||
req: RequestShim,
|
||||
dataClient: IScopedClusterClient,
|
||||
isEnabled: boolean
|
||||
): Promise<DeprecationLoggingStatus> {
|
||||
const response = await callWithRequest(req, 'cluster.putSettings', {
|
||||
const response = await dataClient.callAsCurrentUser('cluster.putSettings', {
|
||||
body: {
|
||||
transient: {
|
||||
'logger.deprecation': isEnabled ? 'WARN' : 'ERROR',
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { elasticsearchServiceMock } from 'src/core/server/mocks';
|
||||
import { getUpgradeAssistantStatus } from './es_migration_apis';
|
||||
|
||||
import { DeprecationAPIResponse } from 'src/legacy/core_plugins/elasticsearch';
|
||||
|
@ -13,7 +14,8 @@ import fakeDeprecations from './__fixtures__/fake_deprecations.json';
|
|||
describe('getUpgradeAssistantStatus', () => {
|
||||
let deprecationsResponse: DeprecationAPIResponse;
|
||||
|
||||
const callWithRequest = jest.fn().mockImplementation(async (req, api, { path }) => {
|
||||
const dataClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
(dataClient.callAsCurrentUser as jest.Mock).mockImplementation(async (api, { path }) => {
|
||||
if (path === '/_migration/deprecations') {
|
||||
return deprecationsResponse;
|
||||
} else if (api === 'indices.getMapping') {
|
||||
|
@ -28,15 +30,15 @@ describe('getUpgradeAssistantStatus', () => {
|
|||
});
|
||||
|
||||
it('calls /_migration/deprecations', async () => {
|
||||
await getUpgradeAssistantStatus(callWithRequest, {} as any, false);
|
||||
expect(callWithRequest).toHaveBeenCalledWith({}, 'transport.request', {
|
||||
await getUpgradeAssistantStatus(dataClient, false);
|
||||
expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('transport.request', {
|
||||
path: '/_migration/deprecations',
|
||||
method: 'GET',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the correct shape of data', async () => {
|
||||
const resp = await getUpgradeAssistantStatus(callWithRequest, {} as any, false);
|
||||
const resp = await getUpgradeAssistantStatus(dataClient, false);
|
||||
expect(resp).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -48,9 +50,10 @@ describe('getUpgradeAssistantStatus', () => {
|
|||
index_settings: {},
|
||||
};
|
||||
|
||||
await expect(
|
||||
getUpgradeAssistantStatus(callWithRequest, {} as any, false)
|
||||
).resolves.toHaveProperty('readyForUpgrade', false);
|
||||
await expect(getUpgradeAssistantStatus(dataClient, false)).resolves.toHaveProperty(
|
||||
'readyForUpgrade',
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('returns readyForUpgrade === true when no critical issues found', async () => {
|
||||
|
@ -61,9 +64,10 @@ describe('getUpgradeAssistantStatus', () => {
|
|||
index_settings: {},
|
||||
};
|
||||
|
||||
await expect(
|
||||
getUpgradeAssistantStatus(callWithRequest, {} as any, false)
|
||||
).resolves.toHaveProperty('readyForUpgrade', true);
|
||||
await expect(getUpgradeAssistantStatus(dataClient, false)).resolves.toHaveProperty(
|
||||
'readyForUpgrade',
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('filters out security realm deprecation on Cloud', async () => {
|
||||
|
@ -80,7 +84,7 @@ describe('getUpgradeAssistantStatus', () => {
|
|||
index_settings: {},
|
||||
};
|
||||
|
||||
const result = await getUpgradeAssistantStatus(callWithRequest, {} as any, true);
|
||||
const result = await getUpgradeAssistantStatus(dataClient, true);
|
||||
|
||||
expect(result).toHaveProperty('readyForUpgrade', true);
|
||||
expect(result).toHaveProperty('cluster', []);
|
|
@ -4,32 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
CallClusterWithRequest,
|
||||
DeprecationAPIResponse,
|
||||
DeprecationInfo,
|
||||
} from 'src/legacy/core_plugins/elasticsearch';
|
||||
|
||||
import { RequestShim } from '../types';
|
||||
|
||||
export interface EnrichedDeprecationInfo extends DeprecationInfo {
|
||||
index?: string;
|
||||
node?: string;
|
||||
reindex?: boolean;
|
||||
}
|
||||
|
||||
export interface UpgradeAssistantStatus {
|
||||
readyForUpgrade: boolean;
|
||||
cluster: EnrichedDeprecationInfo[];
|
||||
indices: EnrichedDeprecationInfo[];
|
||||
}
|
||||
import { IScopedClusterClient } from 'src/core/server';
|
||||
import { DeprecationAPIResponse } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { EnrichedDeprecationInfo, UpgradeAssistantStatus } from '../../common/types';
|
||||
|
||||
export async function getUpgradeAssistantStatus(
|
||||
callWithRequest: CallClusterWithRequest,
|
||||
req: RequestShim,
|
||||
dataClient: IScopedClusterClient,
|
||||
isCloudEnabled: boolean
|
||||
): Promise<UpgradeAssistantStatus> {
|
||||
const deprecations = await callWithRequest(req, 'transport.request', {
|
||||
const deprecations = await dataClient.callAsCurrentUser('transport.request', {
|
||||
path: '/_migration/deprecations',
|
||||
method: 'GET',
|
||||
});
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { SemVer } from 'semver';
|
||||
import { IScopedClusterClient, kibanaResponseFactory } from 'src/core/server';
|
||||
import { CURRENT_VERSION } from '../../../common/version';
|
||||
import { CURRENT_VERSION } from '../../common/version';
|
||||
import {
|
||||
esVersionCheck,
|
||||
getAllNodeVersions,
|
|
@ -13,7 +13,7 @@ import {
|
|||
RequestHandler,
|
||||
RequestHandlerContext,
|
||||
} from 'src/core/server';
|
||||
import { CURRENT_VERSION } from '../../../common/version';
|
||||
import { CURRENT_VERSION } from '../../common/version';
|
||||
|
||||
/**
|
||||
* Returns an array of all the unique Elasticsearch Node Versions in the Elasticsearch cluster.
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ReindexSavedObject } from '../../../../common/types';
|
||||
import { ReindexSavedObject } from '../../../common/types';
|
||||
import { Credential, credentialStoreFactory } from './credential_store';
|
||||
|
||||
describe('credentialStore', () => {
|
|
@ -5,12 +5,11 @@
|
|||
*/
|
||||
|
||||
import { createHash } from 'crypto';
|
||||
import { Request } from 'hapi';
|
||||
import stringify from 'json-stable-stringify';
|
||||
|
||||
import { ReindexSavedObject } from '../../../../common/types';
|
||||
import { ReindexSavedObject } from '../../../common/types';
|
||||
|
||||
export type Credential = Request['headers'];
|
||||
export type Credential = Record<string, any>;
|
||||
|
||||
/**
|
||||
* An in-memory cache for user credentials to be used for reindexing operations. When looking up
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue