Upgrade assistant - Phase 1 (#25168)

* Get basic scaffolding working

* Wire up cluster checkup data

* Add types for elasticsearch plugin

* Implement basics of checkup tab

* Update style of deprecations

* Add copy and reload button to checkup

* Add filtering by warning level

* Add deprecation logging tab

* Copy updates and cleanup

* Type cleanup

* Move deprecation logging to overview tab

* Make filters togglable

* Move sections into tabs and add support for grouping

* Cleanup and add clearer labels

* Use tables for message grouping

* Cleanup and small fixes

* Allow console to load relative URLs

* Add reindex in console button to reindex tasks

* Merge documentation UI and uiButtons

* Fix tests

* Filter bar tweaks

* Filter out index settings that can't be set

* Fix types

* Add tests for deprecation_logging

* Add tests for reindex templates

* Make KibanaConfig generic

* Simplify integration test

* Finish backend unit tests

* Fixup types

* Fix uiButton updating for reindex items

* Fixed background color stretching

* Pulling tabs out and re-ordering filter buttons

* Making accordions more item-list like

* Turned Healths into Badges

- Couldn’t do the conversion within the cell because it only passed color

* Fix overflow issue

* Optional filter and expand/collapse controls

* Reorganizing

- Added placeholder for moving action button up into accordion header
- Removed repetitive message name outputs
- Slightly better listing of each message when sorting by index
- Only showing number of severity when sorting by index
  - Still need to allow showing all severity levels
- Added indice count when sorting by issue

* Putting `Deprecation logging` in a `EuiDescribedFormGroup`

* Added some stats, empty prompts, and all clear prompt

* Added docs link

* Cleaned up sass files

* Revert changes to fake_deprecations

* Update blacklisted settings

* wip

* Move data fetching and tab control

* Wire up overview summary

* Cleanup docs/uiButtons + move actions to index table

* Add expand/collapse all functionality

* Wire up search box

* Wire up severity indicators

* Fix types

* Round out functional tests

* Fix fake data

* Remove info deprecation level

* Fix extra space on cluster tab control bar

* Cleanup code and localize majority of UI controls

* Change overview tab to steps layout

* Update copy

* Localize overview tab

* Complete localization of checkup tabs

* Make ES version dynamic based on branch

* Add pagination to checkup tabs

* Rename checkup -> assistant

* Cleanup filter and group by bars

* WIP UI unit tests

* Copy tweaks

* Fix i18n formatting issues

* Update tests for copy

* Add tests for remaining UI

* Fix pagination w/ filter changes + table button color

* Small cleanup

* Add reindex button to old index deprecations

* Add shrunken indices setting to copy settings blacklist for #18469

* Add next steps to overview tab + update copy

* Remove usage of migration assistance API

* Use all/critical toggle for filter buttons

* Cloud upgrade copy

* Translate reindex button

* Remove hacked EUI type

* Show incomplete banner on all tabs

* Update copy for waiting for next version

* Review comments

* Update deprecation level type

* Update checkup tab snapshots

* Remove dependencies on types from #25168

* Use types from new global type defs

* Remove 'Reindex in Console' button

* Remove unused variable
This commit is contained in:
Josh Dover 2018-12-18 17:08:06 -06:00 committed by GitHub
parent fcb0a6d1ab
commit 4d295c7923
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 4916 additions and 3 deletions

View file

@ -31,6 +31,7 @@
"xpack.searchProfiler": "x-pack/plugins/searchprofiler",
"xpack.security": "x-pack/plugins/security",
"xpack.spaces": "x-pack/plugins/spaces",
"xpack.upgradeAssistant": "x-pack/plugins/upgrade_assistant",
"xpack.watcher": "x-pack/plugins/watcher"
},
"exclude": [

View file

@ -371,7 +371,7 @@ export interface CallClusterWithRequest {
<T = any>(
request: Request,
endpoint: string,
clientParams: GenericParams,
clientParams: any,
options?: CallClusterOptions
): Promise<T>;
}

View file

@ -29,6 +29,7 @@ import { kueryAutocomplete } from './plugins/kuery_autocomplete';
import { canvas } from './plugins/canvas';
import { infra } from './plugins/infra';
import { rollup } from './plugins/rollup';
import { upgradeAssistant } from './plugins/upgrade_assistant';
module.exports = function (kibana) {
return [
@ -57,5 +58,6 @@ module.exports = function (kibana) {
kueryAutocomplete(kibana),
infra(kibana),
rollup(kibana),
upgradeAssistant(kibana),
];
};

View file

@ -29,6 +29,8 @@ function getInjected(key) {
return 'apm*';
case 'mlEnabled':
return true;
case 'isCloudEnabled':
return false;
default:
throw new Error(`Unexpected config key: ${key}`);
}

View file

@ -95,6 +95,8 @@ declare module '@elastic/eui' {
rel?: string;
target?: string;
type?: string;
hasActiveFilters?: boolean;
numFilters?: number;
};
export const EuiFilterButton: React.SFC<EuiFilterButtonProps>;

View file

@ -0,0 +1,14 @@
/*
* 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 pkg from '../../../package.json';
// Extract version information
const currentVersionNum = pkg.version as string;
const matches = currentVersionNum.match(/^([1-9]+)\.([0-9]+)\.([0-9]+)$/)!;
export const CURRENT_MAJOR_VERSION = matches[1];
export const NEXT_MAJOR_VERSION = (parseInt(CURRENT_MAJOR_VERSION, 10) + 1).toString();

View file

@ -0,0 +1,32 @@
/*
* 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 { Server } from 'hapi';
import Joi from 'joi';
import { resolve } from 'path';
import { initServer } from './server';
export function upgradeAssistant(kibana: any) {
return new kibana.Plugin({
id: 'upgrade_assistant',
require: ['elasticsearch'],
uiExports: {
managementSections: ['plugins/upgrade_assistant'],
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
},
publicDir: resolve(__dirname, 'public'),
config() {
return Joi.object({
enabled: Joi.boolean().default(true),
}).default();
},
init(server: Server) {
// Add server routes and initialize the plugin here
initServer(server);
},
});
}

View file

@ -0,0 +1,4 @@
upgrade-assistant {
flex-grow: 1;
background-color: $euiColorLightestShade;
}

View file

@ -0,0 +1,36 @@
/*
* 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 { EuiPage, EuiPageBody, EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui';
import { FormattedMessage, injectI18nProvider } from '@kbn/i18n/react';
import { NEXT_MAJOR_VERSION } from '../common/version';
import { UpgradeAssistantTabs } from './components/tabs';
export const RootComponentUI: React.StatelessComponent = () => (
<EuiPage restrictWidth data-test-subj="upgradeAssistantRoot">
<EuiPageBody>
<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>
<UpgradeAssistantTabs />
</EuiPageBody>
</EuiPage>
);
export const RootComponent = injectI18nProvider(RootComponentUI);

View file

@ -0,0 +1,2 @@
@import './tabs/checkup/index';
@import './tabs/overview/index';

View file

@ -0,0 +1,49 @@
/*
* 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 { EuiCallOut, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../common/version';
export const LatestMinorBanner: React.StatelessComponent = () => (
<EuiCallOut
title={
<FormattedMessage
id="xpack.upgradeAssistant.tabs.incompleteCallout.calloutTitle"
defaultMessage="Issues list might be incomplete"
/>
}
color="warning"
iconType="help"
>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.tabs.incompleteCallout.calloutBody.calloutDetail"
defaultMessage="The complete list of {breakingChangesDocButton} in Elasticsearch {nextEsVersion}
will be available in the final {currentEsVersion} minor release. When the list
is complete, this warning will go away."
values={{
breakingChangesDocButton: (
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes.html"
target="_blank"
>
<FormattedMessage
id="xpack.upgradeAssistant.tabs.incompleteCallout.calloutBody.breackingChangesDocButtonLabel"
defaultMessage="deprecations and breaking changes"
/>
</EuiLink>
),
nextEsVersion: `${NEXT_MAJOR_VERSION}.x`,
currentEsVersion: `${CURRENT_MAJOR_VERSION}.x`,
}}
/>
</p>
</EuiCallOut>
);

View file

@ -0,0 +1,61 @@
/*
* 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 { mountWithIntl } from 'test_utils/enzyme_helpers';
jest.mock('axios', () => ({
get: jest.fn(),
}));
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));
describe('UpgradeAssistantTabs', () => {
test('renders loading state', async () => {
// @ts-ignore
axios.get.mockReturnValue(
new Promise(resolve => {
/* never resolve */
})
);
const wrapper = mountWithIntl(<UpgradeAssistantTabs />);
// 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({
data: {
cluster: [],
indices: [],
},
});
const wrapper = mountWithIntl(<UpgradeAssistantTabs />);
expect(axios.get).toHaveBeenCalled();
await promisesToResolve();
wrapper.update();
// Should pass down success status to child component
expect(wrapper.find(OverviewTab).prop('loadingState')).toEqual(LoadingState.Success);
});
test('network failure', async () => {
// @ts-ignore
axios.get.mockRejectedValue(new Error(`oh no!`));
const wrapper = mountWithIntl(<UpgradeAssistantTabs />);
await promisesToResolve();
wrapper.update();
// Should pass down error status to child component
expect(wrapper.find(OverviewTab).prop('loadingState')).toEqual(LoadingState.Error);
});
});

View file

@ -0,0 +1,145 @@
/*
* 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 axios from 'axios';
import { findIndex } from 'lodash';
import React from 'react';
import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui';
import { injectI18n } from '@kbn/i18n/react';
import chrome from 'ui/chrome';
import { UpgradeAssistantStatus } from '../../server/lib/es_migration_apis';
import { LatestMinorBanner } from './latest_minor_banner';
import { CheckupTab } from './tabs/checkup';
import { OverviewTab } from './tabs/overview';
import { LoadingState, UpgradeAssistantTabProps } from './types';
interface TabsState {
loadingState: LoadingState;
checkupData?: UpgradeAssistantStatus;
selectedTabIndex: number;
}
export class UpgradeAssistantTabsUI extends React.Component<
ReactIntl.InjectedIntlProps,
TabsState
> {
constructor(props: ReactIntl.InjectedIntlProps) {
super(props);
this.state = {
loadingState: LoadingState.Loading,
selectedTabIndex: 0,
};
}
public componentDidMount() {
this.loadData();
}
public render() {
const { selectedTabIndex } = this.state;
const tabs = this.tabs;
return (
<EuiTabbedContent
tabs={tabs}
onTabClick={this.onTabClick}
selectedTab={tabs[selectedTabIndex]}
/>
);
}
private onTabClick = (selectedTab: EuiTabbedContentTab) => {
const selectedTabIndex = findIndex(this.tabs, { id: selectedTab.id });
if (selectedTabIndex === -1) {
throw new Error(`Clicked tab did not exist in tabs array`);
}
this.setSelectedTabIndex(selectedTabIndex);
};
private setSelectedTabIndex = (selectedTabIndex: number) => {
this.setState({ selectedTabIndex });
};
private loadData = async () => {
try {
this.setState({ loadingState: LoadingState.Loading });
const resp = await axios.get(chrome.addBasePath('/api/upgrade_assistant/status'));
this.setState({
loadingState: LoadingState.Success,
checkupData: resp.data,
});
} catch (e) {
this.setState({ loadingState: LoadingState.Error });
}
};
private get tabs() {
const { intl } = this.props;
const { loadingState, checkupData } = this.state;
const commonProps: UpgradeAssistantTabProps = {
loadingState,
refreshCheckupData: this.loadData,
setSelectedTabIndex: this.setSelectedTabIndex,
// Remove this in last minor of the current major (eg. 6.7)
alertBanner: <LatestMinorBanner />,
};
return [
{
id: 'overview',
name: intl.formatMessage({
id: 'xpack.upgradeAssistant.overviewTab.overviewTabTitle',
defaultMessage: 'Overview',
}),
content: <OverviewTab checkupData={checkupData} {...commonProps} />,
},
{
id: 'cluster',
name: intl.formatMessage({
id: 'xpack.upgradeAssistant.checkupTab.clusterTabLabel',
defaultMessage: 'Cluster',
}),
content: (
<CheckupTab
key="cluster"
deprecations={checkupData ? checkupData.cluster : undefined}
checkupLabel={intl.formatMessage({
id: 'xpack.upgradeAssistant.tabs.checkupTab.clusterLabel',
defaultMessage: 'cluster',
})}
{...commonProps}
/>
),
},
{
id: 'indices',
name: intl.formatMessage({
id: 'xpack.upgradeAssistant.checkupTab.indicesTabLabel',
defaultMessage: 'Indices',
}),
content: (
<CheckupTab
key="indices"
deprecations={checkupData ? checkupData.indices : undefined}
checkupLabel={intl.formatMessage({
id: 'xpack.upgradeAssistant.checkupTab.indexLabel',
defaultMessage: 'index',
})}
showBackupWarning
{...commonProps}
/>
),
},
];
}
}
export const UpgradeAssistantTabs = injectI18n(UpgradeAssistantTabsUI);

View file

@ -0,0 +1,870 @@
{
"cluster": [
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 3",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 3 4",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 3 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 1 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 1 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
},
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead 0 1 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings 0 1 2 3 4 5",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
}
],
"nodes": [],
"indices": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]",
"index": ".monitoring-es-6-2018.11.07"
},
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: tweet, field: liked]]",
"index": "twitter"
},
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]",
"index": ".kibana"
},
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]",
"index": ".watcher-history-6-2018.11.07"
},
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: doc, field: snapshot]]",
"index": ".monitoring-kibana-6-2018.11.07"
},
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: tweet, field: liked]]",
"index": "twitter2"
},
{
"index": "twitter",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"actions": [
{
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter.json"
}
],
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html"
},
{
"index": ".triggered_watches",
"level": "critical",
"message": "This index must be upgraded in order to upgrade the Elastic Stack.",
"details": "Upgrading is irreversible, so always back up your index before proceeding.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html"
},
{
"index": ".reindex-status",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"actions": [
{
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2F.reindex-status.json"
}
],
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html"
},
{
"index": "twitter2",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"actions": [
{
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter2.json"
}
],
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html"
},
{
"index": ".watches",
"level": "critical",
"message": "This index must be upgraded in order to upgrade the Elastic Stack.",
"details": "Upgrading is irreversible, so always back up your index before proceeding.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html"
}
]
}

View file

@ -0,0 +1,518 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CheckupTab render with deprecations 1`] = `
<React.Fragment>
<EuiSpacer
size="l"
/>
<EuiText
grow={false}
size="m"
>
<p>
<FormattedMessage
defaultMessage="These {strongCheckupLabel} issues need your attention. Resolve them before upgrading to Elasticsearch {nextEsVersion}."
id="xpack.upgradeAssistant.checkupTab.tabDetail"
values={
Object {
"nextEsVersion": "8.x",
"strongCheckupLabel": <strong>
index
</strong>,
}
}
/>
</p>
</EuiText>
<EuiSpacer
size="l"
/>
<React.Fragment>
<EuiCallOut
color="warning"
iconType="help"
size="m"
title={
<FormattedMessage
defaultMessage="Back up your indices now"
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutTitle"
values={Object {}}
/>
}
>
<p>
<FormattedMessage
defaultMessage="Back up your data using the {snapshotRestoreDocsButton}."
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail"
values={
Object {
"snapshotRestoreDocsButton": <EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="snapshot and restore APIs"
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiCallOut>
<EuiSpacer
size="l"
/>
</React.Fragment>
<EuiPageContent
panelPaddingSize="l"
>
<EuiPageContentBody>
<React.Fragment>
<InjectIntl(Component)
allDeprecations={
Array [
Object {
"details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]",
"index": ".monitoring-es-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: tweet, field: liked]]",
"index": "twitter",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]",
"index": ".kibana",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]",
"index": ".watcher-history-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: doc, field: snapshot]]",
"index": ".monitoring-kibana-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: tweet, field: liked]]",
"index": "twitter2",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"actions": Array [
Object {
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter.json",
},
],
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"index": "twitter",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html",
},
Object {
"details": "Upgrading is irreversible, so always back up your index before proceeding.",
"index": ".triggered_watches",
"level": "critical",
"message": "This index must be upgraded in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html",
},
Object {
"actions": Array [
Object {
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2F.reindex-status.json",
},
],
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"index": ".reindex-status",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html",
},
Object {
"actions": Array [
Object {
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter2.json",
},
],
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"index": "twitter2",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html",
},
Object {
"details": "Upgrading is irreversible, so always back up your index before proceeding.",
"index": ".watches",
"level": "critical",
"message": "This index must be upgraded in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html",
},
]
}
availableGroupByOptions={
Array [
"message",
"index",
]
}
currentFilter="all"
currentGroupBy="message"
loadData={[MockFunction]}
loadingState={1}
onFilterChange={[Function]}
onGroupByChange={[Function]}
onSearchChange={[Function]}
search=""
/>
<EuiSpacer
size="l"
/>
<GroupedDeprecations
allDeprecations={
Array [
Object {
"details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]",
"index": ".monitoring-es-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: tweet, field: liked]]",
"index": "twitter",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]",
"index": ".kibana",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]",
"index": ".watcher-history-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: doc, field: snapshot]]",
"index": ".monitoring-kibana-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: tweet, field: liked]]",
"index": "twitter2",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"actions": Array [
Object {
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter.json",
},
],
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"index": "twitter",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html",
},
Object {
"details": "Upgrading is irreversible, so always back up your index before proceeding.",
"index": ".triggered_watches",
"level": "critical",
"message": "This index must be upgraded in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html",
},
Object {
"actions": Array [
Object {
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2F.reindex-status.json",
},
],
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"index": ".reindex-status",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html",
},
Object {
"actions": Array [
Object {
"label": "Reindex in Console",
"url": "/app/kibana#/dev_tools/console?load_from=%2Fapi%2Fupgrade_assistant%2Freindex%2Fconsole_template%2Ftwitter2.json",
},
],
"details": "Reindexing is irreversible, so always back up your index before proceeding.",
"index": "twitter2",
"level": "critical",
"message": "This index must be reindexed in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/reindex-upgrade.html",
},
Object {
"details": "Upgrading is irreversible, so always back up your index before proceeding.",
"index": ".watches",
"level": "critical",
"message": "This index must be upgraded in order to upgrade the Elastic Stack.",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/migration-api-upgrade.html",
},
]
}
currentFilter="all"
currentGroupBy="message"
search=""
/>
</React.Fragment>
</EuiPageContentBody>
</EuiPageContent>
</React.Fragment>
`;
exports[`CheckupTab render with error 1`] = `
<React.Fragment>
<EuiSpacer
size="l"
/>
<EuiText
grow={false}
size="m"
>
<p>
<FormattedMessage
defaultMessage="These {strongCheckupLabel} issues need your attention. Resolve them before upgrading to Elasticsearch {nextEsVersion}."
id="xpack.upgradeAssistant.checkupTab.tabDetail"
values={
Object {
"nextEsVersion": "8.x",
"strongCheckupLabel": <strong>
index
</strong>,
}
}
/>
</p>
</EuiText>
<EuiSpacer
size="l"
/>
<React.Fragment>
<EuiCallOut
color="warning"
iconType="help"
size="m"
title={
<FormattedMessage
defaultMessage="Back up your indices now"
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutTitle"
values={Object {}}
/>
}
>
<p>
<FormattedMessage
defaultMessage="Back up your data using the {snapshotRestoreDocsButton}."
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail"
values={
Object {
"snapshotRestoreDocsButton": <EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="snapshot and restore APIs"
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiCallOut>
<EuiSpacer
size="l"
/>
</React.Fragment>
<EuiPageContent
panelPaddingSize="l"
>
<EuiPageContentBody>
<EuiCallOut
color="danger"
iconType="cross"
size="m"
title={
<FormattedMessage
defaultMessage="A network error occurred while retrieving the checkup results."
id="xpack.upgradeAssistant.checkupTab.errorCallout.calloutTitle"
values={Object {}}
/>
}
/>
</EuiPageContentBody>
</EuiPageContent>
</React.Fragment>
`;
exports[`CheckupTab render without deprecations 1`] = `
<React.Fragment>
<EuiSpacer
size="l"
/>
<EuiText
grow={false}
size="m"
>
<p>
<FormattedMessage
defaultMessage="These {strongCheckupLabel} issues need your attention. Resolve them before upgrading to Elasticsearch {nextEsVersion}."
id="xpack.upgradeAssistant.checkupTab.tabDetail"
values={
Object {
"nextEsVersion": "8.x",
"strongCheckupLabel": <strong>
index
</strong>,
}
}
/>
</p>
</EuiText>
<EuiSpacer
size="l"
/>
<React.Fragment>
<EuiCallOut
color="warning"
iconType="help"
size="m"
title={
<FormattedMessage
defaultMessage="Back up your indices now"
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutTitle"
values={Object {}}
/>
}
>
<p>
<FormattedMessage
defaultMessage="Back up your data using the {snapshotRestoreDocsButton}."
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail"
values={
Object {
"snapshotRestoreDocsButton": <EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="snapshot and restore APIs"
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiCallOut>
<EuiSpacer
size="l"
/>
</React.Fragment>
<EuiPageContent
panelPaddingSize="l"
>
<EuiPageContentBody>
<EuiEmptyPrompt
body={
<React.Fragment>
<p
data-test-subj="upgradeAssistantIssueSummary"
>
<FormattedMessage
defaultMessage="You have no {strongCheckupLabel} issues."
id="xpack.upgradeAssistant.checkupTab.noIssues.noIssuesLabel"
values={
Object {
"strongCheckupLabel": <strong>
index
</strong>,
}
}
/>
</p>
<p>
<FormattedMessage
defaultMessage="Check the {overviewTabButton} for next steps."
id="xpack.upgradeAssistant.checkupTab.noIssues.nextStepsDetail"
values={
Object {
"overviewTabButton": <EuiLink
color="primary"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="Overview tab"
id="xpack.upgradeAssistant.checkupTab.noIssues.nextStepsDetail.overviewTabButtonLabel"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</React.Fragment>
}
iconColor="subdued"
iconType="faceHappy"
title={
<h2>
<FormattedMessage
defaultMessage="All clear!"
id="xpack.upgradeAssistant.checkupTab.noIssues.noIssuesTitle"
values={Object {}}
/>
</h2>
}
/>
</EuiPageContentBody>
</EuiPageContent>
</React.Fragment>
`;

View file

@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FilterBar renders 1`] = `
<EuiFlexItem
component="div"
grow={false}
>
<EuiFilterGroup>
<EuiFilterButton
color="text"
grow={false}
hasActiveFilters={false}
iconSide="right"
key="all"
numFilters={2}
onClick={[Function]}
type="button"
>
all
</EuiFilterButton>
<EuiFilterButton
color="text"
grow={false}
hasActiveFilters={true}
iconSide="right"
key="critical"
numFilters={2}
onClick={[Function]}
type="button"
>
critical
</EuiFilterButton>
</EuiFilterGroup>
</EuiFlexItem>
`;

View file

@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GroupByBar renders 1`] = `
<EuiFlexItem
component="div"
grow={false}
>
<EuiFilterGroup>
<EuiFilterButton
color="text"
grow={false}
hasActiveFilters={true}
iconSide="right"
key="message"
onClick={[Function]}
type="button"
>
by issue
</EuiFilterButton>
<EuiFilterButton
color="text"
grow={false}
hasActiveFilters={false}
iconSide="right"
key="index"
onClick={[Function]}
type="button"
>
by index
</EuiFilterButton>
</EuiFilterGroup>
</EuiFlexItem>
`;

View file

@ -0,0 +1 @@
@import './deprecations/index';

View file

@ -0,0 +1,50 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { LoadingState } from '../../types';
import AssistanceData from '../__fixtures__/checkup_api_response.json';
import { CheckupTab } from './checkup_tab';
const defaultProps = {
checkupLabel: 'index',
deprecations: AssistanceData.indices,
showBackupWarning: true,
refreshCheckupData: jest.fn(),
loadingState: LoadingState.Success,
setSelectedTabIndex: jest.fn(),
};
/**
* Mostly a dumb container with copy, test the three main states.
*/
describe('CheckupTab', () => {
test('render with deprecations', () => {
expect(shallow(<CheckupTab {...defaultProps} />)).toMatchSnapshot();
});
test('render without deprecations', () => {
expect(
shallow(
<CheckupTab
{...{ ...defaultProps, deprecations: undefined, loadingState: LoadingState.Loading }}
/>
)
).toMatchSnapshot();
});
test('render with error', () => {
expect(
shallow(
<CheckupTab
{...{ ...defaultProps, deprecations: undefined, loadingState: LoadingState.Error }}
/>
)
).toMatchSnapshot();
});
});

View file

@ -0,0 +1,247 @@
/*
* 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 { find } from 'lodash';
import React, { Fragment } from 'react';
import {
// @ts-ignore
EuiAccordion,
EuiCallOut,
EuiEmptyPrompt,
EuiLink,
EuiPageContent,
EuiPageContentBody,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { NEXT_MAJOR_VERSION } from '../../../../common/version';
import {
GroupByOption,
LevelFilterOption,
LoadingState,
UpgradeAssistantTabComponent,
UpgradeAssistantTabProps,
} from '../../types';
import { CheckupControls } from './controls';
import { GroupedDeprecations } from './deprecations/grouped';
interface CheckupTabProps extends UpgradeAssistantTabProps {
checkupLabel: string;
showBackupWarning?: boolean;
}
interface CheckupTabState {
currentFilter: LevelFilterOption;
search: string;
currentGroupBy: GroupByOption;
}
/**
* Displays a list of deprecations that filterable and groupable. Can be used for cluster,
* nodes, or indices checkups.
*/
export class CheckupTab extends UpgradeAssistantTabComponent<CheckupTabProps, CheckupTabState> {
constructor(props: CheckupTabProps) {
super(props);
this.state = {
// initialize to all filters
currentFilter: LevelFilterOption.all,
search: '',
currentGroupBy: GroupByOption.message,
};
}
public render() {
const {
alertBanner,
checkupLabel,
deprecations,
loadingState,
refreshCheckupData,
setSelectedTabIndex,
showBackupWarning = false,
} = this.props;
const { currentFilter, search, currentGroupBy } = this.state;
return (
<Fragment>
<EuiSpacer />
<EuiText grow={false}>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.tabDetail"
defaultMessage="These {strongCheckupLabel} issues need your attention. Resolve them before upgrading to Elasticsearch {nextEsVersion}."
values={{
strongCheckupLabel: <strong>{checkupLabel}</strong>,
nextEsVersion: `${NEXT_MAJOR_VERSION}.x`,
}}
/>
</p>
</EuiText>
<EuiSpacer />
{alertBanner && (
<Fragment>
{alertBanner}
<EuiSpacer />
</Fragment>
)}
{showBackupWarning && (
<Fragment>
<EuiCallOut
title={
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutTitle"
defaultMessage="Back up your indices now"
/>
}
color="warning"
iconType="help"
>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail"
defaultMessage="Back up your data using the {snapshotRestoreDocsButton}."
values={{
snapshotRestoreDocsButton: (
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"
target="_blank"
>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel"
defaultMessage="snapshot and restore APIs"
/>
</EuiLink>
),
}}
/>
</p>
</EuiCallOut>
<EuiSpacer />
</Fragment>
)}
<EuiPageContent>
<EuiPageContentBody>
{loadingState === LoadingState.Error ? (
<EuiCallOut
title={
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.errorCallout.calloutTitle"
defaultMessage="A network error occurred while retrieving the checkup results."
/>
}
color="danger"
iconType="cross"
/>
) : deprecations && deprecations.length > 0 ? (
<Fragment>
<CheckupControls
allDeprecations={deprecations}
loadingState={loadingState}
loadData={refreshCheckupData}
currentFilter={currentFilter}
onFilterChange={this.changeFilter}
search={search}
onSearchChange={this.changeSearch}
availableGroupByOptions={this.availableGroupByOptions()}
currentGroupBy={currentGroupBy}
onGroupByChange={this.changeGroupBy}
/>
<EuiSpacer />
{this.renderCheckupData()}
</Fragment>
) : (
<EuiEmptyPrompt
iconType="faceHappy"
title={
<h2>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.noIssues.noIssuesTitle"
defaultMessage="All clear!"
/>
</h2>
}
body={
<Fragment>
<p data-test-subj="upgradeAssistantIssueSummary">
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.noIssues.noIssuesLabel"
defaultMessage="You have no {strongCheckupLabel} issues."
values={{
strongCheckupLabel: <strong>{checkupLabel}</strong>,
}}
/>
</p>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.noIssues.nextStepsDetail"
defaultMessage="Check the {overviewTabButton} for next steps."
values={{
overviewTabButton: (
<EuiLink onClick={() => setSelectedTabIndex(0)}>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.noIssues.nextStepsDetail.overviewTabButtonLabel"
defaultMessage="Overview tab"
/>
</EuiLink>
),
}}
/>
</p>
</Fragment>
}
/>
)}
</EuiPageContentBody>
</EuiPageContent>
</Fragment>
);
}
private changeFilter = (filter: LevelFilterOption) => {
this.setState({ currentFilter: filter });
};
private changeSearch = (search: string) => {
this.setState({ search });
};
private changeGroupBy = (groupBy: GroupByOption) => {
this.setState({ currentGroupBy: groupBy });
};
private availableGroupByOptions() {
const { deprecations } = this.props;
if (!deprecations) {
return [];
}
return Object.keys(GroupByOption).filter(opt => find(deprecations, opt)) as GroupByOption[];
}
private renderCheckupData() {
const { deprecations } = this.props;
const { currentFilter, currentGroupBy, search } = this.state;
return (
<GroupedDeprecations
currentGroupBy={currentGroupBy}
currentFilter={currentFilter}
search={search}
allDeprecations={deprecations}
/>
);
}
}

View file

@ -0,0 +1,22 @@
/*
* 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 { IconColor } from '@elastic/eui';
import { invert } from 'lodash';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
export const LEVEL_MAP: { [level: string]: number } = {
warning: 0,
critical: 1,
};
export const REVERSE_LEVEL_MAP: { [idx: number]: DeprecationInfo['level'] } = invert(LEVEL_MAP);
export const COLOR_MAP: { [level: string]: IconColor } = {
warning: 'default',
critical: 'danger',
};

View file

@ -0,0 +1,74 @@
/*
* 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, { StatelessComponent } from 'react';
import { EuiButton, EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
import { GroupByOption, LevelFilterOption, LoadingState } from '../../types';
import { FilterBar } from './filter_bar';
import { GroupByBar } from './group_by_bar';
interface CheckupControlsProps extends ReactIntl.InjectedIntlProps {
allDeprecations?: DeprecationInfo[];
loadingState: LoadingState;
loadData: () => void;
currentFilter: LevelFilterOption;
onFilterChange: (filter: LevelFilterOption) => void;
search: string;
onSearchChange: (filter: string) => void;
availableGroupByOptions: GroupByOption[];
currentGroupBy: GroupByOption;
onGroupByChange: (groupBy: GroupByOption) => void;
}
export const CheckupControlsUI: StatelessComponent<CheckupControlsProps> = ({
allDeprecations,
loadingState,
loadData,
currentFilter,
onFilterChange,
search,
onSearchChange,
availableGroupByOptions,
currentGroupBy,
onGroupByChange,
intl,
}) => (
<EuiFlexGroup alignItems="center" wrap={true} responsive={false}>
<EuiFlexItem grow={true}>
<EuiFieldSearch
placeholder={intl.formatMessage({
id: 'xpack.upgradeAssistant.checkupTab.controls.searchBarPlaceholder',
defaultMessage: 'Filter',
})}
value={search}
onChange={e => onSearchChange(e.target.value)}
/>
</EuiFlexItem>
{/* These two components provide their own EuiFlexItem wrappers */}
<FilterBar {...{ allDeprecations, currentFilter, onFilterChange }} />
<GroupByBar {...{ availableGroupByOptions, currentGroupBy, onGroupByChange }} />
<EuiFlexItem grow={false}>
<EuiButton
fill
onClick={loadData}
iconType="refresh"
isLoading={loadingState === LoadingState.Loading}
>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.controls.refreshButtonLabel"
defaultMessage="Refresh"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
);
export const CheckupControls = injectI18n(CheckupControlsUI);

View file

@ -0,0 +1,4 @@
.upgDeprecationCell {
overflow: hidden;
padding: $euiSize 0 0 $euiSizeL;
}

View file

@ -0,0 +1,20 @@
@import './cell';
.upgDeprecations {
// Pull the container through the padding of EuiPageContent
margin-left: -$euiSizeL;
margin-right: -$euiSizeL;
}
.upgDeprecations__item {
padding: $euiSize $euiSizeL;
border-top: $euiBorderThin;
&:last-of-type {
margin-bottom: -$euiSizeL;
}
}
.upgDeprecations__itemName {
font-weight: $euiFontWeightMedium;
}

View file

@ -0,0 +1,95 @@
/*
* 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, { ReactNode, StatelessComponent } from 'react';
import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
interface DeprecationCellProps {
items?: Array<{ title?: string; body: string }>;
docUrl?: string;
headline?: string;
healthColor?: string;
actions?: Array<{
label: string;
url: string;
}>;
children?: ReactNode;
}
/**
* Used to display a deprecation with links to docs, a health indicator, and other descriptive information.
*/
export const DeprecationCell: StatelessComponent<DeprecationCellProps> = ({
headline,
healthColor,
actions,
docUrl,
items = [],
children,
}) => (
<div className="upgDeprecationCell">
<EuiFlexGroup responsive={false} wrap alignItems="baseline">
{healthColor && (
<EuiFlexItem grow={false}>
<EuiIcon type="dot" color={healthColor} />
</EuiFlexItem>
)}
<EuiFlexItem grow>
{headline && (
<EuiTitle size="xxs">
<h2>{headline}</h2>
</EuiTitle>
)}
{docUrl && (
<div>
<EuiLink href={docUrl} target="_blank">
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.deprecations.documentationButtonLabel"
defaultMessage="Documentation"
/>
</EuiLink>
<EuiSpacer size="s" />
</div>
)}
{items.map(item => (
<div key={item.title || item.body}>
<EuiText>
{item.title && <h6>{item.title}</h6>}
<p>{item.body}</p>
</EuiText>
</div>
))}
</EuiFlexItem>
{actions &&
actions.map(button => (
<EuiFlexItem key={button.url} grow={false}>
<EuiButton size="s" href={button.url} target="_blank">
{button.label}
</EuiButton>
</EuiFlexItem>
))}
</EuiFlexGroup>
<EuiSpacer size="s" />
{children}
</div>
);

View file

@ -0,0 +1,41 @@
/*
* 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, { Fragment, StatelessComponent } from 'react';
import { EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis';
export const DeprecationCountSummary: StatelessComponent<{
deprecations: EnrichedDeprecationInfo[];
allDeprecations: EnrichedDeprecationInfo[];
}> = ({ deprecations, allDeprecations }) => (
<EuiText size="s">
{allDeprecations.length ? (
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.numDeprecationsShownLabel"
defaultMessage="Showing {numShown} of {total}"
values={{ numShown: deprecations.length, total: allDeprecations.length }}
/>
) : (
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.noDeprecationsLabel"
defaultMessage="No deprecations"
/>
)}
{deprecations.length !== allDeprecations.length && (
<Fragment>
{'. '}
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.changeFiltersShowMoreLabel"
description="Explains how to show all deprecations if there are more available."
defaultMessage="Change filter to show more."
/>
</Fragment>
)}
</EuiText>
);

View file

@ -0,0 +1,190 @@
/*
* 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 { range } from 'lodash';
import React from 'react';
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/lib/es_migration_apis';
import { GroupByOption, LevelFilterOption } from '../../../types';
import { DeprecationAccordion, filterDeps, GroupedDeprecations } from './grouped';
describe('filterDeps', () => {
test('filters on levels', () => {
const fd = filterDeps(LevelFilterOption.critical);
expect(fd({ level: 'critical' } as DeprecationInfo)).toBe(true);
expect(fd({ level: 'warning' } as DeprecationInfo)).toBe(false);
});
test('filters on search', () => {
const fd = filterDeps(LevelFilterOption.critical, 'wow');
expect(fd({ level: 'critical', message: 'the wow error' } as DeprecationInfo)).toBe(true);
expect(fd({ level: 'critical', message: 'other error' } as DeprecationInfo)).toBe(false);
});
});
describe('GroupedDeprecations', () => {
const defaultProps = {
currentFilter: LevelFilterOption.all,
search: '',
currentGroupBy: GroupByOption.message,
allDeprecations: [
{ message: 'Cluster error 1', url: '', level: 'warning' },
{ message: 'Cluster error 2', url: '', level: 'critical' },
] as EnrichedDeprecationInfo[],
};
describe('expand + collapse all', () => {
const expectNumOpen = (wrapper: any, numExpected: number) =>
expect(wrapper.find('div.euiAccordion-isOpen')).toHaveLength(numExpected);
test('clicking opens and closes panels', () => {
const wrapper = mountWithIntl(<GroupedDeprecations {...defaultProps} />);
expectNumOpen(wrapper, 0);
// Test expand all
wrapper.find('button[data-test-subj="expandAll"]').simulate('click');
expectNumOpen(wrapper, 2);
// Test collapse all
wrapper.find('button[data-test-subj="collapseAll"]').simulate('click');
expectNumOpen(wrapper, 0);
});
test('clicking overrides current state when some are open', () => {
const wrapper = mountWithIntl(<GroupedDeprecations {...defaultProps} />);
// Open a single deprecation
wrapper
.find('button.euiAccordion__button')
.first()
.simulate('click');
expectNumOpen(wrapper, 1);
// Test expand all
wrapper.find('button[data-test-subj="expandAll"]').simulate('click');
expectNumOpen(wrapper, 2);
// Close a single deprecation
wrapper
.find('button.euiAccordion__button')
.first()
.simulate('click');
expectNumOpen(wrapper, 1);
// Test collapse all
wrapper.find('button[data-test-subj="collapseAll"]').simulate('click');
expectNumOpen(wrapper, 0);
});
});
describe('pagination', () => {
const paginationProps = {
...defaultProps,
allDeprecations: range(0, 40).map(i => ({
message: `Message ${i}`,
level: 'warning',
})) as DeprecationInfo[],
};
test('it only displays 25 items', () => {
const wrapper = shallowWithIntl(<GroupedDeprecations {...paginationProps} />);
expect(wrapper.find(DeprecationAccordion)).toHaveLength(25);
});
test('it displays pagination', () => {
const wrapper = shallowWithIntl(<GroupedDeprecations {...paginationProps} />);
expect(wrapper.find(EuiPagination).exists()).toBe(true);
});
test('shows next page on click', () => {
const wrapper = mountWithIntl(<GroupedDeprecations {...paginationProps} />);
wrapper.find('button[data-test-subj="pagination-button-next"]').simulate('click');
expect(wrapper.find(DeprecationAccordion)).toHaveLength(15); // 40 total - 25 first page = 15 second page
});
});
describe('grouping', () => {
test('group by message', () => {
const wrapper = shallowWithIntl(
<GroupedDeprecations
{...defaultProps}
currentGroupBy={GroupByOption.message}
allDeprecations={[
{ message: 'Cluster error 1', url: '', level: 'warning' },
{ message: 'Cluster error 2', url: '', level: 'warning' },
{ message: 'Cluster error 2', url: '', level: 'warning' },
{ message: 'Cluster error 2', url: '', level: 'warning' },
]}
/>
);
// Only 2 groups should exist b/c there are only 2 unique messages
expect(wrapper.find(DeprecationAccordion)).toHaveLength(2);
});
test('group by index', () => {
const wrapper = shallowWithIntl(
<GroupedDeprecations
{...defaultProps}
currentGroupBy={GroupByOption.index}
allDeprecations={[
{
message: 'Cluster error 1',
url: '',
level: 'warning',
index: 'index1',
},
{
message: 'Cluster error 2',
url: '',
level: 'warning',
index: 'index1',
},
{
message: 'Cluster error 2',
url: '',
level: 'warning',
index: 'index2',
},
{
message: 'Cluster error 2',
url: '',
level: 'warning',
index: 'index3',
},
]}
/>
);
// Only 3 groups should exist b/c there are only 3 unique indexes
expect(wrapper.find(DeprecationAccordion)).toHaveLength(3);
});
});
});
describe('DeprecationAccordion', () => {
const defaultProps = {
id: 'x',
title: 'Issue 1',
currentGroupBy: GroupByOption.message,
forceExpand: false,
deprecations: [{ index: 'index1' }, { index: 'index2' }] as EnrichedDeprecationInfo[],
};
test('shows indices count badge', () => {
const wrapper = mountWithIntl(<DeprecationAccordion {...defaultProps} />);
expect(
wrapper
.find(EuiBadge)
.find('[data-test-subj="indexCount"]')
.text()
).toEqual('2');
});
});

View file

@ -0,0 +1,256 @@
/*
* 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 { groupBy } from 'lodash';
import React, { Fragment, StatelessComponent } from 'react';
import {
EuiAccordion,
EuiBadge,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiPagination,
EuiSpacer,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis';
import { GroupByOption, LevelFilterOption } from '../../../types';
import { DeprecationCountSummary } from './count_summary';
import { DeprecationHealth } from './health';
import { DeprecationList } from './list';
// exported only for testing
export const filterDeps = (level: LevelFilterOption, search: string = '') => {
const conditions: Array<(dep: DeprecationInfo) => boolean> = [];
if (level !== LevelFilterOption.all) {
conditions.push((dep: DeprecationInfo) => dep.level === level);
}
if (search.length > 0) {
// Change everything to lower case for a case-insensitive comparison
conditions.push(dep => {
try {
const searchReg = new RegExp(search.toLowerCase());
return Boolean(
dep.message.toLowerCase().match(searchReg) ||
(dep.details && dep.details.match(searchReg))
);
} catch (e) {
// ignore any regexp errors.
return true;
}
});
}
// Return true if every condition function returns true (boolean AND)
return (dep: DeprecationInfo) => conditions.map(c => c(dep)).every(t => t);
};
/**
* A single accordion item for a grouped deprecation item.
*/
export const DeprecationAccordion: StatelessComponent<{
id: string;
deprecations: EnrichedDeprecationInfo[];
title: string;
currentGroupBy: GroupByOption;
forceExpand: boolean;
}> = ({ id, deprecations, title, currentGroupBy, forceExpand }) => {
const hasIndices = Boolean(
currentGroupBy === GroupByOption.message && deprecations.filter(d => d.index).length
);
const numIndices = hasIndices ? deprecations.length : null;
return (
<EuiAccordion
id={id}
className="upgDeprecations__item"
initialIsOpen={forceExpand}
buttonContent={<span className="upgDeprecations__itemName">{title}</span>}
extraAction={
<div>
{hasIndices && (
<Fragment>
<EuiBadge color="hollow">
<span data-test-subj="indexCount">{numIndices}</span>{' '}
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.indicesBadgeLabel"
defaultMessage="{numIndices, plural, one {index} other {indices}}"
values={{ numIndices }}
/>
</EuiBadge>
&emsp;
</Fragment>
)}
<DeprecationHealth
single={currentGroupBy === GroupByOption.message}
deprecations={deprecations}
/>
</div>
}
>
<DeprecationList deprecations={deprecations} currentGroupBy={currentGroupBy} />
</EuiAccordion>
);
};
interface GroupedDeprecationsProps {
currentFilter: LevelFilterOption;
search: string;
currentGroupBy: GroupByOption;
allDeprecations?: EnrichedDeprecationInfo[];
}
interface GroupedDeprecationsState {
forceExpand: true | false | null;
expandNumber: number;
currentPage: number;
}
const PER_PAGE = 25;
/**
* Collection of calculated fields based on props, extracted for reuse in
* `render` and `getDerivedStateFromProps`.
*/
const CalcFields = {
filteredDeprecations(props: GroupedDeprecationsProps) {
const { allDeprecations = [], currentFilter, search } = props;
return allDeprecations.filter(filterDeps(currentFilter, search));
},
groups(props: GroupedDeprecationsProps) {
const { currentGroupBy } = props;
return groupBy(CalcFields.filteredDeprecations(props), currentGroupBy);
},
numPages(props: GroupedDeprecationsProps) {
return Math.ceil(Object.keys(CalcFields.groups(props)).length / PER_PAGE);
},
};
/**
* Displays groups of deprecation messages in an accordion.
*/
export class GroupedDeprecations extends React.Component<
GroupedDeprecationsProps,
GroupedDeprecationsState
> {
public static getDerivedStateFromProps(
nextProps: GroupedDeprecationsProps,
{ currentPage }: GroupedDeprecationsState
) {
// If filters change and the currentPage is now bigger than the num of pages we're going to show,
// reset the current page to 0.
if (currentPage >= CalcFields.numPages(nextProps)) {
return { currentPage: 0 };
} else {
return null;
}
}
public state = {
forceExpand: false,
// `expandNumber` is used as workaround to force EuiAccordion to re-render by
// incrementing this number (used as a key) when expand all or collapse all is clicked.
expandNumber: 0,
currentPage: 0,
};
public render() {
const { currentGroupBy, allDeprecations = [] } = this.props;
const { forceExpand, expandNumber, currentPage } = this.state;
const filteredDeprecations = CalcFields.filteredDeprecations(this.props);
const groups = CalcFields.groups(this.props);
return (
<Fragment>
<EuiFlexGroup responsive={false} alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
flush="left"
size="s"
onClick={() => this.setExpand(true)}
data-test-subj="expandAll"
>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.controls.expandAllButtonLabel"
defaultMessage="Expand all"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
flush="left"
size="s"
onClick={() => this.setExpand(false)}
data-test-subj="collapseAll"
>
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.controls.collapseAllButtonLabel"
defaultMessage="Collapse all"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem />
<EuiFlexItem grow={false}>
<DeprecationCountSummary
allDeprecations={allDeprecations}
deprecations={filteredDeprecations}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
<div className="upgDeprecations">
{Object.keys(groups)
.sort()
// Apply pagination
.slice(currentPage * PER_PAGE, (currentPage + 1) * PER_PAGE)
.map(groupName => [
<DeprecationAccordion
key={expandNumber}
id={`depgroup-${groupName}`}
title={groupName}
deprecations={groups[groupName]}
{...{ currentGroupBy, forceExpand }}
/>,
])}
{/* Only show pagination if we have more than PER_PAGE. */}
{Object.keys(groups).length > PER_PAGE && (
<Fragment>
<EuiSpacer />
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiPagination
pageCount={CalcFields.numPages(this.props)}
activePage={currentPage}
onPageClick={this.setPage}
/>
</EuiFlexItem>
</EuiFlexGroup>
</Fragment>
)}
</div>
</Fragment>
);
}
private setExpand = (forceExpand: boolean) => {
this.setState({ forceExpand, expandNumber: this.state.expandNumber + 1 });
};
private setPage = (currentPage: number) => this.setState({ currentPage });
}

View file

@ -0,0 +1,89 @@
/*
* 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 { countBy } from 'lodash';
import React, { StatelessComponent } from 'react';
import { EuiBadge, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
import { COLOR_MAP, LEVEL_MAP, REVERSE_LEVEL_MAP } from '../constants';
const LocalizedLevels: { [level: string]: string } = {
warning: i18n.translate('xpack.upgradeAssistant.checkupTab.deprecations.warningLabel', {
defaultMessage: 'warning',
}),
critical: i18n.translate('xpack.upgradeAssistant.checkupTab.deprecations.criticalLabel', {
defaultMessage: 'critical',
}),
};
export const LocalizedActions: { [level: string]: string } = {
warning: i18n.translate('xpack.upgradeAssistant.checkupTab.deprecations.warningActionTooltip', {
defaultMessage: 'Resolving this issue before upgrading is advised, but not required.',
}),
critical: i18n.translate('xpack.upgradeAssistant.checkupTab.deprecations.criticalActionTooltip', {
defaultMessage: 'Resolve this issue before upgrading.',
}),
};
interface DeprecationHealthProps {
deprecations: DeprecationInfo[];
single?: boolean;
}
const SingleHealth: StatelessComponent<{ level: DeprecationInfo['level']; label: string }> = ({
level,
label,
}) => (
<React.Fragment>
<EuiToolTip content={LocalizedActions[level]}>
<EuiBadge color={COLOR_MAP[level]}>{label}</EuiBadge>
</EuiToolTip>
&emsp;
</React.Fragment>
);
/**
* Displays a summary health for a list of deprecations that shows the number and level of severity
* deprecations in the list.
*/
export const DeprecationHealth: StatelessComponent<DeprecationHealthProps> = ({
deprecations,
single = false,
}) => {
if (deprecations.length === 0) {
return <span />;
}
const levels = deprecations.map(d => LEVEL_MAP[d.level]);
if (single) {
const highest = Math.max(...levels);
const highestLevel = REVERSE_LEVEL_MAP[highest];
return <SingleHealth level={highestLevel} label={LocalizedLevels[highestLevel]} />;
}
const countByLevel = countBy(levels);
return (
<React.Fragment>
{Object.keys(countByLevel)
.map(k => parseInt(k, 10))
.sort()
.map(level => [level, REVERSE_LEVEL_MAP[level]])
.map(([numLevel, stringLevel]) => (
<SingleHealth
key={stringLevel}
level={stringLevel as DeprecationInfo['level']}
label={`${countByLevel[numLevel]} ${LocalizedLevels[stringLevel]}`}
/>
))}
</React.Fragment>
);
};

View file

@ -0,0 +1,7 @@
/*
* 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 { GroupedDeprecations } from './grouped';

View file

@ -0,0 +1,106 @@
/*
* 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 { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { IndexDeprecationTableProps, IndexDeprecationTableUI } from './index_table';
describe('IndexDeprecationTable', () => {
const actions = [{ label: 'Do it', url: 'http://justdoit.com' }];
const defaultProps = {
indices: [
{ index: 'index1', details: 'Index 1 deets', actions },
{ index: 'index2', details: 'Index 2 deets', actions },
{ index: 'index3', details: 'Index 3 deets', actions },
],
} as IndexDeprecationTableProps;
// Relying pretty heavily on EUI to implement the table functionality correctly.
// This test simply verifies that the props passed to EuiBaseTable are the ones
// expected.
test('render', () => {
expect(shallowWithIntl(<IndexDeprecationTableUI {...defaultProps} />)).toMatchInlineSnapshot(`
<EuiBasicTable
columns={
Array [
Object {
"field": "index",
"name": "Index",
"sortable": true,
},
Object {
"field": "details",
"name": "Details",
},
Object {
"actions": Array [
Object {
"render": [Function],
},
],
},
]
}
hasActions={false}
items={
Array [
Object {
"actions": Array [
Object {
"label": "Do it",
"url": "http://justdoit.com",
},
],
"details": "Index 1 deets",
"index": "index1",
},
Object {
"actions": Array [
Object {
"label": "Do it",
"url": "http://justdoit.com",
},
],
"details": "Index 2 deets",
"index": "index2",
},
Object {
"actions": Array [
Object {
"label": "Do it",
"url": "http://justdoit.com",
},
],
"details": "Index 3 deets",
"index": "index3",
},
]
}
noItemsMessage="No items found"
onChange={[Function]}
pagination={
Object {
"hidePerPageOptions": true,
"pageIndex": 0,
"pageSize": 10,
"pageSizeOptions": Array [],
"totalItemCount": 3,
}
}
responsive={true}
sorting={
Object {
"sort": Object {
"direction": "asc",
"field": "index",
},
}
}
/>
`);
});
});

View file

@ -0,0 +1,161 @@
/*
* 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 { sortBy } from 'lodash';
import React from 'react';
import { EuiBasicTable, EuiButton } from '@elastic/eui';
import { injectI18n } from '@kbn/i18n/react';
const PAGE_SIZES = [10, 25, 50, 100, 250, 500, 1000];
export interface IndexDeprecationDetails {
index: string;
details?: string;
actions?: Array<{
label: string;
url: string;
}>;
}
export interface IndexDeprecationTableProps extends ReactIntl.InjectedIntlProps {
indices: IndexDeprecationDetails[];
}
interface IndexDeprecationTableState {
sortField: string;
sortDirection: 'asc' | 'desc';
pageIndex: number;
pageSize: number;
}
export class IndexDeprecationTableUI extends React.Component<
IndexDeprecationTableProps,
IndexDeprecationTableState
> {
constructor(props: IndexDeprecationTableProps) {
super(props);
this.state = {
sortField: 'index',
sortDirection: 'asc',
pageIndex: 0,
pageSize: 10,
};
}
public render() {
const { intl } = this.props;
const { pageIndex, pageSize, sortField, sortDirection } = this.state;
const columns = [
{
field: 'index',
name: intl.formatMessage({
id: 'xpack.upgradeAssistant.checkupTab.deprecations.indexTable.indexColumnLabel',
defaultMessage: 'Index',
}),
sortable: true,
},
{
field: 'details',
name: intl.formatMessage({
id: 'xpack.upgradeAssistant.checkupTab.deprecations.indexTable.detailsColumnLabel',
defaultMessage: 'Details',
}),
},
];
if (this.actionsColumn) {
// @ts-ignore
columns.push(this.actionsColumn);
}
const sorting = { sort: { field: sortField, direction: sortDirection } };
const pagination = {
pageIndex,
pageSize,
...this.pageSizeOptions(),
};
return (
<EuiBasicTable
items={this.getRows()}
columns={columns}
sorting={sorting}
pagination={pagination}
onChange={this.onTableChange}
hasActions={false}
/>
);
}
private getRows() {
const { sortField, sortDirection, pageIndex, pageSize } = this.state;
const { indices } = this.props;
let sorted = sortBy(indices, sortField);
if (sortDirection === 'desc') {
sorted = sorted.reverse();
}
const start = pageIndex * pageSize;
return sorted.slice(start, start + pageSize);
}
private onTableChange = (tableProps: any) => {
this.setState({
sortField: tableProps.sort.field,
sortDirection: tableProps.sort.direction,
pageIndex: tableProps.page.index,
pageSize: tableProps.page.size,
});
};
private pageSizeOptions() {
const { indices } = this.props;
const totalItemCount = indices.length;
// If we only have that smallest page size, don't show any page size options.
if (totalItemCount <= PAGE_SIZES[0]) {
return { totalItemCount, pageSizeOptions: [], hidePerPageOptions: true };
}
// Keep a size option if the # of items is larger than the previous option.
// This avoids having a long list of useless page sizes.
const pageSizeOptions = PAGE_SIZES.filter((perPage, idx) => {
return idx === 0 || totalItemCount > PAGE_SIZES[idx - 1];
});
return { totalItemCount, pageSizeOptions, hidePerPageOptions: false };
}
private get actionsColumn() {
// NOTE: this naive implementation assumes all indices in the table have
// the same actions (can still have different URLs). This should work for known usecases.
const { indices } = this.props;
if (!indices.find(i => i.actions !== undefined)) {
return null;
}
const actions = indices[0].actions!;
return {
actions: actions.map((action, idx) => ({
render(index: IndexDeprecationDetails) {
const { url, label } = index.actions![idx];
return (
<EuiButton size="s" href={url} target="_blank">
{label}
</EuiButton>
);
},
})),
};
}
}
export const IndexDeprecationTable = injectI18n(IndexDeprecationTableUI);

View file

@ -0,0 +1,128 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { EnrichedDeprecationInfo } from 'x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis';
import { GroupByOption } from '../../../types';
import { DeprecationList } from './list';
describe('DeprecationList', () => {
describe('group by message', () => {
const defaultProps = {
deprecations: [
{ message: 'Issue 1', url: '', level: 'warning' },
{ message: 'Issue 1', url: '', level: 'warning' },
] as EnrichedDeprecationInfo[],
currentGroupBy: GroupByOption.message,
};
test('shows simple messages when index field is not present', () => {
expect(shallow(<DeprecationList {...defaultProps} />)).toMatchInlineSnapshot(`
<div>
<SimpleMessageDeprecation
deprecation={
Object {
"level": "warning",
"message": "Issue 1",
"url": "",
}
}
key="Issue 1"
/>
<SimpleMessageDeprecation
deprecation={
Object {
"level": "warning",
"message": "Issue 1",
"url": "",
}
}
key="Issue 1"
/>
</div>
`);
});
test('shows index deprecation when index field is present', () => {
// Add index fields to deprecation items
const props = {
...defaultProps,
deprecations: defaultProps.deprecations.map((d, index) => ({
...d,
index: index.toString(),
})),
};
const wrapper = shallow(<DeprecationList {...props} />);
expect(wrapper).toMatchInlineSnapshot(`
<IndexDeprecation
deprecation={
Object {
"index": "0",
"level": "warning",
"message": "Issue 1",
"url": "",
}
}
indices={
Array [
Object {
"actions": undefined,
"details": undefined,
"index": "0",
},
Object {
"actions": undefined,
"details": undefined,
"index": "1",
},
]
}
/>
`);
});
});
describe('group by index', () => {
const defaultProps = {
deprecations: [
{ message: 'Issue 1', index: 'index1', url: '', level: 'warning' },
{ message: 'Issue 2', index: 'index1', url: '', level: 'warning' },
] as EnrichedDeprecationInfo[],
currentGroupBy: GroupByOption.index,
};
test('shows detailed messages', () => {
expect(shallow(<DeprecationList {...defaultProps} />)).toMatchInlineSnapshot(`
<div>
<MessageDeprecation
deprecation={
Object {
"index": "index1",
"level": "warning",
"message": "Issue 1",
"url": "",
}
}
key="Issue 1"
/>
<MessageDeprecation
deprecation={
Object {
"index": "index1",
"level": "warning",
"message": "Issue 2",
"url": "",
}
}
key="Issue 2"
/>
</div>
`);
});
});
});

View file

@ -0,0 +1,113 @@
/*
* 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, { StatelessComponent } from 'react';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis';
import { GroupByOption } from '../../../types';
import { COLOR_MAP, LEVEL_MAP } from '../constants';
import { DeprecationCell } from './cell';
import { IndexDeprecationDetails, IndexDeprecationTable } from './index_table';
const sortByLevelDesc = (a: DeprecationInfo, b: DeprecationInfo) => {
return -1 * (LEVEL_MAP[a.level] - LEVEL_MAP[b.level]);
};
/**
* Used to show a single deprecation message with any detailed information.
*/
const MessageDeprecation: StatelessComponent<{ deprecation: EnrichedDeprecationInfo }> = ({
deprecation,
}) => {
const items = [];
if (deprecation.details) {
items.push({ body: deprecation.details });
}
return (
<DeprecationCell
headline={deprecation.message}
healthColor={COLOR_MAP[deprecation.level]}
actions={deprecation.actions}
docUrl={deprecation.url}
items={items}
/>
);
};
/**
* Used to show a single (simple) deprecation message with any detailed information.
*/
const SimpleMessageDeprecation: StatelessComponent<{ deprecation: EnrichedDeprecationInfo }> = ({
deprecation,
}) => {
const items = [];
if (deprecation.details) {
items.push({ body: deprecation.details });
}
return <DeprecationCell items={items} docUrl={deprecation.url} />;
};
interface IndexDeprecationProps {
deprecation: DeprecationInfo;
indices: IndexDeprecationDetails[];
}
/**
* Shows a single deprecation and table of affected indices with details for each index.
*/
const IndexDeprecation: StatelessComponent<IndexDeprecationProps> = ({ deprecation, indices }) => {
return (
<DeprecationCell docUrl={deprecation.url}>
<IndexDeprecationTable indices={indices} />
</DeprecationCell>
);
};
/**
* A list of deprecations that is either shown as individual deprecation cells or as a
* deprecation summary for a list of indices.
*/
export const DeprecationList: StatelessComponent<{
deprecations: EnrichedDeprecationInfo[];
currentGroupBy: GroupByOption;
}> = ({ deprecations, currentGroupBy }) => {
// If we're grouping by message and the first deprecation has an index field, show an index
// group deprecation. Otherwise, show each message.
if (currentGroupBy === GroupByOption.message && deprecations[0].index !== undefined) {
// If we're grouping by index we assume that every deprecation message is the same
// issue and that each deprecation will have an index associated with it.
const indices = deprecations.map(dep => ({
index: dep.index!,
details: dep.details,
actions: dep.actions,
}));
return <IndexDeprecation indices={indices} deprecation={deprecations[0]} />;
} else if (currentGroupBy === GroupByOption.index) {
// If we're grouping by index show all info for each message
return (
<div>
{deprecations.sort(sortByLevelDesc).map(dep => (
<MessageDeprecation deprecation={dep} key={dep.message} />
))}
</div>
);
} else {
return (
<div>
{deprecations.sort(sortByLevelDesc).map(dep => (
<SimpleMessageDeprecation deprecation={dep} key={dep.message} />
))}
</div>
);
}
};

View file

@ -0,0 +1,34 @@
/*
* 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 { mount, shallow } from 'enzyme';
import React from 'react';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
import { LevelFilterOption } from '../../types';
import { FilterBar } from './filter_bar';
const defaultProps = {
allDeprecations: [
{ level: LevelFilterOption.critical },
{ level: LevelFilterOption.critical },
] as DeprecationInfo[],
currentFilter: LevelFilterOption.critical,
onFilterChange: jest.fn(),
};
describe('FilterBar', () => {
test('renders', () => {
expect(shallow(<FilterBar {...defaultProps} />)).toMatchSnapshot();
});
test('clicking button calls onFilterChange', () => {
const wrapper = mount(<FilterBar {...defaultProps} />);
wrapper.find('button.euiFilterButton-hasActiveFilters').simulate('click');
expect(defaultProps.onFilterChange).toHaveBeenCalledTimes(1);
expect(defaultProps.onFilterChange.mock.calls[0][0]).toEqual(LevelFilterOption.critical);
});
});

View file

@ -0,0 +1,68 @@
/*
* 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 { groupBy } from 'lodash';
import React from 'react';
import { EuiFilterButton, EuiFilterGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
import { LevelFilterOption } from '../../types';
const LocalizedOptions: { [option: string]: string } = {
all: i18n.translate('xpack.upgradeAssistant.checkupTab.controls.filterBar.allButtonLabel', {
defaultMessage: 'all',
}),
critical: i18n.translate(
'xpack.upgradeAssistant.checkupTab.controls.filterBar.criticalButtonLabel',
{ defaultMessage: 'critical' }
),
};
const allFilterOptions = Object.keys(LevelFilterOption) as LevelFilterOption[];
interface FilterBarProps {
allDeprecations?: DeprecationInfo[];
currentFilter: LevelFilterOption;
onFilterChange(level: LevelFilterOption): void;
}
export const FilterBar: React.StatelessComponent<FilterBarProps> = ({
allDeprecations = [],
currentFilter,
onFilterChange,
}) => {
const levelGroups = groupBy(allDeprecations, 'level');
const levelCounts = Object.keys(levelGroups).reduce(
(counts, level) => {
counts[level] = levelGroups[level].length;
return counts;
},
{} as { [level: string]: number }
);
const allCount = allDeprecations.length;
return (
<EuiFlexItem grow={false}>
<EuiFilterGroup>
{allFilterOptions.map(option => (
<EuiFilterButton
key={option}
onClick={onFilterChange.bind(null, option)}
hasActiveFilters={currentFilter === option}
numFilters={
option === LevelFilterOption.all ? allCount : levelCounts[option] || undefined
}
>
{LocalizedOptions[option]}
</EuiFilterButton>
))}
</EuiFilterGroup>
</EuiFlexItem>
);
};

View file

@ -0,0 +1,30 @@
/*
* 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 { mount, shallow } from 'enzyme';
import React from 'react';
import { GroupByOption } from '../../types';
import { GroupByBar } from './group_by_bar';
const defaultProps = {
availableGroupByOptions: [GroupByOption.message, GroupByOption.index],
currentGroupBy: GroupByOption.message,
onGroupByChange: jest.fn(),
};
describe('GroupByBar', () => {
test('renders', () => {
expect(shallow(<GroupByBar {...defaultProps} />)).toMatchSnapshot();
});
test('clicking button calls onGroupByChange', () => {
const wrapper = mount(<GroupByBar {...defaultProps} />);
wrapper.find('button.euiFilterButton-hasActiveFilters').simulate('click');
expect(defaultProps.onGroupByChange).toHaveBeenCalledTimes(1);
expect(defaultProps.onGroupByChange.mock.calls[0][0]).toEqual(GroupByOption.message);
});
});

View file

@ -0,0 +1,53 @@
/*
* 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 { EuiFilterButton, EuiFilterGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { GroupByOption } from '../../types';
const LocalizedOptions: { [option: string]: string } = {
message: i18n.translate('xpack.upgradeAssistant.checkupTab.controls.groupByBar.byIssueLabel', {
defaultMessage: 'by issue',
}),
index: i18n.translate('xpack.upgradeAssistant.checkupTab.controls.groupByBar.byIndexLabel', {
defaultMessage: 'by index',
}),
};
interface GroupByBarProps {
availableGroupByOptions: GroupByOption[];
currentGroupBy: GroupByOption;
onGroupByChange: (groupBy: GroupByOption) => void;
}
export const GroupByBar: React.StatelessComponent<GroupByBarProps> = ({
availableGroupByOptions,
currentGroupBy,
onGroupByChange,
}) => {
if (availableGroupByOptions.length <= 1) {
return null;
}
return (
<EuiFlexItem grow={false}>
<EuiFilterGroup>
{availableGroupByOptions.map(option => (
<EuiFilterButton
key={option}
onClick={onGroupByChange.bind(null, option)}
hasActiveFilters={currentGroupBy === option}
>
{LocalizedOptions[option]}
</EuiFilterButton>
))}
</EuiFilterGroup>
</EuiFlexItem>
);
};

View file

@ -0,0 +1,7 @@
/*
* 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 { CheckupTab } from './checkup_tab';

View file

@ -0,0 +1 @@
@import './steps';

View file

@ -0,0 +1,6 @@
.upgSteps {
.euiStep:last-child .euiStep__content {
padding-bottom: 0;
margin-bottom: 0;
}
}

View file

@ -0,0 +1,124 @@
/*
* 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 axios from 'axios';
import React from 'react';
import { EuiLoadingSpinner, EuiSwitch } from '@elastic/eui';
import { injectI18n } from '@kbn/i18n/react';
import chrome from 'ui/chrome';
import { LoadingState } from '../../types';
interface DeprecationLoggingTabState {
loadingState: LoadingState;
loggingEnabled?: boolean;
}
export class DeprecationLoggingToggleUI extends React.Component<
ReactIntl.InjectedIntlProps,
DeprecationLoggingTabState
> {
constructor(props: ReactIntl.InjectedIntlProps) {
super(props);
this.state = {
loadingState: LoadingState.Loading,
};
}
public componentWillMount() {
this.loadData();
}
public render() {
const { loggingEnabled, loadingState } = this.state;
// Show a spinner until we've done the initial load.
if (loadingState === LoadingState.Loading && loggingEnabled === undefined) {
return <EuiLoadingSpinner size="l" />;
}
return (
<EuiSwitch
data-test-subj="upgradeAssistantDeprecationToggle"
label={this.renderLoggingState()}
checked={loggingEnabled}
onChange={this.toggleLogging}
disabled={loadingState === LoadingState.Loading || loadingState === LoadingState.Error}
/>
);
}
private renderLoggingState() {
const { intl } = this.props;
const { loggingEnabled, loadingState } = this.state;
if (loadingState === LoadingState.Error) {
return intl.formatMessage({
id:
'xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.enableDeprecationLoggingToggleSwitch.errorLabel',
defaultMessage: 'Could not load logging state',
});
} else if (loggingEnabled) {
return intl.formatMessage({
id:
'xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.enableDeprecationLoggingToggleSwitch.enabledLabel',
defaultMessage: 'On',
});
} else {
return intl.formatMessage({
id:
'xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.enableDeprecationLoggingToggleSwitch.disabledLabel',
defaultMessage: 'Off',
});
}
}
private loadData = async () => {
try {
this.setState({ loadingState: LoadingState.Loading });
const resp = await axios.get(
chrome.addBasePath('/api/upgrade_assistant/deprecation_logging')
);
this.setState({
loadingState: LoadingState.Success,
loggingEnabled: resp.data.isEnabled,
});
} catch (e) {
this.setState({ loadingState: LoadingState.Error });
}
};
private toggleLogging = async () => {
try {
// Optimistically toggle the UI
const newEnabled = !this.state.loggingEnabled;
this.setState({ loadingState: LoadingState.Loading, loggingEnabled: newEnabled });
const resp = await axios.put(
chrome.addBasePath('/api/upgrade_assistant/deprecation_logging'),
{
isEnabled: newEnabled,
},
{
headers: {
'kbn-xsrf': chrome.getXsrfToken(),
},
}
);
this.setState({
loadingState: LoadingState.Success,
loggingEnabled: resp.data.isEnabled,
});
} catch (e) {
this.setState({ loadingState: LoadingState.Error });
}
};
}
export const DeprecationLoggingToggle = injectI18n(DeprecationLoggingToggleUI);

View file

@ -0,0 +1,67 @@
/*
* 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, { Fragment, StatelessComponent } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiLoadingSpinner,
EuiPageContent,
EuiPageContentBody,
EuiSpacer,
// @ts-ignore
EuiStat,
EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { NEXT_MAJOR_VERSION } from '../../../../common/version';
import { LoadingState, UpgradeAssistantTabProps } from '../../types';
import { Steps } from './steps';
export const OverviewTab: StatelessComponent<UpgradeAssistantTabProps> = props => (
<Fragment>
<EuiSpacer />
<EuiText grow={false}>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.tabDetail"
defaultMessage="This assistant checks your cluster and indices and identifies the changes
you need to make before upgrading to Elasticsearch {nextEsVersion}."
values={{
nextEsVersion: `${NEXT_MAJOR_VERSION}.x`,
}}
/>
</p>
</EuiText>
<EuiSpacer />
{props.alertBanner && (
<Fragment>
{props.alertBanner}
<EuiSpacer />
</Fragment>
)}
<EuiPageContent>
<EuiPageContentBody>
{props.loadingState === LoadingState.Success ? (
<Steps {...props} />
) : (
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner />
</EuiFlexItem>
</EuiFlexGroup>
)}
</EuiPageContentBody>
</EuiPageContent>
</Fragment>
);

View file

@ -0,0 +1,275 @@
/*
* 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, { Fragment, StatelessComponent } from 'react';
import {
EuiFormRow,
EuiLink,
EuiNotificationBadge,
EuiSpacer,
// @ts-ignore
EuiStat,
EuiSteps,
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import chrome from 'ui/chrome';
import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../common/version';
import { UpgradeAssistantTabProps } from '../../types';
import { DeprecationLoggingToggle } from './deprecation_logging_toggle';
// Leaving these here even if unused so they are picked up for i18n static analysis
// Keep this until last minor release (when next major is also released).
const WAIT_FOR_RELEASE_STEP = {
title: i18n.translate('xpack.upgradeAssistant.overviewTab.steps.waitForReleaseStep.stepTitle', {
defaultMessage: 'Wait for the Elasticsearch {nextEsVersion} release',
values: {
nextEsVersion: `${NEXT_MAJOR_VERSION}.0`,
},
}),
children: (
<Fragment>
<EuiText grow={false}>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.waitForReleaseStep.stepDetail"
defaultMessage="Once the release is out, upgrade to the latest {currentEsMajorVersion} version, and then
return here to proceed with your {nextEsMajorVersion} upgrade."
values={{
currentEsMajorVersion: `${CURRENT_MAJOR_VERSION}.x`, // use "0.x" notation to imply the last minor
nextEsMajorVersion: `${NEXT_MAJOR_VERSION}.0`,
}}
/>
</p>
</EuiText>
</Fragment>
),
};
// Swap in this step for the one above it on the last minor release.
// @ts-ignore
const START_UPGRADE_STEP = {
title: i18n.translate('xpack.upgradeAssistant.overviewTab.steps.startUpgradeStep.stepTitle', {
defaultMessage: 'Start your upgrade',
}),
children: (
<Fragment>
<EuiText grow={false}>
<p>
{chrome.getInjected('isCloudEnabled', false) ? (
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.startUpgradeStepCloud.stepDetail.goToCloudDashboardDetail"
defaultMessage="Go to the Deployments section on the Elastic Cloud dashboard to start your upgrade."
/>
) : (
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.startUpgradeStepOnPrem.stepDetail.followInstructionsDetail"
defaultMessage="Follow {instructionButton} to start your upgrade."
values={{
instructionButton: (
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-upgrade.html"
target="_blank"
>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.startUpgradeStepOnPrem.stepDetail.instructionButtonLabel"
defaultMessage="these instructions"
/>
</EuiLink>
),
}}
/>
)}
</p>
</EuiText>
</Fragment>
),
};
export const StepsUI: StatelessComponent<
UpgradeAssistantTabProps & ReactIntl.InjectedIntlProps
> = ({ checkupData, setSelectedTabIndex, intl }) => {
const countByType = Object.keys(checkupData!).reduce(
(counts, checkupType) => {
counts[checkupType] = checkupData![checkupType].length;
return counts;
},
{} as { [checkupType: string]: number }
);
return (
<EuiSteps
className="upgSteps"
headingElement="h2"
steps={[
{
title: countByType.cluster
? intl.formatMessage({
id: 'xpack.upgradeAssistant.overviewTab.steps.clusterStep.issuesRemainingStepTitle',
defaultMessage: 'Check for issues with your cluster',
})
: intl.formatMessage({
id:
'xpack.upgradeAssistant.overviewTab.steps.clusterStep.noIssuesRemainingStepTitle',
defaultMessage: 'Your cluster settings are ready',
}),
status: countByType.cluster ? 'warning' : 'complete',
children: (
<EuiText>
{countByType.cluster ? (
<Fragment>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.clusterStep.todo.todoDetail"
defaultMessage="Go to the {clusterTabButton} to update the deprecated settings."
values={{
clusterTabButton: (
<EuiLink onClick={() => setSelectedTabIndex(1)}>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.clusterStep.todo.clusterTabButtonLabel"
defaultMessage="Cluster tab"
/>
</EuiLink>
),
}}
/>
</p>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.clusterStep.remainingIssuesDetail"
defaultMessage="{numIssues} issues must be resolved."
values={{
numIssues: (
<EuiNotificationBadge>{countByType.cluster}</EuiNotificationBadge>
),
}}
/>
</p>
</Fragment>
) : (
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.clusterStep.noRemainingIssuesLabel"
defaultMessage="No remaining deprecated settings."
/>
</p>
)}
</EuiText>
),
},
{
title: countByType.indices
? intl.formatMessage({
id: 'xpack.upgradeAssistant.overviewTab.steps.indicesStep.issuesRemainingStepTitle',
defaultMessage: 'Check for issues with your indices',
})
: intl.formatMessage({
id:
'xpack.upgradeAssistant.overviewTab.steps.indicesStep.noIssuesRemainingStepTitle',
defaultMessage: 'Your index settings are ready',
}),
status: countByType.indices ? 'warning' : 'complete',
children: (
<EuiText>
{countByType.indices ? (
<Fragment>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.indicesStep.todo.todoDetail"
defaultMessage="Go to the {indicesTabButton} to update the deprecated settings."
values={{
indicesTabButton: (
<EuiLink onClick={() => setSelectedTabIndex(2)}>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.indicesStep.todo.indicesTabButtonLabel"
defaultMessage="Indices tab"
/>
</EuiLink>
),
}}
/>
</p>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.indicesStep.remainingIssuesDetail"
defaultMessage="{numIssues} issues must be resolved."
values={{
numIssues: (
<EuiNotificationBadge>{countByType.indices}</EuiNotificationBadge>
),
}}
/>
</p>
</Fragment>
) : (
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.indicesStep.noRemainingIssuesLabel"
defaultMessage="No remaining deprecated settings."
/>
</p>
)}
</EuiText>
),
},
{
title: intl.formatMessage({
id: 'xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.stepTitle',
defaultMessage: 'Review the Elasticsearch deprecation logs',
}),
children: (
<Fragment>
<EuiText grow={false}>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.deprecationLogs.logsDetail"
defaultMessage="Read the {deprecationLogsDocButton} to see if your applications
are using functionality that is not available in {nextEsVersion}. You may need to enable deprecation logging."
values={{
deprecationLogsDocButton: (
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/logging.html#deprecation-logging"
target="_blank"
>
<FormattedMessage
id="xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.deprecationLogs.deprecationLogsDocButtonLabel"
defaultMessage="deprecation logs"
/>
</EuiLink>
),
nextEsVersion: `${NEXT_MAJOR_VERSION}.0`,
}}
/>
</p>
</EuiText>
<EuiSpacer />
<EuiFormRow
label={intl.formatMessage({
id:
'xpack.upgradeAssistant.overviewTab.steps.deprecationLogsStep.enableDeprecationLoggingLabel',
defaultMessage: 'Enable deprecation logging?',
})}
describedByIds={['deprecation-logging']}
>
<DeprecationLoggingToggle />
</EuiFormRow>
</Fragment>
),
},
// Swap in START_UPGRADE_STEP on the last minor release.
WAIT_FOR_RELEASE_STEP,
]}
/>
);
};
export const Steps = injectI18n(StepsUI);

View file

@ -0,0 +1,42 @@
/*
* 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 {
EnrichedDeprecationInfo,
UpgradeAssistantStatus,
} from '../../server/lib/es_migration_apis';
export interface UpgradeAssistantTabProps {
alertBanner?: React.ReactNode;
checkupData?: UpgradeAssistantStatus;
deprecations?: EnrichedDeprecationInfo[];
refreshCheckupData: () => Promise<void>;
loadingState: LoadingState;
setSelectedTabIndex: (tabIndex: number) => void;
}
export class UpgradeAssistantTabComponent<
T extends UpgradeAssistantTabProps = UpgradeAssistantTabProps,
S = {}
> extends React.Component<T, S> {}
export enum LoadingState {
Loading,
Success,
Error,
}
export enum LevelFilterOption {
all = 'all',
critical = 'critical',
}
export enum GroupByOption {
message = 'message',
index = 'index',
node = 'node',
}

View file

@ -0,0 +1,4 @@
@import 'ui/public/styles/_styling_constants';
@import './app';
@import './components/index';

View file

@ -0,0 +1,39 @@
/*
* 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';
// @ts-ignore
import { management } from 'ui/management';
// @ts-ignore
import { uiModules } from 'ui/modules';
// @ts-ignore
import routes from 'ui/routes';
import { NEXT_MAJOR_VERSION } from '../common/version';
import { RootComponent } from './app';
const BASE_PATH = `/management/elasticsearch/upgrade_assistant`;
function startApp() {
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}`,
});
uiModules.get('kibana').directive('upgradeAssistant', (reactDirective: any) => {
return reactDirective(RootComponent);
});
routes.when(`${BASE_PATH}/:view?`, {
template: '<upgrade-assistant />',
});
}
startApp();

View file

@ -0,0 +1,14 @@
/*
* 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 { registerClusterCheckupRoutes } from './routes/cluster_checkup';
import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging';
export function initServer(server: Legacy.Server) {
registerClusterCheckupRoutes(server);
registerDeprecationLoggingRoutes(server);
}

View file

@ -0,0 +1,68 @@
{
"cluster_settings": [
{
"level": "warning",
"message": "Template patterns are no longer using `template` field, but `index_patterns` instead",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
"details": "templates using `template` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template"
},
{
"level": "warning",
"message": "one or more templates use deprecated mapping settings",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}"
}
],
"node_settings": [],
"index_settings": {
".monitoring-es-6-2018.11.07": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]"
}
],
"twitter": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: tweet, field: liked]]"
}
],
".kibana": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]"
}
],
".watcher-history-6-2018.11.07": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]"
}
],
".monitoring-kibana-6-2018.11.07": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: doc, field: snapshot]]"
}
],
"twitter2": [
{
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
"details": "[[type: tweet, field: liked]]"
}
]
},
"ml_settings": []
}

View file

@ -0,0 +1,65 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getUpgradeAssistantStatus returns the correct shape of data 1`] = `
Object {
"cluster": Array [
Object {
"details": "templates using \`template\` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template",
"level": "warning",
"message": "Template patterns are no longer using \`template\` field, but \`index_patterns\` instead",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html#_index_templates_use_literal_index_patterns_literal_instead_of_literal_template_literal",
},
Object {
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}",
"level": "warning",
"message": "one or more templates use deprecated mapping settings",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_indices_changes.html",
},
],
"indices": Array [
Object {
"details": "[[type: doc, field: spins], [type: doc, field: mlockall], [type: doc, field: node_master], [type: doc, field: primary]]",
"index": ".monitoring-es-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: tweet, field: liked]]",
"index": "twitter",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: index-pattern, field: notExpandable], [type: config, field: xPackMonitoring:allowReport], [type: config, field: xPackMonitoring:showBanner], [type: dashboard, field: pause], [type: dashboard, field: timeRestore]]",
"index": ".kibana",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: doc, field: notify], [type: doc, field: created], [type: doc, field: attach_payload], [type: doc, field: met]]",
"index": ".watcher-history-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: doc, field: snapshot]]",
"index": ".monitoring-kibana-6-2018.11.07",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"details": "[[type: tweet, field: liked]]",
"index": "twitter2",
"level": "warning",
"message": "Coercion of boolean fields",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
],
"nodes": Array [],
}
`;

View file

@ -0,0 +1,84 @@
/*
* 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 {
getDeprecationLoggingStatus,
isDeprecationLoggingEnabled,
setDeprecationLogging,
} from './es_deprecation_logging_apis';
describe('getDeprecationLoggingStatus', () => {
it('calls cluster.getSettings', async () => {
const callWithRequest = jest.fn();
await getDeprecationLoggingStatus(callWithRequest, {} as any);
expect(callWithRequest).toHaveBeenCalledWith({}, 'cluster.getSettings', {
includeDefaults: true,
});
});
});
describe('setDeprecationLogging', () => {
describe('isEnabled = true', async () => {
it('calls cluster.putSettings with logger.deprecation = WARN', async () => {
const callWithRequest = jest.fn();
await setDeprecationLogging(callWithRequest, {} as any, true);
expect(callWithRequest).toHaveBeenCalledWith({}, 'cluster.putSettings', {
body: { transient: { 'logger.deprecation': 'WARN' } },
});
});
});
describe('isEnabled = false', async () => {
it('calls cluster.putSettings with logger.deprecation = ERROR', async () => {
const callWithRequest = jest.fn();
await setDeprecationLogging(callWithRequest, {} as any, false);
expect(callWithRequest).toHaveBeenCalledWith({}, 'cluster.putSettings', {
body: { transient: { 'logger.deprecation': 'ERROR' } },
});
});
});
});
describe('isDeprecationLoggingEnabled', () => {
['default', 'persistent', 'transient'].forEach(tier => {
['ALL', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ALL'].forEach(level => {
it(`returns true when ${tier} is set to ${level}`, () => {
expect(isDeprecationLoggingEnabled({ [tier]: { logger: { deprecation: level } } })).toBe(
true
);
});
});
});
['default', 'persistent', 'transient'].forEach(tier => {
['ERROR', 'FATAL'].forEach(level => {
it(`returns false when ${tier} is set to ${level}`, () => {
expect(isDeprecationLoggingEnabled({ [tier]: { logger: { deprecation: level } } })).toBe(
false
);
});
});
});
it('allows transient to override persistent and default', () => {
expect(
isDeprecationLoggingEnabled({
default: { logger: { deprecation: 'FATAL' } },
persistent: { logger: { deprecation: 'FATAL' } },
transient: { logger: { deprecation: 'WARN' } },
})
).toBe(true);
});
it('allows persistent to override default', () => {
expect(
isDeprecationLoggingEnabled({
default: { logger: { deprecation: 'FATAL' } },
persistent: { logger: { deprecation: 'WARN' } },
})
).toBe(true);
});
});

View file

@ -0,0 +1,54 @@
/*
* 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 { get } from 'lodash';
import { Legacy } from 'kibana';
import { CallClusterWithRequest } from 'src/legacy/core_plugins/elasticsearch';
interface DeprecationLoggingStatus {
isEnabled: boolean;
}
export async function getDeprecationLoggingStatus(
callWithRequest: CallClusterWithRequest,
req: Legacy.Request
): Promise<DeprecationLoggingStatus> {
const response = await callWithRequest(req, 'cluster.getSettings', {
includeDefaults: true,
});
return {
isEnabled: isDeprecationLoggingEnabled(response),
};
}
export async function setDeprecationLogging(
callWithRequest: CallClusterWithRequest,
req: Legacy.Request,
isEnabled: boolean
): Promise<DeprecationLoggingStatus> {
const response = await callWithRequest(req, 'cluster.putSettings', {
body: {
transient: {
'logger.deprecation': isEnabled ? 'WARN' : 'ERROR',
},
},
});
return {
isEnabled: isDeprecationLoggingEnabled(response),
};
}
export function isDeprecationLoggingEnabled(settings: any) {
const deprecationLogLevel = ['default', 'persistent', 'transient'].reduce(
(currentLogLevel, settingsTier) =>
get(settings, [settingsTier, 'logger', 'deprecation'], currentLogLevel),
'WARN'
);
return ['ALL', 'TRACE', 'DEBUG', 'INFO', 'WARN'].includes(deprecationLogLevel);
}

View file

@ -0,0 +1,40 @@
/*
* 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 { getUpgradeAssistantStatus } from './es_migration_apis';
import { DeprecationAPIResponse } from 'src/legacy/core_plugins/elasticsearch';
import fakeDeprecations from './__fixtures__/fake_deprecations.json';
describe('getUpgradeAssistantStatus', () => {
let deprecationsResponse: DeprecationAPIResponse;
const callWithRequest = jest.fn().mockImplementation(async (req, api, { path }) => {
if (path === '/_xpack/migration/deprecations') {
return deprecationsResponse;
} else {
throw new Error(`Unexpected API call: ${path}`);
}
});
beforeEach(() => {
deprecationsResponse = _.cloneDeep(fakeDeprecations);
});
it('calls /_xpack/migration/deprecations', async () => {
await getUpgradeAssistantStatus(callWithRequest, {} as any, '/');
expect(callWithRequest).toHaveBeenCalledWith({}, 'transport.request', {
path: '/_xpack/migration/deprecations',
method: 'GET',
});
});
it('returns the correct shape of data', async () => {
const resp = await getUpgradeAssistantStatus(callWithRequest, {} as any, '/');
expect(resp).toMatchSnapshot();
});
});

View file

@ -0,0 +1,59 @@
/*
* 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 { Request } from 'hapi';
import { DeprecationAPIResponse, DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch';
export interface EnrichedDeprecationInfo extends DeprecationInfo {
index?: string;
node?: string;
actions?: Array<{
label: string;
url: string;
}>;
}
export interface UpgradeAssistantStatus {
cluster: EnrichedDeprecationInfo[];
nodes: EnrichedDeprecationInfo[];
indices: EnrichedDeprecationInfo[];
[checkupType: string]: EnrichedDeprecationInfo[];
}
export async function getUpgradeAssistantStatus(
callWithRequest: any,
req: Request,
basePath: string
): Promise<UpgradeAssistantStatus> {
const deprecations = (await callWithRequest(req, 'transport.request', {
path: '/_xpack/migration/deprecations',
method: 'GET',
})) as DeprecationAPIResponse;
return {
cluster: deprecations.cluster_settings,
nodes: deprecations.node_settings,
indices: getCombinedIndexInfos(deprecations, basePath),
};
}
// Combines the information from the migration assistance api and the deprecation api into a single array.
// Enhances with information about which index the deprecation applies to and adds buttons for accessing the
// reindex UI.
const getCombinedIndexInfos = (deprecations: DeprecationAPIResponse, basePath: string) =>
Object.keys(deprecations.index_settings).reduce(
(indexDeprecations, indexName) => {
return indexDeprecations.concat(
deprecations.index_settings[indexName].map(
d => ({ ...d, index: indexName } as EnrichedDeprecationInfo)
)
);
},
[] as EnrichedDeprecationInfo[]
);

View file

@ -0,0 +1,72 @@
/*
* 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 { Server } from 'hapi';
import { registerClusterCheckupRoutes } from './cluster_checkup';
// Need to require to get mock on named export to work.
// tslint:disable:no-var-requires
const MigrationApis = require('../lib/es_migration_apis');
MigrationApis.getUpgradeAssistantStatus = jest.fn();
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the es_migration_apis test.
*/
describe('reindex template API', () => {
const server = new Server();
server.plugins = {
elasticsearch: {
getCluster: () => ({ callWithRequest: jest.fn() } as any),
} as any,
} as any;
server.config = () => ({ get: () => '' } as any);
registerClusterCheckupRoutes(server);
describe('GET /api/upgrade_assistant/reindex/{indexName}.json', () => {
it('returns a template', async () => {
MigrationApis.getUpgradeAssistantStatus.mockResolvedValue({
cluster: [],
indices: [],
nodes: [],
});
const resp = await server.inject({
method: 'GET',
url: '/api/upgrade_assistant/status',
});
expect(resp.statusCode).toEqual(200);
expect(resp.payload).toMatchInlineSnapshot(
`"{\\"cluster\\":[],\\"indices\\":[],\\"nodes\\":[]}"`
);
});
it('returns an 403 error if it throws forbidden', async () => {
const e: any = new Error(`you can't go here!`);
e.status = 403;
MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(e);
const resp = await server.inject({
method: 'GET',
url: '/api/upgrade_assistant/status',
});
expect(resp.statusCode).toEqual(403);
});
it('returns an 500 error if it throws', async () => {
MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(new Error(`scary error!`));
const resp = await server.inject({
method: 'GET',
url: '/api/upgrade_assistant/status',
});
expect(resp.statusCode).toEqual(500);
});
});
});

View file

@ -0,0 +1,33 @@
/*
* 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';
import { Legacy } from 'kibana';
import { getUpgradeAssistantStatus } from '../lib/es_migration_apis';
export function registerClusterCheckupRoutes(server: Legacy.Server) {
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
const basePath = server.config().get<string>('server.basePath');
server.route({
path: '/api/upgrade_assistant/status',
method: 'GET',
async handler(request) {
try {
return await getUpgradeAssistantStatus(callWithRequest, request, basePath);
} catch (e) {
if (e.status === 403) {
return Boom.forbidden(e.message);
}
return Boom.boomify(e, {
statusCode: 500,
});
}
},
});
}

View file

@ -0,0 +1,76 @@
/*
* 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 { Server } from 'hapi';
import { registerDeprecationLoggingRoutes } from './deprecation_logging';
/**
* Since these route callbacks are so thin, these serve simply as integration tests
* to ensure they're wired up to the lib functions correctly. Business logic is tested
* more thoroughly in the es_deprecation_logging_apis test.
*/
describe('deprecation logging API', () => {
const callWithRequest = jest.fn();
const server = new Server();
server.plugins = {
elasticsearch: {
getCluster: () => ({ callWithRequest } as any),
} as any,
} as any;
registerDeprecationLoggingRoutes(server);
describe('GET /api/upgrade_assistant/deprecation_logging', () => {
it('returns isEnabled', async () => {
callWithRequest.mockResolvedValue({ default: { logger: { deprecation: 'WARN' } } });
const resp = await server.inject({
method: 'GET',
url: '/api/upgrade_assistant/deprecation_logging',
});
expect(resp.statusCode).toEqual(200);
expect(JSON.parse(resp.payload)).toEqual({ isEnabled: true });
});
it('returns an error if it throws', async () => {
callWithRequest.mockRejectedValue(new Error(`scary error!`));
const resp = await server.inject({
method: 'GET',
url: '/api/upgrade_assistant/deprecation_logging',
});
expect(resp.statusCode).toEqual(500);
});
});
describe('PUT /api/upgrade_assistant/deprecation_logging', () => {
it('returns isEnabled', async () => {
callWithRequest.mockResolvedValue({ default: { logger: { deprecation: 'ERROR' } } });
const resp = await server.inject({
method: 'GET',
url: '/api/upgrade_assistant/deprecation_logging',
payload: {
isEnabled: false,
},
});
expect(JSON.parse(resp.payload)).toEqual({ isEnabled: false });
});
it('returns an error if it throws', async () => {
callWithRequest.mockRejectedValue(new Error(`scary error!`));
const resp = await server.inject({
method: 'PUT',
url: '/api/upgrade_assistant/deprecation_logging',
payload: {
isEnabled: false,
},
});
expect(resp.statusCode).toEqual(500);
});
});
});

View file

@ -0,0 +1,50 @@
/*
* 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';
import Joi from 'joi';
import { Legacy } from 'kibana';
import {
getDeprecationLoggingStatus,
setDeprecationLogging,
} from '../lib/es_deprecation_logging_apis';
export function registerDeprecationLoggingRoutes(server: Legacy.Server) {
const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin');
server.route({
path: '/api/upgrade_assistant/deprecation_logging',
method: 'GET',
async handler(request) {
try {
return await getDeprecationLoggingStatus(callWithRequest, request);
} catch (e) {
return Boom.boomify(e, { statusCode: 500 });
}
},
});
server.route({
path: '/api/upgrade_assistant/deprecation_logging',
method: 'PUT',
options: {
validate: {
payload: Joi.object({
isEnabled: Joi.boolean(),
}),
},
},
async handler(request) {
try {
const { isEnabled } = request.payload as { isEnabled: boolean };
return await setDeprecationLogging(callWithRequest, request, isEnabled);
} catch (e) {
return Boom.boomify(e, { statusCode: 500 });
}
},
});
}

View file

@ -3,10 +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 { TestInvoker } from './lib/types';
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
// tslint:disable:no-default-export
export default function statusPage({ loadTestFile }: TestInvoker) {
export default function statusPage({ loadTestFile }: KibanaFunctionalTestDefaultProviders) {
describe('Status page', function statusPageTestSuite() {
this.tags('ciGroup4');

View file

@ -0,0 +1,15 @@
/*
* 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 { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
// tslint:disable:no-default-export
export default function upgradeCheckup({ loadTestFile }: KibanaFunctionalTestDefaultProviders) {
describe('Upgrade checkup ', function upgradeAssistantTestSuite() {
this.tags('ciGroup4');
loadTestFile(require.resolve('./upgrade_assistant'));
});
}

View file

@ -0,0 +1,45 @@
/*
* 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 { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
// tslint:disable:no-default-export
export default function upgradeAssistantFunctionalTests({
getService,
getPageObjects,
}: KibanaFunctionalTestDefaultProviders) {
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['upgradeAssistant']);
describe('Upgrade Checkup', () => {
before(async () => await esArchiver.load('empty_kibana'));
after(async () => await esArchiver.unload('empty_kibana'));
it('allows user to navigate to upgrade checkup', async () => {
await PageObjects.upgradeAssistant.navigateToPage();
await PageObjects.upgradeAssistant.expectUpgradeAssistant();
});
it('allows user to toggle deprecation logging', async () => {
await PageObjects.upgradeAssistant.navigateToPage();
await PageObjects.upgradeAssistant.expectDeprecationLoggingLabel('On');
await PageObjects.upgradeAssistant.toggleDeprecationLogging();
await PageObjects.upgradeAssistant.expectDeprecationLoggingLabel('Off');
});
it('allows user to open cluster tab', async () => {
await PageObjects.upgradeAssistant.navigateToPage();
await PageObjects.upgradeAssistant.clickTab('cluster');
await PageObjects.upgradeAssistant.expectIssueSummary('You have no cluster issues.');
});
it('allows user to open indices tab', async () => {
await PageObjects.upgradeAssistant.navigateToPage();
await PageObjects.upgradeAssistant.clickTab('indices');
await PageObjects.upgradeAssistant.expectIssueSummary('You have no index issues.');
});
});
}

View file

@ -20,6 +20,7 @@ import {
AccountSettingProvider,
InfraHomePageProvider,
StatusPagePageProvider,
UpgradeAssistantProvider,
} from './page_objects';
import {
@ -78,6 +79,7 @@ export default async function ({ readConfigFile }) {
resolve(__dirname, './apps/grok_debugger'),
resolve(__dirname, './apps/infra'),
resolve(__dirname, './apps/status_page'),
resolve(__dirname, './apps/upgrade_assistant'),
],
// define the name and providers for services that should be
@ -129,6 +131,7 @@ export default async function ({ readConfigFile }) {
spaceSelector: SpaceSelectorPageProvider,
infraHome: InfraHomePageProvider,
statusPage: StatusPagePageProvider,
upgradeAssistant: UpgradeAssistantProvider,
},
servers: kibanaFunctionalConfig.get('servers'),

View file

@ -15,3 +15,4 @@ export { SpaceSelectorPageProvider } from './space_selector_page';
export { AccountSettingProvider } from './accountsetting_page';
export { InfraHomePageProvider } from './infra_home_page';
export { StatusPagePageProvider } from './status_page';
export { UpgradeAssistantProvider } from './upgrade_assistant';

View file

@ -0,0 +1,74 @@
/*
* 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 'expect.js';
export function UpgradeAssistantProvider({ getService, getPageObjects }) {
const retry = getService('retry');
const log = getService('log');
const browser = getService('browser');
const find = getService('find');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common', 'settings', 'security']);
class UpgradeAssistant {
async initTests() {
log.debug('UpgradeAssistant:initTests');
}
async navigateToPage() {
return await retry.try(async () => {
await PageObjects.common.navigateToApp('settings');
await testSubjects.click('upgrade_assistant');
});
}
async expectUpgradeAssistant() {
return await retry.try(async () => {
log.debug(`expectUpgradeAssistant()`);
expect(testSubjects.exists('upgradeAssistantRoot')).to.be.true;
const url = await browser.getCurrentUrl();
expect(url).to.contain(`/upgrade_assistant`);
});
}
async toggleDeprecationLogging() {
return await retry.try(async () => {
log.debug('toggleDeprecationLogging()');
await testSubjects.click('upgradeAssistantDeprecationToggle');
});
}
async expectDeprecationLoggingLabel(labelText) {
return await retry.try(async () => {
log.debug('expectDeprecationLoggingLabel()');
const toggle = await testSubjects.find('upgradeAssistantDeprecationToggle');
const div = await toggle.getProperty('parentElement');
const label = await div.findByCssSelector('label');
expect(await label.getVisibleText()).to.eql(labelText);
});
}
async clickTab(tabId) {
return await retry.try(async () => {
log.debug('clickTab()');
const tab = await find.byCssSelector(`.euiTabs .euiTab#${tabId}`);
await tab.click();
});
}
async expectIssueSummary(summary) {
return await retry.try(async () => {
log.debug('expectIssueSummary()');
const summaryEl = await testSubjects.find('upgradeAssistantIssueSummary');
const summaryElText = await summaryEl.getVisibleText();
expect(summaryElText).to.eql(summary);
});
}
}
return new UpgradeAssistant();
}