Move ILM out of legacy (#61915)

* Rename IndexMgmtSetup to IndexManagementPluginSetup.
* Remove unused fetch index template route and related tests.
* Remove unnecessary custom styles.
This commit is contained in:
CJ Cenizal 2020-04-09 16:51:22 -07:00 committed by GitHub
parent 93b34632c0
commit 982c0da78e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
177 changed files with 1264 additions and 1828 deletions

2
.github/CODEOWNERS vendored
View file

@ -180,7 +180,7 @@
/src/plugins/console/ @elastic/es-ui
/src/plugins/es_ui_shared/ @elastic/es-ui
/x-pack/legacy/plugins/cross_cluster_replication/ @elastic/es-ui
/x-pack/legacy/plugins/index_lifecycle_management/ @elastic/es-ui
/x-pack/plugins/index_lifecycle_management/ @elastic/es-ui
/x-pack/legacy/plugins/index_management/ @elastic/es-ui
/x-pack/legacy/plugins/license_management/ @elastic/es-ui
/x-pack/legacy/plugins/rollup/ @elastic/es-ui

View file

@ -18,7 +18,7 @@
"xpack.graph": ["legacy/plugins/graph", "plugins/graph"],
"xpack.grokDebugger": "plugins/grokdebugger",
"xpack.idxMgmt": "plugins/index_management",
"xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management",
"xpack.indexLifecycleMgmt": "plugins/index_lifecycle_management",
"xpack.infra": "plugins/infra",
"xpack.ingestManager": "plugins/ingest_manager",
"xpack.lens": "legacy/plugins/lens",

View file

@ -16,7 +16,6 @@ import { beats } from './legacy/plugins/beats_management';
import { apm } from './legacy/plugins/apm';
import { maps } from './legacy/plugins/maps';
import { indexManagement } from './legacy/plugins/index_management';
import { indexLifecycleManagement } from './legacy/plugins/index_lifecycle_management';
import { spaces } from './legacy/plugins/spaces';
import { canvas } from './legacy/plugins/canvas';
import { infra } from './legacy/plugins/infra';
@ -50,7 +49,6 @@ module.exports = function(kibana) {
maps(kibana),
canvas(kibana),
indexManagement(kibana),
indexLifecycleManagement(kibana),
infra(kibana),
taskManager(kibana),
rollup(kibana),

View file

@ -6,7 +6,7 @@
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import { IndexMgmtSetup } from '../../../../../plugins/index_management/public';
import { IndexManagementPluginSetup } from '../../../../../plugins/index_management/public';
const propertyPath = 'isFollowerIndex';
@ -21,7 +21,7 @@ const followerBadgeExtension = {
filterExpression: 'isFollowerIndex:true',
};
export const extendIndexManagement = (indexManagement?: IndexMgmtSetup) => {
export const extendIndexManagement = (indexManagement?: IndexManagementPluginSetup) => {
if (indexManagement) {
indexManagement.extensionsService.addBadge(followerBadgeExtension);
}

View file

@ -11,7 +11,7 @@ import {
DocLinksStart,
} from 'src/core/public';
import { IndexMgmtSetup } from '../../../../../plugins/index_management/public';
import { IndexManagementPluginSetup } from '../../../../../plugins/index_management/public';
// @ts-ignore;
import { setHttpClient } from './app/services/api';
@ -21,7 +21,7 @@ import { setNotifications } from './app/services/notifications';
import { extendIndexManagement } from './extend_index_management';
interface PluginDependencies {
indexManagement: IndexMgmtSetup;
indexManagement: IndexManagementPluginSetup;
__LEGACY: {
chrome: any;
MANAGEMENT_BREADCRUMB: ChromeBreadcrumb;

View file

@ -6,7 +6,7 @@
import { Plugin, PluginInitializerContext, CoreSetup } from 'src/core/server';
import { IndexMgmtSetup } from '../../../../../plugins/index_management/server';
import { IndexManagementPluginSetup } from '../../../../../plugins/index_management/server';
// @ts-ignore
import { registerLicenseChecker } from './lib/register_license_checker';
@ -15,7 +15,7 @@ import { registerRoutes } from './routes/register_routes';
import { ccrDataEnricher } from './cross_cluster_replication_data';
interface PluginDependencies {
indexManagement: IndexMgmtSetup;
indexManagement: IndexManagementPluginSetup;
__LEGACY: {
server: any;
ccrUIEnabled: boolean;

View file

@ -1,62 +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 { resolve } from 'path';
import { PLUGIN } from './common/constants';
import { Plugin as IndexLifecycleManagementPlugin } from './plugin';
import { createShim } from './shim';
export function indexLifecycleManagement(kibana: any) {
return new kibana.Plugin({
id: PLUGIN.ID,
configPrefix: 'xpack.ilm',
publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch', 'xpack_main', 'index_management'],
uiExports: {
styleSheetPaths: resolve(__dirname, 'public/np_ready/application/index.scss'),
managementSections: ['plugins/index_lifecycle_management/legacy'],
injectDefaultVars(server: Legacy.Server) {
const config = server.config();
return {
ilmUiEnabled: config.get('xpack.ilm.ui.enabled'),
};
},
},
config: (Joi: any) => {
return Joi.object({
// display menu item
ui: Joi.object({
enabled: Joi.boolean().default(true),
}).default(),
// enable plugin
enabled: Joi.boolean().default(true),
filteredNodeAttributes: Joi.array()
.items(Joi.string())
.default([]),
}).default();
},
isEnabled(config: any) {
return (
config.get('xpack.ilm.enabled') &&
config.has('xpack.index_management.enabled') &&
config.get('xpack.index_management.enabled')
);
},
init(server: Legacy.Server) {
const core = server.newPlatform.setup.core;
const plugins = {};
const __LEGACY = createShim(server);
const indexLifecycleManagementPlugin = new IndexLifecycleManagementPlugin();
// Set up plugin.
indexLifecycleManagementPlugin.setup(core, plugins, __LEGACY);
},
});
}

View file

@ -1,54 +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 { CoreSetup } from 'kibana/server';
import { LegacySetup } from './shim';
import { registerLicenseChecker } from './server/lib/register_license_checker';
import { registerIndexRoutes } from './server/routes/api/index';
import { registerNodesRoutes } from './server/routes/api/nodes';
import { registerPoliciesRoutes } from './server/routes/api/policies';
import { registerTemplatesRoutes } from './server/routes/api/templates';
const indexLifecycleDataEnricher = async (indicesList: any, callWithRequest: any) => {
if (!indicesList || !indicesList.length) {
return;
}
const params = {
path: '/*/_ilm/explain',
method: 'GET',
};
const { indices: ilmIndicesData } = await callWithRequest('transport.request', params);
return indicesList.map((index: any): any => {
return {
...index,
ilm: { ...(ilmIndicesData[index.name] || {}) },
};
});
};
export class Plugin {
public setup(core: CoreSetup, plugins: any, __LEGACY: LegacySetup): void {
const { server } = __LEGACY;
registerLicenseChecker(server);
// Register routes.
registerIndexRoutes(server);
registerNodesRoutes(server);
registerPoliciesRoutes(server);
registerTemplatesRoutes(server);
const serverPlugins = server.newPlatform.setup.plugins as any;
if (
server.config().get('xpack.ilm.ui.enabled') &&
serverPlugins.indexManagement &&
serverPlugins.indexManagement.indexDataEnricher
) {
serverPlugins.indexManagement.indexDataEnricher.add(indexLifecycleDataEnricher);
}
}
}

View file

@ -1,109 +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 { App } from 'src/core/public';
/* Legacy Imports */
import { npSetup, npStart } from 'ui/new_platform';
import chrome from 'ui/chrome';
import routes from 'ui/routes';
import { management } from 'ui/management';
import { createUiStatsReporter } from '../../../../../src/legacy/core_plugins/ui_metric/public';
import { PLUGIN, BASE_PATH } from '../common/constants';
import { createPlugin } from './np_ready';
import { addAllExtensions } from './np_ready/extend_index_management';
if (chrome.getInjected('ilmUiEnabled')) {
// We have to initialize this outside of the NP lifecycle, otherwise these extensions won't
// be available in Index Management unless the user visits ILM first.
if ((npSetup.plugins as any).indexManagement) {
addAllExtensions((npSetup.plugins as any).indexManagement.extensionsService);
}
// This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular
// from destroying scope when route changes and both old route and new route are this same route.
const manageAngularLifecycle = ($scope: any, $route: any, unmount: () => void) => {
const lastRoute = $route.current;
const deregister = $scope.$on('$locationChangeSuccess', () => {
const currentRoute = $route.current;
// if templates are the same we are on the same route
if (lastRoute.$$route.template === currentRoute.$$route.template) {
// this prevents angular from destroying scope
$route.current = lastRoute;
}
});
$scope.$on('$destroy', () => {
if (deregister) {
deregister();
}
unmount();
});
};
// Once this app no longer depends upon Angular's routing (e.g. for the "redirect" service), we can
// use the Management plugin's API to register this app within the Elasticsearch section.
const esSection = management.getSection('elasticsearch');
esSection.register('index_lifecycle_policies', {
visible: true,
display: PLUGIN.TITLE,
order: 2,
url: `#${BASE_PATH}policies`,
});
const REACT_ROOT_ID = 'indexLifecycleManagementReactRoot';
const template = `<kbn-management-app section="elasticsearch/index_lifecycle_policies">
<div id="${REACT_ROOT_ID}" class="policyTable__horizontalScrollContainer"/>
</kbn-management-app>
`;
routes.when(`${BASE_PATH}:view?/:action?/:id?`, {
template,
controllerAs: 'indexLifecycleManagement',
controller: class IndexLifecycleManagementController {
constructor($scope: any, $route: any, kbnUrl: any, $rootScope: any) {
$scope.$$postDigest(() => {
const element = document.getElementById(REACT_ROOT_ID)!;
const { core } = npSetup;
const coreDependencies = {
...core,
application: {
...core.application,
async register(app: App<any>) {
const unmountApp = await app.mount({ ...npStart } as any, {
element,
appBasePath: '',
onAppLeave: () => undefined,
// TODO: adapt to use Core's ScopedHistory
history: {} as any,
});
manageAngularLifecycle($scope, $route, unmountApp as any);
},
},
};
// The Plugin interface won't allow us to pass __LEGACY as a third argument, so we'll just
// sneak it inside of the plugins argument for now.
const pluginDependencies = {
__LEGACY: {
redirect: (path: string) => {
$scope.$evalAsync(() => {
kbnUrl.redirect(path);
});
},
createUiStatsReporter,
},
};
const plugin = createPlugin({} as any);
plugin.setup(coreDependencies, pluginDependencies);
});
}
} as any,
} as any);
}

View file

@ -1,17 +0,0 @@
.policyTable__horizontalScrollContainer {
overflow-x: auto;
max-width: 100%;
}
.policyTable__horizontalScroll {
min-width: 800px;
width: 100%;
}
.policyTable__link {
font-weight: 400;
}
.ilmEditPolicyPageContent {
max-width: 1200px !important;
}

View file

@ -1,3 +0,0 @@
// Import the EUI global scope so we can use EUI constants
@import 'src/legacy/ui/public/styles/_styling_constants';
@import 'index_lifecycle_management';

View file

@ -1,63 +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 { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux';
import { DocLinksStart, ToastsSetup, HttpSetup, FatalErrorsSetup } from 'src/core/public';
import { App } from './app';
import { indexLifecycleManagementStore } from './store';
import { init as initHttp } from './services/http';
import { init as initNavigation } from './services/navigation';
import { init as initDocumentation } from './services/documentation';
import { init as initUiMetric } from './services/ui_metric';
import { init as initNotification } from './services/notification';
export interface LegacySetup {
redirect: any;
createUiStatsReporter: any;
}
interface AppDependencies {
legacy: LegacySetup;
I18nContext: any;
http: HttpSetup;
toasts: ToastsSetup;
fatalErrors: FatalErrorsSetup;
docLinks: DocLinksStart;
element: HTMLElement;
}
export const renderApp = (appDependencies: AppDependencies) => {
const {
legacy: { redirect, createUiStatsReporter },
I18nContext,
http,
toasts,
fatalErrors,
docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION },
element,
} = appDependencies;
// Initialize services
initHttp(http);
initNavigation(redirect);
initDocumentation(`${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`);
initUiMetric(createUiStatsReporter);
initNotification(toasts, fatalErrors);
render(
<I18nContext>
<Provider store={indexLifecycleManagementStore()}>
<App />
</Provider>
</I18nContext>,
element
);
return () => unmountComponentAtNode(element);
};

View file

@ -1,81 +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 {
UIM_POLICY_DELETE,
UIM_POLICY_ATTACH_INDEX,
UIM_POLICY_ATTACH_INDEX_TEMPLATE,
UIM_POLICY_DETACH_INDEX,
UIM_INDEX_RETRY_STEP,
} from '../constants';
import { trackUiMetric } from './ui_metric';
import { sendGet, sendPost, sendDelete } from './http';
// The extend_index_management module that we support an injected httpClient here.
export async function loadNodes(httpClient) {
return await sendGet(`nodes/list`, httpClient);
}
export async function loadNodeDetails(selectedNodeAttrs, httpClient) {
return await sendGet(`nodes/${selectedNodeAttrs}/details`, httpClient);
}
export async function loadIndexTemplates(httpClient) {
return await sendGet(`templates`, httpClient);
}
export async function loadIndexTemplate(templateName, httpClient) {
if (!templateName) {
return {};
}
return await sendGet(`templates/${templateName}`, httpClient);
}
export async function loadPolicies(withIndices, httpClient) {
const query = withIndices ? '?withIndices=true' : '';
return await sendGet('policies', query, httpClient);
}
export async function savePolicy(policy, httpClient) {
return await sendPost(`policies`, policy, httpClient);
}
export async function deletePolicy(policyName, httpClient) {
const response = await sendDelete(`policies/${encodeURIComponent(policyName)}`, httpClient);
// Only track successful actions.
trackUiMetric('count', UIM_POLICY_DELETE);
return response;
}
export const retryLifecycleForIndex = async (indexNames, httpClient) => {
const response = await sendPost(`index/retry`, { indexNames }, httpClient);
// Only track successful actions.
trackUiMetric('count', UIM_INDEX_RETRY_STEP);
return response;
};
export const removeLifecycleForIndex = async (indexNames, httpClient) => {
const response = await sendPost(`index/remove`, { indexNames }, httpClient);
// Only track successful actions.
trackUiMetric('count', UIM_POLICY_DETACH_INDEX);
return response;
};
export const addLifecyclePolicyToIndex = async (body, httpClient) => {
const response = await sendPost(`index/add`, body, httpClient);
// Only track successful actions.
trackUiMetric('count', UIM_POLICY_ATTACH_INDEX);
return response;
};
export const addLifecyclePolicyToTemplate = async (body, httpClient) => {
const response = await sendPost(`template`, body, httpClient);
// Only track successful actions.
trackUiMetric('count', UIM_POLICY_ATTACH_INDEX_TEMPLATE);
return response;
};

View file

@ -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 { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import { PLUGIN } from '../../common/constants';
import { LegacySetup } from './application';
interface PluginsSetup {
__LEGACY: LegacySetup;
}
export class IndexLifecycleManagementPlugin implements Plugin<void, void, any, any> {
setup(core: CoreSetup, plugins: PluginsSetup) {
// Extract individual core dependencies.
const {
application,
notifications: { toasts },
fatalErrors,
http,
} = core;
// The Plugin interface won't allow us to pass __LEGACY as a third argument, so we'll just
// sneak it inside of the plugins parameter for now.
const { __LEGACY } = plugins;
application.register({
id: PLUGIN.ID,
title: PLUGIN.TITLE,
async mount(config, mountPoint) {
const {
core: {
docLinks,
i18n: { Context: I18nContext },
},
} = config;
const { element } = mountPoint;
const { renderApp } = await import('./application');
// Inject all dependencies into our app.
return renderApp({
legacy: { ...__LEGACY },
I18nContext,
http,
toasts,
fatalErrors,
docLinks,
element,
});
},
});
}
start(core: CoreStart, plugins: any) {}
stop() {}
}

View file

@ -1,19 +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 { once } from 'lodash';
import { Legacy } from 'kibana';
const callWithRequest = once((server: Legacy.Server): any => {
const cluster = server.plugins.elasticsearch.getCluster('data');
return cluster.callWithRequest;
});
export const callWithRequestFactory = (server: Legacy.Server, request: any) => {
return (...args: any[]) => {
return callWithRequest(server)(request, ...args);
};
};

View file

@ -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 { callWithRequestFactory } from './call_with_request_factory';

View file

@ -1,145 +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 expect from '@kbn/expect';
import { set } from 'lodash';
import { checkLicense } from '../check_license';
describe('check_license', function() {
let mockLicenseInfo;
beforeEach(() => (mockLicenseInfo = {}));
describe('license information is undefined', () => {
beforeEach(() => (mockLicenseInfo = undefined));
it('should set isAvailable to false', () => {
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
});
it('should set showLinks to true', () => {
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
});
it('should set enableLinks to false', () => {
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
});
it('should set a message', () => {
expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
});
});
describe('license information is not available', () => {
beforeEach(() => (mockLicenseInfo.isAvailable = () => false));
it('should set isAvailable to false', () => {
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
});
it('should set showLinks to true', () => {
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
});
it('should set enableLinks to false', () => {
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
});
it('should set a message', () => {
expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
});
});
describe('license information is available', () => {
beforeEach(() => {
mockLicenseInfo.isAvailable = () => true;
set(mockLicenseInfo, 'license.getType', () => 'basic');
});
describe('& license is > basic', () => {
beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true));
describe('& license is active', () => {
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true));
it('should set isAvailable to true', () => {
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
});
it('should set showLinks to true', () => {
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
});
it('should set enableLinks to true', () => {
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true);
});
it('should not set a message', () => {
expect(checkLicense(mockLicenseInfo).message).to.be(undefined);
});
});
describe('& license is expired', () => {
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false));
it('should set isAvailable to false', () => {
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
});
it('should set showLinks to true', () => {
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
});
it('should set enableLinks to false', () => {
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
});
it('should set a message', () => {
expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
});
});
});
describe('& license is basic', () => {
beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true));
describe('& license is active', () => {
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true));
it('should set isAvailable to true', () => {
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
});
it('should set showLinks to true', () => {
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
});
it('should set enableLinks to true', () => {
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true);
});
it('should not set a message', () => {
expect(checkLicense(mockLicenseInfo).message).to.be(undefined);
});
});
describe('& license is expired', () => {
beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false));
it('should set isAvailable to false', () => {
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
});
it('should set showLinks to true', () => {
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
});
it('should set a message', () => {
expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
});
});
});
});
});

View file

@ -1,66 +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 { i18n } from '@kbn/i18n';
export function checkLicense(xpackLicenseInfo: any): any {
const pluginName = 'Index Lifecycle Policies';
// If, for some reason, we cannot get the license information
// from Elasticsearch, assume worst case and disable
if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) {
return {
isAvailable: false,
showLinks: true,
enableLinks: false,
message: i18n.translate('xpack.indexLifecycleMgmt.checkLicense.errorUnavailableMessage', {
defaultMessage:
'You cannot use {pluginName} because license information is not available at this time.',
values: { pluginName },
}),
};
}
const VALID_LICENSE_MODES = ['basic', 'standard', 'gold', 'platinum', 'enterprise', 'trial'];
const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES);
const isLicenseActive = xpackLicenseInfo.license.isActive();
const licenseType = xpackLicenseInfo.license.getType();
// License is not valid
if (!isLicenseModeValid) {
return {
isAvailable: false,
showLinks: false,
message: i18n.translate('xpack.indexLifecycleMgmt.checkLicense.errorUnsupportedMessage', {
defaultMessage:
'Your {licenseType} license does not support {pluginName}. Please upgrade your license.',
values: { licenseType, pluginName },
}),
};
}
// License is valid but not active
if (!isLicenseActive) {
return {
isAvailable: false,
showLinks: true,
enableLinks: false,
message: i18n.translate('xpack.indexLifecycleMgmt.checkLicense.errorExpiredMessage', {
defaultMessage:
'You cannot use {pluginName} because your {licenseType} license has expired.',
values: { pluginName, licenseType },
}),
};
}
// License is valid and active
return {
isAvailable: true,
showLinks: true,
enableLinks: true,
};
}

View file

@ -1,21 +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 expect from '@kbn/expect';
import { wrapCustomError } from '../wrap_custom_error';
describe('wrap_custom_error', () => {
describe('#wrapCustomError', () => {
it('should return a Boom object', () => {
const originalError = new Error('I am an error');
const statusCode = 404;
const wrappedError = wrapCustomError(originalError, statusCode);
expect(wrappedError.isBoom).to.be(true);
expect(wrappedError.output.statusCode).to.equal(statusCode);
});
});
});

View file

@ -1,38 +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 expect from '@kbn/expect';
import { wrapEsError } from '../wrap_es_error';
describe('wrap_es_error', () => {
describe('#wrapEsError', () => {
let originalError;
beforeEach(() => {
originalError = new Error('I am an error');
originalError.statusCode = 404;
});
it('should return a Boom object', () => {
const wrappedError = wrapEsError(originalError);
expect(wrappedError.isBoom).to.be(true);
});
it('should return the correct Boom object', () => {
const wrappedError = wrapEsError(originalError);
expect(wrappedError.output.statusCode).to.be(originalError.statusCode);
expect(wrappedError.output.payload.message).to.be(originalError.message);
});
it('should return the correct Boom object with custom message', () => {
const wrappedError = wrapEsError(originalError, { 404: 'No encontrado!' });
expect(wrappedError.output.statusCode).to.be(originalError.statusCode);
expect(wrappedError.output.payload.message).to.be('No encontrado!');
});
});
});

View file

@ -1,19 +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 expect from '@kbn/expect';
import { wrapUnknownError } from '../wrap_unknown_error';
describe('wrap_unknown_error', () => {
describe('#wrapUnknownError', () => {
it('should return a Boom object', () => {
const originalError = new Error('I am an error');
const wrappedError = wrapUnknownError(originalError);
expect(wrappedError.isBoom).to.be(true);
});
});
});

View file

@ -1,9 +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 { wrapCustomError } from './wrap_custom_error';
export { wrapEsError } from './wrap_es_error';
export { wrapUnknownError } from './wrap_unknown_error';

View file

@ -1,18 +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 Boom from 'boom';
/**
* Wraps a custom error into a Boom error response and returns it
*
* @param err Object error
* @param statusCode Error status code
* @return Object Boom error response
*/
export function wrapCustomError(err: any, statusCode: any): any {
return Boom.boomify(err, { statusCode });
}

View file

@ -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 Boom from 'boom';
/**
* Wraps an error thrown by the ES JS client into a Boom error response and returns it
*
* @param err Object Error thrown by ES JS client
* @param statusCodeToMessageMap Object Optional map of HTTP status codes => error messages
* @return Object Boom error response
*/
export function wrapEsError(err: any, statusCodeToMessageMap: any = {}): any {
const statusCode = err.statusCode;
// If no custom message if specified for the error's status code, just
// wrap the error as a Boom error response and return it
if (!statusCodeToMessageMap[statusCode]) {
return Boom.boomify(err, { statusCode });
}
// Otherwise, use the custom message to create a Boom error response and
// return it
const message = statusCodeToMessageMap[statusCode];
return new Boom(message, { statusCode });
}

View file

@ -1,17 +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 Boom from 'boom';
/**
* Wraps an unknown error into a Boom error response and returns it
*
* @param err Object Unknown error
* @return Object Boom error response
*/
export function wrapUnknownError(err: any): any {
return Boom.boomify(err);
}

View file

@ -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 { isEsError } from './is_es_error';

View file

@ -1,69 +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 expect from '@kbn/expect';
import { licensePreRoutingFactory } from '../license_pre_routing_factory';
describe('license_pre_routing_factory', () => {
describe('#reportingFeaturePreRoutingFactory', () => {
let mockServer;
let mockLicenseCheckResults;
beforeEach(() => {
mockServer = {
plugins: {
xpack_main: {
info: {
feature: () => ({
getLicenseCheckResults: () => mockLicenseCheckResults,
}),
},
},
},
};
});
it('only instantiates one instance per server', () => {
const firstInstance = licensePreRoutingFactory(mockServer);
const secondInstance = licensePreRoutingFactory(mockServer);
expect(firstInstance).to.be(secondInstance);
});
describe('isAvailable is false', () => {
beforeEach(() => {
mockLicenseCheckResults = {
isAvailable: false,
};
});
it('replies with 403', () => {
const licensePreRouting = licensePreRoutingFactory(mockServer);
const stubRequest = {};
expect(() => licensePreRouting(stubRequest)).to.throwException(response => {
expect(response).to.be.an(Error);
expect(response.isBoom).to.be(true);
expect(response.output.statusCode).to.be(403);
});
});
});
describe('isAvailable is true', () => {
beforeEach(() => {
mockLicenseCheckResults = {
isAvailable: true,
};
});
it('replies with nothing', () => {
const licensePreRouting = licensePreRoutingFactory(mockServer);
const stubRequest = {};
const response = licensePreRouting(stubRequest);
expect(response).to.be(null);
});
});
});
});

View file

@ -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 { once } from 'lodash';
import { Legacy } from 'kibana';
import { PLUGIN } from '../../../common/constants';
import { wrapCustomError } from '../error_wrappers';
export const licensePreRoutingFactory = once((server: Legacy.Server) => {
const xpackMainPlugin = server.plugins.xpack_main;
// License checking and enable/disable logic
function licensePreRouting() {
const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults();
if (!licenseCheckResults.isAvailable) {
const error = new Error(licenseCheckResults.message);
const statusCode = 403;
throw wrapCustomError(error, statusCode);
}
return null;
}
return licensePreRouting;
});

View file

@ -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 { registerLicenseChecker } from './register_license_checker';

View file

@ -1,23 +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';
// @ts-ignore
import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status';
import { PLUGIN } from '../../../common/constants';
import { checkLicense } from '../check_license';
export function registerLicenseChecker(server: Legacy.Server) {
const xpackMainPlugin = server.plugins.xpack_main as any;
const ilmPlugin = (server.plugins as any).index_lifecycle_management;
mirrorPluginStatus(xpackMainPlugin, ilmPlugin);
xpackMainPlugin.status.once('green', () => {
// Register a function that is called whenever the xpack info changes,
// to re-compute the license check results for this plugin
xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense);
});
}

View file

@ -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 { registerIndexRoutes } from './register_index_routes';

View file

@ -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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function addLifecyclePolicy(
callWithRequest: any,
indexName: string,
policyName: string,
alias: string
) {
const body = {
lifecycle: {
name: policyName,
rollover_alias: alias,
},
};
const params = {
method: 'PUT',
path: `/${encodeURIComponent(indexName)}/_settings`,
body,
};
return callWithRequest('transport.request', params);
}
export function registerAddPolicyRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/index/add',
method: 'POST',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
const { indexName, policyName, alias } = request.payload as any;
try {
const response = await addLifecyclePolicy(callWithRequest, indexName, policyName, alias);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,51 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function removeLifecycle(callWithRequest: any, indexNames: string[]) {
const responses = [];
for (let i = 0; i < indexNames.length; i++) {
const indexName = indexNames[i];
const params = {
method: 'POST',
path: `/${encodeURIComponent(indexName)}/_ilm/remove`,
ignore: [404],
};
responses.push(callWithRequest('transport.request', params));
}
return Promise.all(responses);
}
export function registerRemoveRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/index/remove',
method: 'POST',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
try {
const response = await removeLifecycle(callWithRequest, request.payload.indexNames);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,51 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function retryLifecycle(callWithRequest: any, indexNames: string[]) {
const responses = [];
for (let i = 0; i < indexNames.length; i++) {
const indexName = indexNames[i];
const params = {
method: 'POST',
path: `/${encodeURIComponent(indexName)}/_ilm/retry`,
ignore: [404],
};
responses.push(callWithRequest('transport.request', params));
}
return Promise.all(responses);
}
export function registerRetryRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/index/retry',
method: 'POST',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
try {
const response = await retryLifecycle(callWithRequest, request.payload.indexNames);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,15 +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 const NODE_ATTRS_KEYS_TO_IGNORE: string[] = [
'ml.enabled',
'ml.machine_memory',
'ml.max_open_jobs',
// Used by ML to identify nodes that have transform enabled:
// https://github.com/elastic/elasticsearch/pull/52712/files#diff-225cc2c1291b4c60a8c3412a619094e1R147
'transform.node',
'xpack.installed',
];

View file

@ -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 { registerNodesRoutes } from './register_nodes_routes';

View file

@ -1,61 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
function findMatchingNodes(stats: any, nodeAttrs: string): any {
return Object.entries(stats.nodes).reduce((accum: any[], [nodeId, nodeStats]: [any, any]) => {
const attributes = nodeStats.attributes || {};
for (const [key, value] of Object.entries(attributes)) {
if (`${key}:${value}` === nodeAttrs) {
accum.push({
nodeId,
stats: nodeStats,
});
break;
}
}
return accum;
}, []);
}
async function fetchNodeStats(callWithRequest: any): Promise<any> {
const params = {
format: 'json',
};
return await callWithRequest('nodes.stats', params);
}
export function registerDetailsRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/nodes/{nodeAttrs}/details',
method: 'GET',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
try {
const stats = await fetchNodeStats(callWithRequest);
const response = findMatchingNodes(stats, request.params.nodeAttrs);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,63 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
import { NODE_ATTRS_KEYS_TO_IGNORE } from './constants';
function convertStatsIntoList(stats: any, attributesToBeFiltered: string[]): any {
return Object.entries(stats.nodes).reduce((accum: any, [nodeId, nodeStats]: [any, any]) => {
const attributes = nodeStats.attributes || {};
for (const [key, value] of Object.entries(attributes)) {
if (!attributesToBeFiltered.includes(key)) {
const attributeString = `${key}:${value}`;
accum[attributeString] = accum[attributeString] || [];
accum[attributeString].push(nodeId);
}
}
return accum;
}, {});
}
async function fetchNodeStats(callWithRequest: any): Promise<any> {
const params = {
format: 'json',
};
return await callWithRequest('nodes.stats', params);
}
export function registerListRoute(server: any) {
const config = server.config();
const filteredNodeAttributes = config.get('xpack.ilm.filteredNodeAttributes');
const attributesToBeFiltered = [...NODE_ATTRS_KEYS_TO_IGNORE, ...filteredNodeAttributes];
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/nodes/list',
method: 'GET',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
try {
const stats = await fetchNodeStats(callWithRequest);
const response = convertStatsIntoList(stats, attributesToBeFiltered);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -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 { registerPoliciesRoutes } from './register_policies_routes';

View file

@ -1,52 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function createPolicy(callWithRequest: any, policy: any): Promise<any> {
const body = {
policy: {
phases: policy.phases,
},
};
const params = {
method: 'PUT',
path: `/_ilm/policy/${encodeURIComponent(policy.name)}`,
ignore: [404],
body,
};
return await callWithRequest('transport.request', params);
}
export function registerCreateRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/policies',
method: 'POST',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
try {
const response = await createPolicy(callWithRequest, request.payload);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function deletePolicies(policyNames: string, callWithRequest: any): Promise<any> {
const params = {
method: 'DELETE',
path: `/_ilm/policy/${encodeURIComponent(policyNames)}`,
// we allow 404 since they may have no policies
ignore: [404],
};
return await callWithRequest('transport.request', params);
}
export function registerDeleteRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/policies/{policyNames}',
method: 'DELETE',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
const { policyNames } = request.params;
try {
await deletePolicies(policyNames, callWithRequest);
return {};
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,84 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
function formatPolicies(policiesMap: any): any {
if (policiesMap.status === 404) {
return [];
}
return Object.keys(policiesMap).reduce((accum: any[], lifecycleName: string) => {
const policyEntry = policiesMap[lifecycleName];
accum.push({
...policyEntry,
name: lifecycleName,
});
return accum;
}, []);
}
async function fetchPolicies(callWithRequest: any): Promise<any> {
const params = {
method: 'GET',
path: '/_ilm/policy',
// we allow 404 since they may have no policies
ignore: [404],
};
return await callWithRequest('transport.request', params);
}
async function addLinkedIndices(policiesMap: any, callWithRequest: any) {
if (policiesMap.status === 404) {
return policiesMap;
}
const params = {
method: 'GET',
path: '/*/_ilm/explain',
// we allow 404 since they may have no policies
ignore: [404],
};
const policyExplanation: any = await callWithRequest('transport.request', params);
Object.entries(policyExplanation.indices).forEach(([indexName, { policy }]: [string, any]) => {
if (policy && policiesMap[policy]) {
policiesMap[policy].linkedIndices = policiesMap[policy].linkedIndices || [];
policiesMap[policy].linkedIndices.push(indexName);
}
});
}
export function registerFetchRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/policies',
method: 'GET',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
const { withIndices } = request.query;
try {
const policiesMap = await fetchPolicies(callWithRequest);
if (withIndices) {
await addLinkedIndices(policiesMap, callWithRequest);
}
return formatPolicies(policiesMap);
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -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 { registerTemplatesRoutes } from './register_templates_routes';

View file

@ -1,67 +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 { merge } from 'lodash';
import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function getIndexTemplate(callWithRequest: any, templateName: string): Promise<any> {
const response = await callWithRequest('indices.getTemplate', { name: templateName });
return response[templateName];
}
async function updateIndexTemplate(callWithRequest: any, indexTemplatePatch: any): Promise<any> {
// Fetch existing template
const template = await getIndexTemplate(callWithRequest, indexTemplatePatch.templateName);
merge(template, {
settings: {
index: {
lifecycle: {
name: indexTemplatePatch.policyName,
rollover_alias: indexTemplatePatch.aliasName,
},
},
},
});
const params = {
method: 'PUT',
path: `/_template/${encodeURIComponent(indexTemplatePatch.templateName)}`,
ignore: [404],
body: template,
};
return await callWithRequest('transport.request', params);
}
export function registerAddPolicyRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/template',
method: 'POST',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
try {
const response = await updateIndexTemplate(callWithRequest, request.payload);
return response;
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,48 +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 { callWithRequestFactory } from '../../../lib/call_with_request_factory';
import { isEsError } from '../../../lib/is_es_error';
import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers';
import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
async function fetchTemplate(callWithRequest: any, templateName: string): Promise<any> {
const params = {
method: 'GET',
path: `/_template/${encodeURIComponent(templateName)}`,
// we allow 404 incase the user shutdown security in-between the check and now
ignore: [404],
};
return await callWithRequest('transport.request', params);
}
export function registerGetRoute(server: any) {
const licensePreRouting = licensePreRoutingFactory(server);
server.route({
path: '/api/index_lifecycle_management/templates/{templateName}',
method: 'GET',
handler: async (request: any) => {
const callWithRequest = callWithRequestFactory(server, request);
const templateName = request.params.templateName;
try {
const template = await fetchTemplate(callWithRequest, templateName);
return template[templateName];
} catch (err) {
if (isEsError(err)) {
return wrapEsError(err);
}
return wrapUnknownError(err);
}
},
config: {
pre: [licensePreRouting],
},
});
}

View file

@ -1,17 +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';
export interface LegacySetup {
server: Legacy.Server;
}
export function createShim(server: Legacy.Server): LegacySetup {
return {
server,
};
}

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
// TODO: Remove this once CCR is migrated to the plugins directory.
export function indexManagement(kibana: any) {
return new kibana.Plugin({
id: 'index_management',

View file

@ -24,7 +24,7 @@ import {
// @ts-ignore
import { CRUD_APP_BASE_PATH } from './crud_app/constants';
import { ManagementSetup } from '../../../../../src/plugins/management/public';
import { IndexMgmtSetup } from '../../../../plugins/index_management/public';
import { IndexManagementPluginSetup } from '../../../../plugins/index_management/public';
import { IndexPatternManagementSetup } from '../../../../../src/plugins/index_pattern_management/public';
import { search } from '../../../../../src/plugins/data/public';
// @ts-ignore
@ -35,7 +35,7 @@ import { renderApp } from './application';
export interface RollupPluginSetupDependencies {
home?: HomePublicPluginSetup;
management: ManagementSetup;
indexManagement?: IndexMgmtSetup;
indexManagement?: IndexManagementPluginSetup;
indexPatternManagement: IndexPatternManagementSetup;
}

View file

@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server';
import { IndexMgmtSetup } from '../../../../plugins/index_management/server';
import { IndexManagementPluginSetup } from '../../../../plugins/index_management/server';
import { registerLicenseChecker } from '../../../server/lib/register_license_checker';
import { PLUGIN } from '../common';
import { ServerShim, RouteDependencies } from './types';
@ -44,7 +44,7 @@ export class RollupsServerPlugin implements Plugin<void, void, any, any> {
__LEGACY: ServerShim;
usageCollection?: UsageCollectionSetup;
metrics?: VisTypeTimeseriesSetup;
indexManagement?: IndexMgmtSetup;
indexManagement?: IndexManagementPluginSetup;
}
) {
const elasticsearch = await elasticsearchService.adminClient;

View file

@ -13,13 +13,13 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import sinon from 'sinon';
import { findTestSubject } from '@elastic/eui/lib/test';
import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers';
import { fetchedPolicies, fetchedNodes } from '../../public/np_ready/application/store/actions';
import { indexLifecycleManagementStore } from '../../public/np_ready/application/store';
import { EditPolicy } from '../../public/np_ready/application/sections/edit_policy';
import { init as initHttp } from '../../public/np_ready/application/services/http';
import { init as initUiMetric } from '../../public/np_ready/application/services/ui_metric';
import { init as initNotification } from '../../public/np_ready/application/services/notification';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
import { fetchedPolicies, fetchedNodes } from '../../public/application/store/actions';
import { indexLifecycleManagementStore } from '../../public/application/store';
import { EditPolicy } from '../../public/application/sections/edit_policy';
import { init as initHttp } from '../../public/application/services/http';
import { init as initUiMetric } from '../../public/application/services/ui_metric';
import { init as initNotification } from '../../public/application/services/notification';
import {
positiveNumbersAboveZeroErrorMessage,
positiveNumberRequiredMessage,
@ -33,16 +33,14 @@ import {
policyNameMustBeDifferentErrorMessage,
policyNameAlreadyUsedErrorMessage,
maximumDocumentsRequiredMessage,
} from '../../public/np_ready/application/store/selectors/lifecycle';
} from '../../public/application/store/selectors/lifecycle';
initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path);
initUiMetric(() => () => {});
initUiMetric({ reportUiStats: () => {} });
initNotification({
addDanger: () => {},
});
jest.mock('ui/new_platform');
let server;
let store;
const policy = {

View file

@ -12,17 +12,15 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import sinon from 'sinon';
import { findTestSubject, takeMountedSnapshot } from '@elastic/eui/lib/test';
import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers';
import { fetchedPolicies } from '../../public/np_ready/application/store/actions';
import { indexLifecycleManagementStore } from '../../public/np_ready/application/store';
import { PolicyTable } from '../../public/np_ready/application/sections/policy_table';
import { init as initHttp } from '../../public/np_ready/application/services/http';
import { init as initUiMetric } from '../../public/np_ready/application/services/ui_metric';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
import { fetchedPolicies } from '../../public/application/store/actions';
import { indexLifecycleManagementStore } from '../../public/application/store';
import { PolicyTable } from '../../public/application/sections/policy_table';
import { init as initHttp } from '../../public/application/services/http';
import { init as initUiMetric } from '../../public/application/services/ui_metric';
initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path);
initUiMetric(() => () => {});
jest.mock('ui/new_platform');
initUiMetric({ reportUiStats: () => {} });
let server = null;

View file

@ -8,7 +8,7 @@ import moment from 'moment-timezone';
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
import { mountWithIntl } from '../../../test_utils/enzyme_helpers';
import {
retryLifecycleActionExtension,
removeLifecyclePolicyActionExtension,
@ -16,21 +16,18 @@ import {
ilmBannerExtension,
ilmFilterExtension,
ilmSummaryExtension,
} from '../public/np_ready/extend_index_management';
import { init as initHttp } from '../public/np_ready/application/services/http';
import { init as initUiMetric } from '../public/np_ready/application/services/ui_metric';
} from '../public/extend_index_management';
import { init as initHttp } from '../public/application/services/http';
import { init as initUiMetric } from '../public/application/services/ui_metric';
// We need to init the http with a mock for any tests that depend upon the http service.
// For example, add_lifecycle_confirm_modal makes an API request in its componentDidMount
// lifecycle method. If we don't mock this, CI will fail with "Call retries were exceeded".
initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path);
initUiMetric(() => () => {});
initUiMetric({ reportUiStats: () => {} });
jest.mock('ui/new_platform');
jest.mock('../../../../plugins/index_management/public', async () => {
const { indexManagementMock } = await import(
'../../../../plugins/index_management/public/mocks.ts'
);
jest.mock('../../../plugins/index_management/public', async () => {
const { indexManagementMock } = await import('../../../plugins/index_management/public/mocks.ts');
return indexManagementMock.createSetup();
});

View file

@ -5,12 +5,18 @@
*/
import { i18n } from '@kbn/i18n';
import { LicenseType } from '../../../licensing/common/types';
const basicLicense: LicenseType = 'basic';
export const PLUGIN = {
ID: 'index_lifecycle_management',
minimumLicenseType: basicLicense,
TITLE: i18n.translate('xpack.indexLifecycleMgmt.appTitle', {
defaultMessage: 'Index Lifecycle Policies',
}),
};
export const BASE_PATH = '/management/elasticsearch/index_lifecycle_management/';
export const API_BASE_PATH = '/api/index_lifecycle_management';

View file

@ -0,0 +1,16 @@
{
"id": "indexLifecycleManagement",
"version": "kibana",
"server": true,
"ui": true,
"requiredPlugins": [
"home",
"licensing",
"management"
],
"optionalPlugins": [
"usageCollection",
"indexManagement"
],
"configPath": ["xpack", "ilm"]
}

View file

@ -8,7 +8,7 @@ import React, { useEffect } from 'react';
import { HashRouter, Switch, Route, Redirect } from 'react-router-dom';
import { METRIC_TYPE } from '@kbn/analytics';
import { BASE_PATH } from '../../../common/constants';
import { BASE_PATH } from '../../common/constants';
import { UIM_APP_LOAD } from './constants';
import { EditPolicy } from './sections/edit_policy';
import { PolicyTable } from './sections/policy_table';

View file

@ -0,0 +1,27 @@
/*
* 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 { Provider } from 'react-redux';
import { I18nStart } from 'kibana/public';
import { UnmountCallback } from 'src/core/public';
import { App } from './app';
import { indexLifecycleManagementStore } from './store';
export const renderApp = (element: Element, I18nContext: I18nStart['Context']): UnmountCallback => {
render(
<I18nContext>
<Provider store={indexLifecycleManagementStore()}>
<App />
</Provider>
</I18nContext>,
element
);
return () => unmountComponentAtNode(element);
};

View file

@ -37,8 +37,8 @@ import {
import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public';
import { BASE_PATH } from '../../../../../../../common/constants';
import { getIndexListUri } from '../../../../../../../index_management/public';
import { BASE_PATH } from '../../../../../../common/constants';
import { UIM_EDIT_CLICK } from '../../../../constants';
import { getPolicyPath } from '../../../../services/navigation';
import { flattenPanelTree } from '../../../../services/flatten_panel_tree';
@ -52,6 +52,7 @@ const COLUMNS = {
label: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.nameHeader', {
defaultMessage: 'Name',
}),
width: 200,
},
linkedIndices: {
label: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.linkedIndicesHeader', {
@ -179,7 +180,6 @@ export class PolicyTable extends Component {
return (
/* eslint-disable-next-line @elastic/eui/href-or-on-click */
<EuiLink
className="policyTable__link"
data-test-subj="policyTablePolicyNameLink"
href={getPolicyPath(value)}
onClick={() => trackUiMetric('click', UIM_EDIT_CLICK)}
@ -415,7 +415,7 @@ export class PolicyTable extends Component {
tableContent = <EuiLoadingSpinner size="m" />;
} else if (totalNumberOfPolicies > 0) {
tableContent = (
<EuiTable className="policyTable__horizontalScroll">
<EuiTable>
<EuiScreenReaderOnly>
<caption role="status" aria-relevant="text" aria-live="polite">
<FormattedMessage

Some files were not shown because too many files have changed in this diff Show more