From 1b421cc2baa6e85e160f5b497add2dd4e6245796 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 27 Apr 2018 09:16:48 -0400 Subject: [PATCH 001/102] Index lifecycle management wizard --- package.json | 1 + x-pack/index.js | 2 ++ yarn.lock | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 231f032931205..da31a96649502 100644 --- a/package.json +++ b/package.json @@ -166,6 +166,7 @@ "querystring-browser": "1.0.4", "raw-loader": "0.5.1", "react": "^16.2.0", + "react-ace": "6.0.0", "react-addons-shallow-compare": "15.6.2", "react-anything-sortable": "^1.7.4", "react-color": "^2.13.8", diff --git a/x-pack/index.js b/x-pack/index.js index ba26c8fcf4161..0b55fde48ecd2 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -20,6 +20,7 @@ import { apm } from './plugins/apm'; import { licenseManagement } from './plugins/license_management'; import { cloud } from './plugins/cloud'; import { indexManagement } from './plugins/index_management'; +import { indexLifecycleManagement } from './plugins/index_lifecycle_management'; import { consoleExtensions } from './plugins/console_extensions'; module.exports = function (kibana) { @@ -40,6 +41,7 @@ module.exports = function (kibana) { licenseManagement(kibana), cloud(kibana), indexManagement(kibana), + indexLifecycleManagement(kibana), consoleExtensions(kibana) ]; }; diff --git a/yarn.lock b/yarn.lock index 30b183fc31286..c77b70093e420 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3589,6 +3589,10 @@ di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" +diff-match-patch@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.0.tgz#1cc3c83a490d67f95d91e39f6ad1f2e086b63048" + diff@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" @@ -10220,6 +10224,16 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-ace@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-6.0.0.tgz#c211c21825f27343a7392f102493dc3ae099886d" + dependencies: + brace "^0.11.0" + diff-match-patch "^1.0.0" + lodash.get "^4.4.2" + lodash.isequal "^4.1.1" + prop-types "^15.5.8" + react-ace@^5.5.0, react-ace@^5.9.0: version "5.9.0" resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-5.9.0.tgz#427a1cc4869b960a6f9748aa7eb169a9269fc336" @@ -10683,9 +10697,6 @@ regex-cache@^0.4.2: regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" regexpu-core@^1.0.0: version "1.0.0" From eb7fc0fb1a538cd228cf78e2403e13b65e977906 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 27 Apr 2018 09:41:54 -0400 Subject: [PATCH 002/102] Adding index lifecycle management files --- package.json | 1 - x-pack/package.json | 1 + .../index_lifecycle_management/TODO.md | 6 + .../common/constants/base_path.js | 7 + .../common/constants/index.js | 8 + .../common/constants/plugin.js | 9 + .../index_lifecycle_management/index.js | 38 ++ .../public/api/index.js | 47 +++ .../index_lifecycle_management/public/app.js | 15 + .../public/index.js | 8 + .../public/lib/find_errors.js | 19 + .../public/lib/manage_angular_lifecycle.js | 23 ++ .../public/main.html | 3 + .../public/register_management_section.js | 17 + .../public/register_routes.js | 44 +++ .../public/sections/landing/index.js | 10 + .../sections/landing/landing.container.js | 24 ++ .../public/sections/landing/landing.js | 43 +++ .../configuration/configuration.container.js | 41 ++ .../components/configuration/configuration.js | 156 ++++++++ .../components/configuration/index.js | 7 + .../components/template_selection/index.js | 7 + .../template_selection.container.js | 27 ++ .../template_selection/template_selection.js | 85 +++++ .../wizard/components/index_template/index.js | 10 + .../index_template/index_template.js | 85 +++++ .../cold_phase/cold_phase.container.js | 34 ++ .../components/cold_phase/cold_phase.js | 223 +++++++++++ .../components/cold_phase/index.js | 7 + .../delete_phase/delete_phase.container.js | 20 + .../components/delete_phase/delete_phase.js | 155 ++++++++ .../components/delete_phase/index.js | 7 + .../hot_phase/hot_phase.container.js | 23 ++ .../components/hot_phase/hot_phase.js | 238 ++++++++++++ .../components/hot_phase/index.js | 7 + .../components/warm_phase/index.js | 7 + .../warm_phase/warm_phase.container.js | 33 ++ .../components/warm_phase/warm_phase.js | 358 ++++++++++++++++++ .../components/policy_configuration/index.js | 7 + .../policy_configuration.container.js | 48 +++ .../policy_configuration.js | 254 +++++++++++++ .../components/policy_selection/index.js | 7 + .../policy_selection.container.js | 29 ++ .../policy_selection/policy_selection.js | 117 ++++++ .../wizard/components/review/index.js | 7 + .../components/review/review.container.js | 45 +++ .../wizard/components/review/review.js | 177 +++++++++ .../wizard/components/review/review.less | 5 + .../public/sections/wizard/form_errors.js | 36 ++ .../public/sections/wizard/index.js | 7 + .../sections/wizard/wizard.container.js | 32 ++ .../public/sections/wizard/wizard.js | 165 ++++++++ .../public/store/actions/general.js | 14 + .../public/store/actions/index.js | 14 + .../public/store/actions/index_template.js | 103 +++++ .../public/store/actions/lifecycle.js | 25 ++ .../public/store/actions/nodes.js | 25 ++ .../public/store/actions/policies.js | 31 ++ .../public/store/constants.js | 104 +++++ .../public/store/index.js | 7 + .../public/store/reducers/general.js | 38 ++ .../public/store/reducers/index.js | 21 + .../public/store/reducers/index_template.js | 54 +++ .../public/store/reducers/nodes.js | 61 +++ .../public/store/reducers/policies.js | 173 +++++++++ .../public/store/selectors/general.js | 12 + .../public/store/selectors/index.js | 14 + .../public/store/selectors/index_template.js | 170 +++++++++ .../public/store/selectors/lifecycle.js | 200 ++++++++++ .../public/store/selectors/nodes.js | 46 +++ .../public/store/selectors/policies.js | 232 ++++++++++++ .../public/store/store.js | 21 + .../call_with_request_factory.js | 18 + .../lib/call_with_request_factory/index.js | 7 + .../check_license/__tests__/check_license.js | 146 +++++++ .../server/lib/check_license/check_license.js | 58 +++ .../server/lib/check_license/index.js | 7 + .../__tests__/wrap_custom_error.js | 21 + .../error_wrappers/__tests__/wrap_es_error.js | 39 ++ .../__tests__/wrap_unknown_error.js | 19 + .../server/lib/error_wrappers/index.js | 9 + .../lib/error_wrappers/wrap_custom_error.js | 18 + .../lib/error_wrappers/wrap_es_error.js | 30 ++ .../lib/error_wrappers/wrap_unknown_error.js | 17 + .../__tests__/is_es_error_factory.js | 48 +++ .../server/lib/is_es_error_factory/index.js | 7 + .../is_es_error_factory.js | 18 + .../__tests__/license_pre_routing_factory.js | 72 ++++ .../lib/license_pre_routing_factory/index.js | 7 + .../license_pre_routing_factory.js | 29 ++ .../lib/register_license_checker/index.js | 7 + .../register_license_checker.js | 21 + .../server/routes/api/indices/index.js | 7 + .../api/indices/register_bootstrap_route.js | 49 +++ .../indices/register_get_affected_route.js | 93 +++++ .../api/indices/register_indices_routes.js | 13 + .../server/routes/api/lifecycle/index.js | 7 + .../api/lifecycle/register_create_route.js | 95 +++++ .../lifecycle/register_lifecycle_routes.js | 11 + .../server/routes/api/nodes/index.js | 7 + .../routes/api/nodes/register_list_route.js | 61 +++ .../routes/api/nodes/register_nodes_routes.js | 11 + .../server/routes/api/policies/index.js | 7 + .../api/policies/register_fetch_route.js | 60 +++ .../api/policies/register_policies_routes.js | 11 + .../server/routes/api/templates/index.js | 7 + .../api/templates/register_fetch_route.js | 81 ++++ .../api/templates/register_get_route.js | 52 +++ .../templates/register_templates_routes.js | 16 + x-pack/yarn.lock | 14 + 110 files changed, 5353 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/index_lifecycle_management/TODO.md create mode 100644 x-pack/plugins/index_lifecycle_management/common/constants/base_path.js create mode 100644 x-pack/plugins/index_lifecycle_management/common/constants/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/common/constants/plugin.js create mode 100644 x-pack/plugins/index_lifecycle_management/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/api/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/app.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/main.html create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_management_section.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/general.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/constants.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/store.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js diff --git a/package.json b/package.json index da31a96649502..231f032931205 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,6 @@ "querystring-browser": "1.0.4", "raw-loader": "0.5.1", "react": "^16.2.0", - "react-ace": "6.0.0", "react-addons-shallow-compare": "15.6.2", "react-anything-sortable": "^1.7.4", "react-color": "^2.13.8", diff --git a/x-pack/package.json b/x-pack/package.json index b652849340d58..1558920956dcf 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -130,6 +130,7 @@ "prop-types": "^15.6.0", "puid": "1.0.5", "react": "^16.2.0", + "react-ace": "6.0.0", "react-clipboard.js": "^1.1.2", "react-dom": "^16.2.0", "react-markdown-renderer": "^1.4.0", diff --git a/x-pack/plugins/index_lifecycle_management/TODO.md b/x-pack/plugins/index_lifecycle_management/TODO.md new file mode 100644 index 0000000000000..3d39db9451560 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/TODO.md @@ -0,0 +1,6 @@ +- PUT to override does not work, is there a PATCH or something? +- Rename PRIMARY_NODES to PRIMARY_SHARDS per configuration.js label change +- Policy type in the UI anywhere? +- Diff editor to EUI +- Index management changes to see status +- CRUD UIs for managing policies and index templates \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/base_path.js b/x-pack/plugins/index_lifecycle_management/common/constants/base_path.js new file mode 100644 index 0000000000000..5eea1d0ead4a4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/base_path.js @@ -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 const BASE_PATH = '/management/elasticsearch/index_lifecycle_management/'; diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/index.js b/x-pack/plugins/index_lifecycle_management/common/constants/index.js new file mode 100644 index 0000000000000..59b61f7b99f98 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PLUGIN } from './plugin'; +export { BASE_PATH } from './base_path'; diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/plugin.js b/x-pack/plugins/index_lifecycle_management/common/constants/plugin.js new file mode 100644 index 0000000000000..0261f57a93e8c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/plugin.js @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const PLUGIN = { + ID: 'index_lifecycle_management' +}; diff --git a/x-pack/plugins/index_lifecycle_management/index.js b/x-pack/plugins/index_lifecycle_management/index.js new file mode 100644 index 0000000000000..2824f8e345d50 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/index.js @@ -0,0 +1,38 @@ +/* + * 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 { resolve } from 'path'; +import { registerTemplatesRoutes } from './server/routes/api/templates'; +import { registerNodesRoutes } from './server/routes/api/nodes'; +import { registerPoliciesRoutes } from './server/routes/api/policies'; +import { registerLifecycleRoutes } from './server/routes/api/lifecycle'; +import { registerIndicesRoutes } from './server/routes/api/indices'; +import { registerLicenseChecker } from './server/lib/register_license_checker'; +import { PLUGIN } from './common/constants'; + +export function indexLifecycleManagement(kibana) { + return new kibana.Plugin({ + id: PLUGIN.ID, + publicDir: resolve(__dirname, 'public'), + require: ['kibana', 'elasticsearch', 'xpack_main'], + uiExports: { + managementSections: [ + 'plugins/index_lifecycle_management', + ] + }, + init: function (server) { + registerLicenseChecker(server); + registerTemplatesRoutes(server); + registerNodesRoutes(server); + registerPoliciesRoutes(server); + registerLifecycleRoutes(server); + registerIndicesRoutes(server); + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js new file mode 100644 index 0000000000000..02018804fa462 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -0,0 +1,47 @@ +/* + * 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 chrome from 'ui/chrome'; +let httpClient; +export const setHttpClient = (client) => { + httpClient = client; +}; +const apiPrefix = chrome.addBasePath('/api/index_lifecycle_management'); + +export async function loadNodes() { + const response = await httpClient.get(`${apiPrefix}/nodes/list`); + return response.data; +} + +export async function loadIndexTemplates() { + const response = await httpClient.get(`${apiPrefix}/templates`); + return response.data; +} + +export async function loadIndexTemplate(templateName) { + const response = await httpClient.get(`${apiPrefix}/template/${templateName}`); + return response.data; +} + +export async function loadPolicies() { + const response = await httpClient.get(`${apiPrefix}/policies`); + return response.data; +} + +export async function saveLifecycle(lifecycle, indexTemplatePatch) { + const response = await httpClient.post(`${apiPrefix}/lifecycle`, { lifecycle, indexTemplatePatch }); + return response.data; +} + +export async function bootstrap(indexName, aliasName) { + const response = await httpClient.post(`${apiPrefix}/indices/bootstrap`, { indexName, aliasName }); + return response.data; +} + +export async function getAffectedIndices(indexTemplateName, policyName) { + const response = await httpClient.get(`${apiPrefix}/indices/affected/${indexTemplateName}/${policyName}`); + return response.data; +} diff --git a/x-pack/plugins/index_lifecycle_management/public/app.js b/x-pack/plugins/index_lifecycle_management/public/app.js new file mode 100644 index 0000000000000..2f5c19a16b684 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/app.js @@ -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 React from 'react'; +import { Landing } from './sections/landing'; + +export const App = () => ( + +); diff --git a/x-pack/plugins/index_lifecycle_management/public/index.js b/x-pack/plugins/index_lifecycle_management/public/index.js new file mode 100644 index 0000000000000..ccde49edbdf5d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/index.js @@ -0,0 +1,8 @@ +/* + * 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 './register_management_section'; +import './register_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js new file mode 100644 index 0000000000000..38d600622a8d4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const hasErrors = (object, keysToIgnore = []) => { + const errors = []; + for (const [key, value] of Object.entries(object)) { + if (keysToIgnore.includes(key)) continue; + if (Array.isArray(value) && value.length > 0) { + return true; + errors.push(...value); + } else if (value) { + return hasErrors(value, keysToIgnore); + } + } + return false; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js new file mode 100644 index 0000000000000..3813e632a0a73 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js @@ -0,0 +1,23 @@ +/* + * 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 { unmountComponentAtNode } from 'react-dom'; + +export const manageAngularLifecycle = ($scope, $route, elem) => { + const lastRoute = $route.current; + + const deregister = $scope.$on('$locationChangeSuccess', () => { + const currentRoute = $route.current; + if (lastRoute.$$route.template === currentRoute.$$route.template) { + $route.current = lastRoute; + } + }); + + $scope.$on('$destroy', () => { + deregister && deregister(); + elem && unmountComponentAtNode(elem); + }); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/main.html b/x-pack/plugins/index_lifecycle_management/public/main.html new file mode 100644 index 0000000000000..1d1d0036e7fc1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/main.html @@ -0,0 +1,3 @@ + +
+ diff --git a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js new file mode 100644 index 0000000000000..16c4de6cf3236 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js @@ -0,0 +1,17 @@ +/* + * 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 { management } from 'ui/management'; +import { BASE_PATH } from '../common/constants'; + +const esSection = management.getSection('elasticsearch'); +esSection.register('index_lifecycle_management', { + visible: true, + display: 'Index Lifecycle Management', + order: 1, + url: `#${BASE_PATH}home` +}); + diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js new file mode 100644 index 0000000000000..c601c63299f31 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { render } from 'react-dom'; +import { Provider } from 'react-redux'; +import { setHttpClient } from './api'; + +import { App } from './app'; +import { BASE_PATH } from '../common/constants/base_path'; +import { indexLifecycleManagementStore } from './store'; + +import routes from 'ui/routes'; + +import template from './main.html'; +import { manageAngularLifecycle } from './lib/manage_angular_lifecycle'; + +const renderReact = async (elem) => { + render( + + + , + elem + ); +}; + +routes.when(`${BASE_PATH}:view?/:id?`, { + template: template, + controllerAs: 'indexManagement', + controller: class IndexManagementController { + constructor($scope, $route, $http) { + setHttpClient($http); + + $scope.$$postDigest(() => { + const elem = document.getElementById('indexLifecycleManagementReactRoot'); + renderReact(elem); + manageAngularLifecycle($scope, $route, elem); + }); + } + } +}); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js new file mode 100644 index 0000000000000..cc858ed0f20e5 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js @@ -0,0 +1,10 @@ +/* + * 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 { Landing } from './landing.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js new file mode 100644 index 0000000000000..93e507b00ab7b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js @@ -0,0 +1,24 @@ +/* + * 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 { connect } from 'react-redux'; +import { Landing as PresentationComponent } from './landing'; +import { fetchIndexTemplates } from '../../store/actions'; +import { + getIndexTemplates, +} from '../../store/selectors'; + +export const Landing = connect( + state => ({ + indexTemplates: getIndexTemplates(state), + }), + { + fetchIndexTemplates + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js b/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js new file mode 100644 index 0000000000000..c9b2366e97e05 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js @@ -0,0 +1,43 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Wizard } from '../wizard'; + +export class Landing extends PureComponent { + static propTypes = { + fetchIndexTemplates: PropTypes.func.isRequired, + + indexTemplates: PropTypes.array, + } + + componentWillMount() { + this.props.fetchIndexTemplates(); + } + + render() { + const { indexTemplates } = this.props; + + if (indexTemplates === null) { + // Loading... + return null; + } + + if (indexTemplates.length === 0) { + return ( +

No index templates found.

+ ); + } + + return ( + + ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js new file mode 100644 index 0000000000000..01ee20f34d5db --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js @@ -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 { connect } from 'react-redux'; +import { Configuration as PresentationComponent } from './configuration'; +import { + getNodeOptions, + getSelectedPrimaryShardCount, + getSelectedReplicaCount, + getSelectedNodeAttrs, + getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount, +} from '../../../../../../store/selectors'; +import { + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + fetchNodes +} from '../../../../../../store/actions'; + +export const Configuration = connect( + state => ({ + nodeOptions: getNodeOptions(state), + selectedNodeAttrs: getSelectedNodeAttrs(state), + selectedPrimaryShardCount: getSelectedPrimaryShardCount(state), + selectedReplicaCount: getSelectedReplicaCount(state), + selectedNodeAttrs: getSelectedNodeAttrs(state), + isPrimaryShardCountHigherThanSelectedNodeAttrsCount: getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount(state), + }), + { + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js new file mode 100644 index 0000000000000..a88ef8e7a97ee --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -0,0 +1,156 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiSelect, + EuiFieldNumber, + EuiHorizontalRule, + EuiCallOut +} from '@elastic/eui'; +import { + STRUCTURE_NODE_ATTRS, + STRUCTURE_PRIMARY_NODES, + STRUCTURE_REPLICAS +} from '../../../../../../store/constants'; + +import { ErrableFormRow } from '../../../../form_errors'; + +export class Configuration extends PureComponent { + static propTypes = { + fetchNodes: PropTypes.func.isRequired, + setSelectedNodeAttrs: PropTypes.func.isRequired, + setSelectedPrimaryShardCount: PropTypes.func.isRequired, + setSelectedReplicaCount: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + selectedPrimaryShardCount: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + selectedNodeAttrs: PropTypes.string.isRequired, + nodeOptions: PropTypes.array.isRequired, + selectedReplicaCount: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + isPrimaryShardCountHigherThanSelectedNodeAttrsCount: PropTypes.bool.isRequired, + }; + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + validate, + + nodeOptions, + selectedPrimaryShardCount, + selectedReplicaCount, + selectedNodeAttrs, + errors, + isShowingErrors, + isPrimaryShardCountHigherThanSelectedNodeAttrsCount + } = this.props; + + const primaryNodeErrors = isPrimaryShardCountHigherThanSelectedNodeAttrsCount ? ( + + The selected primary shard count is higher than the number of nodes matching the selected attributes. + + ) : null; + + return ( +
+ + +

Configure options

+
+ + + { + await setSelectedNodeAttrs(e.target.value); + validate(); + }} + options={nodeOptions} + /> + + + +

+ Optimize these values for throughput. (Add more) +

+
+ + + + + { + await setSelectedPrimaryShardCount(e.target.value); + validate(); + }} + value={selectedPrimaryShardCount} + /> + + + + + { + await setSelectedReplicaCount(e.target.value); + validate(); + }} + value={selectedReplicaCount} + /> + + + + + {primaryNodeErrors} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js new file mode 100644 index 0000000000000..2a387bd853de8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js @@ -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 { Configuration } from './configuration.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js new file mode 100644 index 0000000000000..a7c5cfb8f3dab --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js @@ -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 { TemplateSelection } from './template_selection.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js new file mode 100644 index 0000000000000..8a5f80515d899 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { connect } from 'react-redux'; +import { TemplateSelection as PresentationComponent } from './template_selection'; +import { + getIndexTemplateOptions, + getSelectedIndexTemplateName, +} from '../../../../../../store/selectors'; +import { + fetchIndexTemplates, + setSelectedIndexTemplate +} from '../../../../../../store/actions'; + +export const TemplateSelection = connect( + state => ({ + templateOptions: getIndexTemplateOptions(state), + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + }), + { + fetchIndexTemplates, + setSelectedIndexTemplate, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js new file mode 100644 index 0000000000000..5828749b4f70c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -0,0 +1,85 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { EuiSelect, EuiLink } from '@elastic/eui'; +import { ErrableFormRow } from '../../../../form_errors'; +import { STRUCTURE_TEMPLATE_NAME } from '../../../../../../store/constants'; + +export class TemplateSelection extends PureComponent { + static propTypes = { + fetchIndexTemplates: PropTypes.func.isRequired, + setSelectedIndexTemplate: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + selectedIndexTemplateName: PropTypes.string.isRequired, + templateOptions: PropTypes.array.isRequired, + errors: PropTypes.object.isRequired, + isShowingErrors: PropTypes.bool.isRequired, + }; + + componentWillMount() { + this.props.fetchIndexTemplates(); + } + + render() { + const { + setSelectedIndexTemplate, + validate, + + templateOptions, + selectedIndexTemplateName, + errors, + isShowingErrors, + } = this.props; + + // const noMatchingIndicesWarning = + // affectedIndices.length > 0 ? ( + // + //

+ // The selected index template `{selectedIndexTemplateName}` matches + // existing indices which will not be affected by these changes. + //

+ //
+ // ) : null; + + return ( + + {/* {noMatchingIndicesWarning} */} + + Learn how to{' '} + + add a new index template + . + + } + > + { + await setSelectedIndexTemplate(e.target.value); + validate(); + }} + options={templateOptions} + /> + + + ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js new file mode 100644 index 0000000000000..20271f5f66ec2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js @@ -0,0 +1,10 @@ +/* + * 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 { IndexTemplate } from './index_template'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js new file mode 100644 index 0000000000000..ee693720a53be --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js @@ -0,0 +1,85 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { TemplateSelection } from './components/template_selection'; +import { Configuration } from './components/configuration'; + +import { + EuiSpacer, + EuiTitle, + EuiHorizontalRule, + EuiButton +} from '@elastic/eui'; +import { hasErrors } from '../../../../lib/find_errors'; +import { STRUCTURE_TEMPLATE_SELECTION, STRUCTURE_CONFIGURATION } from '../../../../store/constants'; + +export class IndexTemplate extends Component { + static propTypes = { + done: PropTypes.func.isRequired, + + errors: PropTypes.object, + }; + + constructor(props) { + super(props); + this.state = { + isShowingErrors: false + }; + } + + validate = async () => { + await this.props.validate(); + const noErrors = !hasErrors(this.props.errors); + return noErrors; + } + + submit = async () => { + this.setState({ isShowingErrors: true }); + if (await this.validate()) { + this.props.done(); + } + } + + render() { + const { errors } = this.props; + const { isShowingErrors } = this.state; + + return ( +
+ +

Select a template

+
+ + + + + + + Next + +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js new file mode 100644 index 0000000000000..bfeb21f8d32f1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js @@ -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 { connect } from 'react-redux'; +import { ColdPhase as PresentationComponent } from './cold_phase'; +import { + getNodeOptions, + getPhase, + getPhaseData +} from '../../../../../../store/selectors'; +import { setPhaseData, fetchNodes } from '../../../../../../store/actions'; +import { + PHASE_COLD, + PHASE_WARM, + PHASE_REPLICA_COUNT +} from '../../../../../../store/constants'; + +export const ColdPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_COLD), + nodeOptions: getNodeOptions(state), + warmPhaseReplicaCount: getPhaseData(state, PHASE_WARM, PHASE_REPLICA_COUNT) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value), + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js new file mode 100644 index 0000000000000..7436558c68f46 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -0,0 +1,223 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiAccordion, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiSwitch, + EuiButtonEmpty, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_NODE_ATTRS, + PHASE_REPLICA_COUNT +} from '../../../../../../store/constants'; +import { ErrableFormRow } from '../../../../form_errors'; + +export class ColdPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, + [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired + }).isRequired, + warmPhaseReplicaCount: PropTypes.number.isRequired, + nodeOptions: PropTypes.array.isRequired + }; + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + setPhaseData, + validate, + + phaseData, + nodeOptions, + warmPhaseReplicaCount, + errors, + isShowingErrors + } = this.props; + + return ( + + +
+ +
+
+ + +

Cold phase

+
+ + +

+ This phase is optional. Re-allocate your indices again and + modify the number of replicas. +

+
+
+ {isShowingErrors ? ( + + +

This phase contains errors that need to be fixed.

+
+
+ ) : null} +
+ + } + buttonClassName="ilmAccordion__button" + buttonContentClassName="ilmAccordion__buttonContent" + extraAction={ + setPhaseData(PHASE_ENABLED, e.target.checked)} + label="Enable this phase" + /> + } + > +
+ +

Configuration

+
+ + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + + + + + + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + validate(); + }} + /> + + + + + + setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) + } + > + Set to same as warm phase + + + + +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js new file mode 100644 index 0000000000000..e0d70ceb57726 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js @@ -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 { ColdPhase } from './cold_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js new file mode 100644 index 0000000000000..661489d2d9aa9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { connect } from 'react-redux'; +import { DeletePhase as PresentationComponent } from './delete_phase'; +import { getPhase } from '../../../../../../store/selectors'; +import { setPhaseData } from '../../../../../../store/actions'; +import { PHASE_DELETE } from '../../../../../../store/constants'; + +export const DeletePhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_DELETE) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_DELETE, key, value) + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js new file mode 100644 index 0000000000000..9b1998df49500 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -0,0 +1,155 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiAccordion, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiSwitch, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, +} from '../../../../../../store/constants'; +import { ErrableFormRow } from '../../../../form_errors'; + +export class DeletePhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired + }).isRequired + }; + + render() { + const { + setPhaseData, + validate, + phaseData, + errors, + isShowingErrors + } = this.props; + + return ( + + +
+ +
+
+ + +

Delete phase

+
+ + +

+ This phase is optional. Delete your indices after a + configured amount of time. +

+
+
+ {isShowingErrors ? ( + + +

This phase contains errors that need to be fixed.

+
+
+ ) : null} +
+ + } + buttonClassName="ilmAccordion__button" + buttonContentClassName="ilmAccordion__buttonContent" + extraAction={ + setPhaseData(PHASE_ENABLED, e.target.checked)} + label="Enable this phase" + /> + } + > +
+ +

Configuration

+
+ + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js new file mode 100644 index 0000000000000..5f909ab2c0f79 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js @@ -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 { DeletePhase } from './delete_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js new file mode 100644 index 0000000000000..1db810b49c19c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js @@ -0,0 +1,23 @@ +/* + * 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 { connect } from 'react-redux'; +import { HotPhase as PresentationComponent } from './hot_phase'; +import { getPhase } from '../../../../../../store/selectors'; +import { setPhaseData } from '../../../../../../store/actions'; +import { PHASE_HOT } from '../../../../../../store/constants'; + +export const HotPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_HOT) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_HOT, key, value) + }, +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js new file mode 100644 index 0000000000000..4111e11a31c4d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -0,0 +1,238 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiAccordion, + EuiFieldNumber, + EuiSelect, + EuiSwitch +} from '@elastic/eui'; +import { + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_ROLLOVER_ENABLED, + MAX_SIZE_TYPE_DOCUMENT +} from '../../../../../../store/constants'; + +import { ErrableFormRow } from '../../../../form_errors'; + +export class HotPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MAX_AGE]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MAX_AGE_UNITS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MAX_SIZE_STORED]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: PropTypes.string.isRequired + }).isRequired + }; + + render() { + const { + validate, + setPhaseData, + + phaseData, + isShowingErrors, + errors, + } = this.props; + + return ( + + +
+ +
+
+ + +

Hot phase

+
+ + +

This phase is required. Rollover data by time and size.

+
+
+ {isShowingErrors ? ( + + +

This phase contains errors that need to be fixed.

+
+
+ ) : null} +
+ + } + buttonClassName="ilmAccordion__button" + buttonContentClassName="ilmAccordion__buttonContent" + > +
+ +

Rollover configuration

+
+ + { + await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); + validate(); + }} + label="Enable rollover" + /> + {phaseData[PHASE_ROLLOVER_ENABLED] ? ( + + + {/* + + + { + showErrorsFor(PHASE_ROLLOVER_ALIAS); + setPhaseData(PHASE_ROLLOVER_ALIAS, e.target.value); + }} + /> + + + */} + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED, + e.target.value + ); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'gb', text: 'gigabytes' }, + { value: MAX_SIZE_TYPE_DOCUMENT, text: 'documents' } + ]} + /> + + + + + + + + { + await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_AGE_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + + + ) : null} +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js new file mode 100644 index 0000000000000..114e34c3ef4d0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js @@ -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 { HotPhase } from './hot_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js new file mode 100644 index 0000000000000..7eb5def486c87 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js @@ -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 { WarmPhase } from './warm_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js new file mode 100644 index 0000000000000..b224e9b232769 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js @@ -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 { connect } from 'react-redux'; +import { WarmPhase as PresentationComponent } from './warm_phase'; +import { + getNodeOptions, + getPhase, + getSelectedReplicaCount, + getSelectedPrimaryShardCount +} from '../../../../../../store/selectors'; +import { setPhaseData, fetchNodes } from '../../../../../../store/actions'; +import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../../../store/constants'; + +export const WarmPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_WARM), + hotPhaseReplicaCount: getSelectedReplicaCount(state), + hotPhasePrimaryShardCount: getSelectedPrimaryShardCount(state), + hotPhaseRolloverEnabled: getPhase(state, PHASE_HOT)[PHASE_ROLLOVER_ENABLED], + nodeOptions: getNodeOptions(state) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_WARM, key, value), + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js new file mode 100644 index 0000000000000..dfd70d0876767 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -0,0 +1,358 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiAccordion, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiSwitch, + EuiButtonEmpty, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_ALIAS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_NODE_ATTRS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS +} from '../../../../../../store/constants'; +import { ErrableFormRow } from '../../../../form_errors'; + +export class WarmPhase extends Component { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, + [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired + }).isRequired, + + hotPhaseReplicaCount: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + hotPhasePrimaryShardCount: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + + nodeOptions: PropTypes.array.isRequired + }; + + constructor(props) { + super(props); + this.state = { + applyOnRollover: false + }; + } + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + validate, + setPhaseData, + + phaseData, + hotPhaseReplicaCount, + hotPhasePrimaryShardCount, + nodeOptions, + errors, + isShowingErrors, + hotPhaseRolloverEnabled + } = this.props; + + return ( + + +
+ +
+
+ + +

Warm phase

+
+ + +

+ This phase is optional. Re-allocate indices, redefine number + of active shards, replicas, and compress even further. +

+
+
+ {isShowingErrors ? ( + + +

This phase contains errors that need to be fixed.

+
+
+ ) : null} +
+ + } + buttonClassName="ilmAccordion__button" + buttonContentClassName="ilmAccordion__buttonContent" + extraAction={ + { + await setPhaseData(PHASE_ENABLED, e.target.checked); + validate(); + }} + label="Enable this phase" + /> + } + > +
+ +

Configuration

+
+ + {hotPhaseRolloverEnabled ? ( + + + { + await this.setState({ applyOnRollover: e.target.checked }); + validate(); + }} + /> + + + ) : null} + {!this.state.applyOnRollover ? ( + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_AFTER_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + + ) : null} + + + + + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_REPLICA_COUNT, + hotPhaseReplicaCount + ); + validate(); + }} + > + Set to same as hot phase + + + + + + + + +

Shrink

+
+ + + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + e.target.value + ); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + hotPhasePrimaryShardCount + ); + validate(); + }} + > + Set to same as hot phase + + + + + + + + +

Force merge

+
+ + + { + await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); + validate(); + }} + /> + + + + + { + await setPhaseData(PHASE_FORCE_MERGE_SEGMENTS, e.target.value); + validate(); + }} + /> + +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js new file mode 100644 index 0000000000000..d8a885b0b32f3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js @@ -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 { PolicyConfiguration } from './policy_configuration.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js new file mode 100644 index 0000000000000..a64806993fb5c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { connect } from 'react-redux'; +import { PolicyConfiguration as PresentationComponent } from './policy_configuration'; +import { + getSelectedPolicyName, + getAffectedIndexTemplates, + getSelectedIndexTemplateName, + getBootstrapEnabled, + getIndexName, + getAliasName, + getSaveAsNewPolicy, + getSelectedOriginalPolicyName +} from '../../../../store/selectors'; +import { + setBootstrapEnabled, + setIndexName, + setAliasName, + setSelectedPolicyName, + setSaveAsNewPolicy +} from '../../../../store/actions'; + +export const PolicyConfiguration = connect( + state => ({ + selectedPolicyName: getSelectedPolicyName(state), + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + affectedIndexTemplates: getAffectedIndexTemplates(state), + bootstrapEnabled: getBootstrapEnabled(state), + indexName: getIndexName(state), + aliasName: getAliasName(state), + saveAsNewPolicy: getSaveAsNewPolicy(state), + originalPolicyName: getSelectedOriginalPolicyName(state) + }), + { + setBootstrapEnabled, + setIndexName, + setAliasName, + setSelectedPolicyName, + setSaveAsNewPolicy + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js new file mode 100644 index 0000000000000..fbc308adb65c0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -0,0 +1,254 @@ +/* + * 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, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiTitle, + EuiSpacer, + EuiCallOut, + EuiHorizontalRule, + EuiButton, + EuiFormRow, + EuiSwitch, + EuiFieldText, +} from '@elastic/eui'; +import { HotPhase } from './components/hot_phase'; +import { WarmPhase } from './components/warm_phase'; +import { DeletePhase } from './components/delete_phase'; +import { ColdPhase } from './components/cold_phase'; +import { + PHASE_HOT, + PHASE_COLD, + PHASE_DELETE, + PHASE_WARM, + STRUCTURE_POLICY_NAME, + STRUCTURE_INDEX_NAME, + STRUCTURE_ALIAS_NAME, +} from '../../../../store/constants'; +import { hasErrors } from '../../../../lib/find_errors'; +import { ErrableFormRow } from '../../form_errors'; + +export class PolicyConfiguration extends Component { + static propTypes = { + setSelectedPolicyName: PropTypes.func.isRequired, + setSaveAsNewPolicy: PropTypes.func.isRequired, + setIndexName: PropTypes.func.isRequired, + setAliasName: PropTypes.func.isRequired, + setBootstrapEnabled: PropTypes.func.isRequired, + done: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + affectedIndexTemplates: PropTypes.array.isRequired, + selectedIndexTemplateName: PropTypes.string.isRequired, + selectedPolicyName: PropTypes.string.isRequired, + saveAsNewPolicy: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + bootstrapEnabled: PropTypes.bool.isRequired, + indexName: PropTypes.string.isRequired, + aliasName: PropTypes.string.isRequired, + originalPolicyName: PropTypes.string, + }; + + constructor(props) { + super(props); + this.state = { + isShowingErrors: false, + }; + } + + validate = async () => { + await this.props.validate(); + const noErrors = !hasErrors(this.props.errors); + return noErrors; + }; + + submit = async () => { + this.setState({ isShowingErrors: true }); + if (await this.validate()) { + this.props.done(); + } + }; + + render() { + const { + setSelectedPolicyName, + setSaveAsNewPolicy, + setBootstrapEnabled, + setIndexName, + setAliasName, + validate, + + affectedIndexTemplates, + selectedIndexTemplateName, + selectedPolicyName, + saveAsNewPolicy, + errors, + bootstrapEnabled, + indexName, + aliasName, + originalPolicyName, + } = this.props; + + const { isShowingErrors } = this.state; + + const singleTemplate = ( + + This policy is only attached to the selected template{' '} + {selectedIndexTemplateName}. + + ); + + const multiTemplate = ( + + This policy is attached to{' '} + {affectedIndexTemplates.length - 1} other template(s){' '} + besides {selectedIndexTemplateName}. + + ); + + const warningMessage = + affectedIndexTemplates.length === 1 && + affectedIndexTemplates[0] === selectedIndexTemplateName + ? singleTemplate + : multiTemplate; + + return ( +
+ +

Edit policy: {selectedPolicyName}

+
+ + + Only the hot phase is required. + + } + /> + + + + + + + + + + + + + + + {originalPolicyName ? ( + + { + await setSaveAsNewPolicy(e.target.checked); + validate(); + }} + label={ + + Save this as a new policy so it does not + effect other templates. + + } + /> + + ) : null} + {saveAsNewPolicy ? ( + + { + await setSelectedPolicyName(e.target.value); + validate(); + }} + /> + + ) : null} + + + setBootstrapEnabled(e.target.checked)} + label={Create an index and alias for this template} + /> + + {bootstrapEnabled ? ( + + + { + await setIndexName(e.target.value); + validate(); + }} + /> + + + { + await setAliasName(e.target.value); + validate(); + }} + /> + + + ) : null} + + + + + + Next + +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js new file mode 100644 index 0000000000000..447bc384c368d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js @@ -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 { PolicySelection } from './policy_selection.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js new file mode 100644 index 0000000000000..abe56ef4db032 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js @@ -0,0 +1,29 @@ +/* + * 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 { connect } from 'react-redux'; +import { PolicySelection as PresentationComponent } from './policy_selection'; +import { + getPolicies, getExistingPolicyName, +} from '../../../../store/selectors'; +import { + fetchPolicies, + setSelectedPolicy, +} from '../../../../store/actions'; + +export const PolicySelection = connect( + state => ({ + policies: getPolicies(state), + existingPolicyName: getExistingPolicyName(state), + }), + { + fetchPolicies, + setSelectedPolicy, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js new file mode 100644 index 0000000000000..52fe3a6cb1806 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -0,0 +1,117 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiSpacer, + EuiPanel, + EuiText +} from '@elastic/eui'; + +export class PolicySelection extends Component { + static propTypes = { + fetchPolicies: PropTypes.func.isRequired, + setSelectedPolicy: PropTypes.func.isRequired, + done: PropTypes.func.isRequired, + + existingPolicyName: PropTypes.string.isRequired, + policies: PropTypes.array.isRequired + }; + + componentWillMount() { + this.props.fetchPolicies(); + } + + selectPolicy(policy) { + this.props.setSelectedPolicy(policy); + this.props.done(); + } + + render() { + const { policies, existingPolicyName } = this.props; + + return ( +
+ + + +

Select a policy to start from

+
+ +

+ You can edit existing policies and save them under a new name + later +

+
+
+ {/* + + + + Hot + + + + + Warm + + + + + Cold + + + + + Delete + + + + */} +
+ + + + this.selectPolicy(null)} + > + + +

New policy

+
+
+
+ {policies.map(item => ( + + this.selectPolicy(item)} + > + +

+ {item.name} + {existingPolicyName === item.name ? '*' : ''} +

+
+
+
+ ))} +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js new file mode 100644 index 0000000000000..defebab0131ea --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js @@ -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 { Review } from './review.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js new file mode 100644 index 0000000000000..a809576f77dd9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js @@ -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 { connect } from 'react-redux'; +import { Review as PresentationComponent } from './review'; +import { + getSelectedIndexTemplateName, + getAffectedIndexTemplates, + getTemplateDiff, + getLifecycle, + getSelectedPolicyName, + getSaveAsNewPolicy, + getSelectedOriginalPolicyName, + getAliasName, + getBootstrapEnabled, +} from '../../../../store/selectors'; +import { + setSelectedPolicyName, + setSaveAsNewPolicy, +} from '../../../../store/actions'; + +export const Review = connect( + state => ({ + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + affectedIndexTemplates: getAffectedIndexTemplates(state), + templateDiff: getTemplateDiff(state), + lifecycle: getLifecycle(state), + bootstrapEnabled: getBootstrapEnabled(state), + aliasName: getAliasName(state), + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + selectedPolicyName: getSelectedPolicyName(state), + saveAsNewPolicy: getSaveAsNewPolicy(state), + originalPolicyName: getSelectedOriginalPolicyName(state), + }), + { + setSelectedPolicyName, + setSaveAsNewPolicy, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js new file mode 100644 index 0000000000000..d5fbb7991bdf3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -0,0 +1,177 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import DiffEditor from 'react-ace/lib/diff'; +import './review.less'; + +import 'brace/theme/github'; +import 'brace/mode/json'; +import 'brace/snippets/json'; +import 'brace/ext/language_tools'; + +import { + EuiTitle, + EuiSpacer, + EuiHorizontalRule, + EuiButton, + EuiFlexItem, + EuiFlexGrid, + EuiPanel, + EuiText, +} from '@elastic/eui'; +import { getAffectedIndices } from '../../../../api'; + +export class Review extends Component { + static propTypes = { + setSelectedPolicyName: PropTypes.func.isRequired, + setSaveAsNewPolicy: PropTypes.func.isRequired, + done: PropTypes.func.isRequired, + + selectedIndexTemplateName: PropTypes.string.isRequired, + affectedIndexTemplates: PropTypes.array.isRequired, + templateDiff: PropTypes.object.isRequired, + lifecycle: PropTypes.object.isRequired, + selectedPolicyName: PropTypes.string.isRequired, + saveAsNewPolicy: PropTypes.bool.isRequired, + originalPolicyName: PropTypes.string, + bootstrapEnabled: PropTypes.bool.isRequired, + aliasName: PropTypes.string.isRequired + }; + + constructor(props) { + super(props); + this.state = { + selectedTab: 1, + affectedIndices: [] + }; + } + + async componentWillMount() { + const affectedIndices = await getAffectedIndices( + this.props.selectedIndexTemplateName, + this.props.selectedPolicyName + ); + this.setState({ affectedIndices }); + } + + render() { + const { + done, + + selectedIndexTemplateName, + affectedIndexTemplates, + templateDiff, + lifecycle, + bootstrapEnabled, + aliasName + } = this.props; + + const { affectedIndices } = this.state; + + return ( +
+ +

Changes that will occur

+
+ + + + + +

{affectedIndexTemplates.length}

+
+ +

+ Index templates affected by this change (to the selected policy): +

+
    + {affectedIndexTemplates.map(template => ( +
  • {template}
  • + ))} +
+
+
+
+ + + +

{affectedIndices.length}

+
+ +

+ Indices affected by this change (to the selected policy): +

+
    + {affectedIndices.map(index =>
  • {index}
  • )} +
+
+
+
+ {bootstrapEnabled ? ( + + + +

New alias

+
+ +

Point to these new aliases going forward:

+
    +
  • + READ: {aliasName} +
  • +
  • + WRITE: {aliasName} +
  • +
+
+
+
+ ) : null} +
+ + +

+ We will be changing the index template named `{ + selectedIndexTemplateName + }` +

+
+ + + + + + done(lifecycle)} + > + Looks good, make these changes + +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less new file mode 100644 index 0000000000000..862b1bd58e8c8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less @@ -0,0 +1,5 @@ +.codeMarker { + background: #FFF677; + position:absolute; + z-index:20 +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js new file mode 100644 index 0000000000000..fc3c29c4beb0c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js @@ -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, { cloneElement, Children, Fragment } from 'react'; +import { EuiFormRow } from '@elastic/eui'; + +export const ErrableFormRow = ({ + errorKey, + isShowingErrors, + errors, + children, + ...rest +}) => { + return ( + 0 + } + error={errors[errorKey]} + {...rest} + > + + {Children.map(children, child => cloneElement(child, { + isInvalid: isShowingErrors && errors[errorKey].length > 0, + }))} + + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js new file mode 100644 index 0000000000000..3a6f61a7c0492 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js @@ -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 { Wizard } from './wizard.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js new file mode 100644 index 0000000000000..57eb484bcc0fd --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js @@ -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 { connect } from 'react-redux'; +import { Wizard as PresentationComponent } from './wizard'; +import { saveLifecycle } from '../../store/actions'; +import { + getIndexTemplatePatch, + getBootstrapEnabled, + getIndexName, + getAliasName, + validateLifecycle, +} from '../../store/selectors'; + +export const Wizard = connect( + state => ({ + indexTemplatePatch: getIndexTemplatePatch(state), + bootstrapEnabled: getBootstrapEnabled(state), + indexName: getIndexName(state), + aliasName: getAliasName(state), + validateLifecycle: () => validateLifecycle(state), + }), + { + saveLifecycle + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js new file mode 100644 index 0000000000000..d2639b268da2f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -0,0 +1,165 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; +import { IndexTemplate } from './components/index_template'; +import { PolicySelection } from './components/policy_selection'; +import { PolicyConfiguration } from './components/policy_configuration'; +import { Review } from './components/review'; +import { + EuiPage, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiStepsHorizontal +} from '@elastic/eui'; +import { bootstrap } from '../../api'; +import { + STRUCTURE_INDEX_TEMPLATE, + STRUCTURE_POLICY_CONFIGURATION +} from '../../store/constants'; + +export class Wizard extends Component { + static propTypes = { + saveLifecycle: PropTypes.func.isRequired, + validateLifecycle: PropTypes.func.isRequired, + + indexTemplatePatch: PropTypes.object.isRequired, + bootstrapEnabled: PropTypes.bool.isRequired, + indexName: PropTypes.string.isRequired, + aliasName: PropTypes.string.isRequired + }; + + constructor(props) { + super(props); + + this.state = { + selectedStep: 1, + errors: this.getErrors() + }; + } + + onSelectedStepChanged = selectedStep => { + this.setState({ + selectedStep + }); + }; + + getErrors = () => { + return this.props.validateLifecycle(); + }; + + validate = () => { + const errors = this.getErrors(); + this.setState({ errors }); + }; + + addLifecycle = async lifecycle => { + await this.props.saveLifecycle(lifecycle, this.props.indexTemplatePatch); + await this.bootstrap(); + // this.onSelectedStepChanged(5); + }; + + bootstrap = async () => { + const { indexName, aliasName, bootstrapEnabled } = this.props; + if (!bootstrapEnabled) { + return; + } + + const response = await bootstrap(indexName, aliasName); + if (response && response.acknowledged) { + toastNotifications.addSuccess( + 'Successfully bootstrapped an index and alias' + ); + // Bounce back to management + // this.onSelectedStepChanged(1); + // TODO: also clear state? + } else { + toastNotifications.addDanger('Unable to bootstrap an index and alias'); + } + }; + + renderContent() { + const { selectedStep, errors } = this.state; + + switch (selectedStep) { + case 1: + return ( + this.onSelectedStepChanged(2)} + /> + ); + case 2: + return this.onSelectedStepChanged(3)} />; + case 3: + return ( + this.onSelectedStepChanged(4)} + /> + ); + case 4: + return ; + } + } + + render() { + const steps = [ + { + title: 'Select a template', + isSelected: this.state.selectedStep === 1, + isComplete: this.state.selectedStep > 1, + onClick: () => this.onSelectedStepChanged(1) + }, + { + title: 'Select or create policy', + isSelected: this.state.selectedStep === 2, + isComplete: this.state.selectedStep > 2, + disabled: this.state.selectedStep < 2, + onClick: () => this.onSelectedStepChanged(2) + }, + { + title: 'Configure policy', + isSelected: this.state.selectedStep === 3, + isComplete: this.state.selectedStep > 3, + disabled: this.state.selectedStep < 3, + onClick: () => this.onSelectedStepChanged(3) + }, + { + title: 'Review and save', + isSelected: this.state.selectedStep === 4, + isComplete: this.state.selectedStep > 4, + disabled: this.state.selectedStep < 4, + onClick: () => this.onSelectedStepChanged(4) + } + ]; + + return ( + + + + +

Index lifecycle management

+
+
+
+ + + + {this.renderContent()} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/general.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/general.js new file mode 100644 index 0000000000000..584488d4c2b42 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/general.js @@ -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 { createAction } from 'redux-actions'; + +export const setBootstrapEnabled = createAction('SET_BOOTSTRAP_ENABLED'); +export const setIndexName = createAction('SET_INDEX_NAME'); +export const setAliasName = createAction('SET_ALIAS_NAME'); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/index.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/index.js new file mode 100644 index 0000000000000..621cbf007d3b2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/index.js @@ -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. + */ + + + + +export * from './index_template'; +export * from './nodes'; +export * from './policies'; +export * from './lifecycle'; +export * from './general'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js new file mode 100644 index 0000000000000..50eec52976059 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js @@ -0,0 +1,103 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { loadIndexTemplates, loadIndexTemplate } from '../../api'; +import { getAlias } from '../selectors'; +import { + setPhaseData, + setIndexName, + setAliasName, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + setSelectedNodeAttrs +} from '.'; +import { + PHASE_HOT, + PHASE_ROLLOVER_ALIAS, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE +} from '../constants'; + +export const fetchingIndexTemplates = createAction('FETCHING_INDEX_TEMPLATES'); +export const fetchedIndexTemplates = createAction('FETCHED_INDEX_TEMPLATES'); +export const fetchIndexTemplates = () => async dispatch => { + dispatch(fetchingIndexTemplates()); + + let templates; + try { + templates = await loadIndexTemplates(); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedIndexTemplates(templates)); +}; + +export const fetchedIndexTemplate = createAction('FETCHED_INDEX_TEMPLATE'); +export const fetchIndexTemplate = templateName => async dispatch => { + let template; + try { + template = await loadIndexTemplate(templateName); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + if (template.settings && template.settings.index) { + dispatch( + setSelectedPrimaryShardCount(template.settings.index.number_of_shards) + ); + dispatch( + setSelectedReplicaCount(template.settings.index.number_of_replicas) + ); + if ( + template.settings.index.routing && + template.settings.index.routing.allocation && + template.settings.index.routing.allocation.include + ) { + dispatch( + setSelectedNodeAttrs( + template.settings.index.routing.allocation.include.sattr_name + ) + ); + } + } + + let indexPattern = template.index_patterns[0]; + if (indexPattern.endsWith('*')) { + indexPattern = indexPattern.slice(0, -1); + } + dispatch(setIndexName(`${indexPattern}-00001`)); + dispatch(setAliasName(`${indexPattern}-alias`)); + dispatch(fetchedIndexTemplate(template)); +}; + +export const setSelectedIndexTemplateName = createAction( + 'SET_SELECTED_INDEX_TEMPLATE_NAME' +); + +export const setSelectedIndexTemplate = name => async (dispatch, getState) => { + // Await all of these to ensure they happen before the next round of validation + const promises = [ + dispatch(setSelectedIndexTemplateName(name)), + dispatch(fetchIndexTemplate(name)) + ]; + const alias = getAlias(getState()); + if (alias) { + promises.push(...[ + dispatch(setPhaseData(PHASE_HOT, PHASE_ROLLOVER_ALIAS, alias)), + dispatch(setPhaseData(PHASE_WARM, PHASE_ROLLOVER_ALIAS, alias)), + dispatch(setPhaseData(PHASE_COLD, PHASE_ROLLOVER_ALIAS, alias)), + dispatch(setPhaseData(PHASE_DELETE, PHASE_ROLLOVER_ALIAS, alias)) + ]); + } + await Promise.all(promises); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js new file mode 100644 index 0000000000000..676894d079093 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js @@ -0,0 +1,25 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { saveLifecycle as saveLifecycleApi } from '../../api'; + + +export const savedLifecycle = createAction('SAVED_LIFECYCLE'); +export const saveLifecycle = (lifecycle, indexTemplatePatch) => async dispatch => { + let saved; + try { + saved = await saveLifecycleApi(lifecycle, indexTemplatePatch); + } + catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + toastNotifications.addSuccess(`Successfully created lifecycle policy '${lifecycle.name}'`); + + dispatch(savedLifecycle(saved)); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js new file mode 100644 index 0000000000000..11b0a77be8106 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js @@ -0,0 +1,25 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { loadNodes } from '../../api'; + +export const setSelectedNodeAttrs = createAction('SET_SELECTED_NODE_ATTRS'); +export const setSelectedPrimaryShardCount = createAction('SET_SELECTED_PRIMARY_SHARED_COUNT'); +export const setSelectedReplicaCount = createAction('SET_SELECTED_REPLICA_COUNT'); +export const fetchedNodes = createAction('FETCHED_NODES'); +export const fetchNodes = () => async dispatch => { + let nodes; + try { + nodes = await loadNodes(); + } + catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedNodes(nodes)); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js new file mode 100644 index 0000000000000..47046331a4eb4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -0,0 +1,31 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { loadPolicies } from '../../api'; + +export const fetchedPolicies = createAction('FETCHED_POLICIES'); +export const fetchPolicies = () => async dispatch => { + let policies; + try { + policies = await loadPolicies(); + } + catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedPolicies(policies)); +}; + +export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); +export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); +export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); + +export const setPhaseData = createAction('SET_PHASE_DATA', (phase, key, value) => ({ phase, key, value })); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js new file mode 100644 index 0000000000000..7b6e7076d87c5 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +export const PHASE_HOT = 'hot'; +export const PHASE_WARM = 'warm'; +export const PHASE_COLD = 'cold'; +export const PHASE_DELETE = 'delete'; + +export const PHASE_ENABLED = 'phaseEnabled'; + +export const MAX_SIZE_TYPE_DOCUMENT = 'd'; + +export const PHASE_ROLLOVER_ENABLED = 'rolloverEnabled'; +export const PHASE_ROLLOVER_ALIAS = 'selectedAlias'; +export const PHASE_ROLLOVER_MAX_AGE = 'selectedMaxAge'; +export const PHASE_ROLLOVER_MAX_AGE_UNITS = 'selectedMaxAgeUnits'; +export const PHASE_ROLLOVER_MAX_SIZE_STORED = 'selectedMaxSizeStored'; +export const PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS = 'selectedMaxSizeStoredUnits'; +export const PHASE_ROLLOVER_MAX_DOC_SIZE = 'selectedMaxDocSize'; +export const PHASE_ROLLOVER_AFTER = 'selectedAfter'; +export const PHASE_ROLLOVER_AFTER_UNITS = 'selectedAfterUnits'; + +export const PHASE_FORCE_MERGE_SEGMENTS = 'selectedForceMergeSegments'; +export const PHASE_FORCE_MERGE_ENABLED = 'forceMergeEnabled'; + +export const PHASE_NODE_ATTRS = 'selectedNodeAttrs'; +export const PHASE_PRIMARY_SHARD_COUNT = 'selectedPrimaryShardCount'; +export const PHASE_REPLICA_COUNT = 'selectedReplicaCount'; + +export const PHASE_ATTRIBUTES_THAT_ARE_NUMBERS = [ + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_DOC_SIZE, + PHASE_ROLLOVER_AFTER, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, +]; + +export const STRUCTURE_INDEX_TEMPLATE = 'indexTemplate'; +export const STRUCTURE_TEMPLATE_SELECTION = 'templateSelection'; +export const STRUCTURE_TEMPLATE_NAME = 'templateName'; +export const STRUCTURE_CONFIGURATION = 'configuration'; +export const STRUCTURE_NODE_ATTRS = 'node_attrs'; +export const STRUCTURE_PRIMARY_NODES = 'primary_nodes'; +export const STRUCTURE_REPLICAS = 'replicas'; + +export const STRUCTURE_POLICY_CONFIGURATION = 'policyConfiguration'; +export const STRUCTURE_POLICY_NAME = 'policyName'; +export const STRUCTURE_INDEX_NAME = 'indexName'; +export const STRUCTURE_ALIAS_NAME = 'aliasName'; + +export const ERROR_STRUCTURE = { + [STRUCTURE_INDEX_TEMPLATE]: { + [STRUCTURE_TEMPLATE_SELECTION]: { + [STRUCTURE_TEMPLATE_NAME]: [] + }, + [STRUCTURE_CONFIGURATION]: { + [STRUCTURE_NODE_ATTRS]: [], + [STRUCTURE_PRIMARY_NODES]: [], + [STRUCTURE_REPLICAS]: [] + } + }, + [STRUCTURE_POLICY_CONFIGURATION]: { + [STRUCTURE_POLICY_NAME]: [], + [STRUCTURE_INDEX_NAME]: [], + [STRUCTURE_ALIAS_NAME]: [], + [PHASE_HOT]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_MAX_AGE]: [], + [PHASE_ROLLOVER_MAX_AGE_UNITS]: [], + [PHASE_ROLLOVER_MAX_SIZE_STORED]: [], + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: [], + [PHASE_ROLLOVER_MAX_DOC_SIZE]: [] + }, + [PHASE_WARM]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_AFTER]: [], + [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_NODE_ATTRS]: [], + [PHASE_PRIMARY_SHARD_COUNT]: [], + [PHASE_REPLICA_COUNT]: [], + [PHASE_FORCE_MERGE_SEGMENTS]: [], + }, + [PHASE_COLD]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_AFTER]: [], + [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_NODE_ATTRS]: [], + [PHASE_REPLICA_COUNT]: [], + }, + [PHASE_DELETE]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_AFTER]: [], + [PHASE_ROLLOVER_AFTER_UNITS]: [], + }, + } +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/index.js b/x-pack/plugins/index_lifecycle_management/public/store/index.js new file mode 100644 index 0000000000000..808eb489bf913 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/index.js @@ -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 { indexLifecycleManagementStore } from './store'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js new file mode 100644 index 0000000000000..abb56f5cdae2f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js @@ -0,0 +1,38 @@ +/* + * 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 { handleActions } from 'redux-actions'; +import { setIndexName, setAliasName, setBootstrapEnabled } from '../actions/general'; + +const defaultState = { + bootstrapEnabled: false, + indexName: '', + aliasName: '', +}; + +export const general = handleActions({ + [setIndexName](state, { payload: indexName }) { + return { + ...state, + indexName, + }; + }, + [setAliasName](state, { payload: aliasName }) { + return { + ...state, + aliasName, + }; + }, + [setBootstrapEnabled](state, { payload: bootstrapEnabled }) { + return { + ...state, + bootstrapEnabled, + }; + } +}, defaultState); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js new file mode 100644 index 0000000000000..7225d9e0be9f5 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js @@ -0,0 +1,21 @@ +/* + * 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 { combineReducers } from 'redux'; +import { indexTemplate } from './index_template'; +import { nodes } from './nodes'; +import { policies } from './policies'; +import { general } from './general'; + +export const indexLifecycleManagement = combineReducers({ + indexTemplate, + nodes, + policies, + general, +}); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js new file mode 100644 index 0000000000000..19bc7af01954c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js @@ -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 { handleActions } from 'redux-actions'; +import { + fetchingIndexTemplates, + fetchedIndexTemplates, + setSelectedIndexTemplateName, + fetchedIndexTemplate +} from '../actions/index_template'; + +const defaultState = { + isLoading: false, + fullSelectedIndexTemplate: null, + selectedIndexTemplateName: '', + indexTemplates: null, +}; + +export const indexTemplate = handleActions( + { + [fetchingIndexTemplates](state) { + return { + ...state, + isLoading: true + }; + }, + [fetchedIndexTemplates](state, { payload: indexTemplates }) { + return { + ...state, + isLoading: false, + indexTemplates + }; + }, + [fetchedIndexTemplate](state, { payload: fullSelectedIndexTemplate }) { + return { + ...state, + fullSelectedIndexTemplate, + }; + }, + [setSelectedIndexTemplateName](state, { payload: selectedIndexTemplateName }) { + return { + ...state, + selectedIndexTemplateName + }; + } + }, + defaultState +); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js new file mode 100644 index 0000000000000..88d5b9a0f6d53 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js @@ -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 { handleActions } from 'redux-actions'; +import { + fetchedNodes, + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount +} from '../actions/nodes'; + +const defaultState = { + isLoading: false, + selectedNodeAttrs: '', + selectedPrimaryShardCount: 1, + selectedReplicaCount: 1, + nodes: [] +}; + +export const nodes = handleActions( + { + [fetchedNodes](state, { payload: nodes }) { + return { + ...state, + isLoading: false, + nodes + }; + }, + [setSelectedNodeAttrs](state, { payload: selectedNodeAttrs }) { + return { + ...state, + selectedNodeAttrs + }; + }, + [setSelectedPrimaryShardCount](state, { payload }) { + let selectedPrimaryShardCount = parseInt(payload); + if (isNaN(selectedPrimaryShardCount)) { + selectedPrimaryShardCount = ''; + } + return { + ...state, + selectedPrimaryShardCount + }; + }, + [setSelectedReplicaCount](state, { payload }) { + let selectedReplicaCount = parseInt(payload); + if (isNaN(selectedReplicaCount)) { + selectedReplicaCount = ''; + } + + return { + ...state, + selectedReplicaCount + }; + } + }, + defaultState +); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js new file mode 100644 index 0000000000000..8b68c8a5d1ceb --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -0,0 +1,173 @@ +/* + * 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 { handleActions } from 'redux-actions'; +import { + fetchedPolicies, + setSelectedPolicy, + setSelectedPolicyName, + setSaveAsNewPolicy, + setPhaseData +} from '../actions'; +import { policyFromES } from '../selectors'; +import { + PHASE_HOT, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE, + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_NODE_ATTRS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MAX_DOC_SIZE +} from '../constants'; + +export const defaultWarmPhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_FORCE_MERGE_SEGMENTS]: '', + [PHASE_FORCE_MERGE_ENABLED]: false, + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 's', + [PHASE_NODE_ATTRS]: '', + [PHASE_PRIMARY_SHARD_COUNT]: '', + [PHASE_REPLICA_COUNT]: '' +}; + +export const defaultHotPhase = { + [PHASE_ENABLED]: true, + [PHASE_ROLLOVER_ENABLED]: true, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_MAX_AGE]: '', + [PHASE_ROLLOVER_MAX_AGE_UNITS]: 's', + [PHASE_ROLLOVER_MAX_SIZE_STORED]: '', + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: 'gb', + [PHASE_ROLLOVER_MAX_DOC_SIZE]: '', +}; + +export const defaultColdPhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 's', + [PHASE_NODE_ATTRS]: '', + [PHASE_REPLICA_COUNT]: '' +}; + +export const defaultDeletePhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 's' +}; + +const defaultPolicy = { + name: '', + saveAsNew: true, + phases: { + [PHASE_HOT]: defaultHotPhase, + [PHASE_WARM]: defaultWarmPhase, + [PHASE_COLD]: defaultColdPhase, + [PHASE_DELETE]: defaultDeletePhase + } +}; + +const defaultState = { + isLoading: false, + originalPolicyName: undefined, + selectedPolicy: defaultPolicy, + policies: [] +}; + +export const policies = handleActions( + { + [fetchedPolicies](state, { payload: policies }) { + return { + ...state, + isLoading: false, + policies + }; + }, + [setSelectedPolicy](state, { payload: selectedPolicy }) { + if (selectedPolicy === null) { + return { + ...state, + selectedPolicy: defaultPolicy, + }; + } + + return { + ...state, + originalPolicyName: selectedPolicy.name, + selectedPolicy: { + ...defaultPolicy, + ...policyFromES(selectedPolicy) + } + }; + }, + [setSelectedPolicyName](state, { payload: name }) { + return { + ...state, + selectedPolicy: { + ...state.selectedPolicy, + name + } + }; + }, + [setSaveAsNewPolicy](state, { payload: saveAsNew }) { + return { + ...state, + selectedPolicy: { + ...state.selectedPolicy, + saveAsNew + } + }; + }, + [setPhaseData](state, { payload }) { + const { phase, key } = payload; + + let value = payload.value; + if (PHASE_ATTRIBUTES_THAT_ARE_NUMBERS.includes(key)) { + value = parseInt(value); + if (isNaN(value)) { + value = ''; + } + } + + return { + ...state, + selectedPolicy: { + ...state.selectedPolicy, + phases: { + ...state.selectedPolicy.phases, + [phase]: { + ...state.selectedPolicy.phases[phase], + [key]: value + } + } + } + }; + } + }, + defaultState +); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js new file mode 100644 index 0000000000000..41459d1bbb2c8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +export const getBootstrapEnabled = state => state.general.bootstrapEnabled; +export const getIndexName = state => state.general.indexName; +export const getAliasName = state => state.general.aliasName; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js new file mode 100644 index 0000000000000..621cbf007d3b2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js @@ -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. + */ + + + + +export * from './index_template'; +export * from './nodes'; +export * from './policies'; +export * from './lifecycle'; +export * from './general'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js new file mode 100644 index 0000000000000..620dcd53be644 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -0,0 +1,170 @@ +/* + * 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 { createSelector } from 'reselect'; +import { merge, cloneDeep } from 'lodash'; +import { + getSaveAsNewPolicy, + getSelectedPolicyName, + getSelectedPrimaryShardCount, + getNodesFromSelectedNodeAttrs, + getSelectedReplicaCount, + getSelectedNodeAttrs +} from '.'; + +export const getIndexTemplates = state => state.indexTemplate.indexTemplates; +export const getIndexTemplateOptions = createSelector( + [state => getIndexTemplates(state)], + templates => { + if (!templates) { + return []; + } + + const options = templates.map(template => ({ + text: template.name, + value: template.name + })); + + options.sort((a, b) => a.text.localeCompare(b.text)); + options.unshift({ + text: '', + value: undefined + }); + + return options; + } +); +export const getSelectedIndexTemplateName = state => + state.indexTemplate.selectedIndexTemplateName; + +export const getSelectedIndexTemplate = createSelector( + [ + state => getSelectedIndexTemplateName(state), + state => getIndexTemplates(state) + ], + (selectedIndexTemplateName, allTemplates) => { + return allTemplates.find( + template => template.name === selectedIndexTemplateName + ); + } +); + +export const getFullSelectedIndexTemplate = state => state.indexTemplate.fullSelectedIndexTemplate; + +export const getExistingPolicyName = state => { + const template = getFullSelectedIndexTemplate(state); + if (template && template.settings && template.settings.index && template.settings.index.lifecycle) { + return template.settings.index.lifecycle.name; + } + return ''; +}; + +export const getAlias = state => { + const template = getSelectedIndexTemplate(state); + if (template && template.settings) { + return template.settings.indexlifecycle.rollover_alias; + } + return undefined; +}; + +// TODO: add createSelector +export const getAffectedIndexTemplates = state => { + const selectedIndexTemplateName = getSelectedIndexTemplateName(state); + const indexTemplates = [selectedIndexTemplateName]; + + const selectedPolicyName = getSelectedPolicyName(state); + const allTemplates = getIndexTemplates(state); + indexTemplates.push( + ...allTemplates.reduce((accum, template) => { + if (template.index_lifecycle_name === selectedPolicyName && template.name !== selectedIndexTemplateName) { + accum.push(template.name); + } + return accum; + }, []) + ); + + return indexTemplates; +}; + +// TODO: add createSelector +export const getAffectedIndexPatterns = state => { + const indexPatterns = [...getSelectedIndexTemplate(state).index_patterns]; + + if (!getSaveAsNewPolicy(state)) { + const allTemplates = getIndexTemplates(state); + const selectedPolicyName = getSelectedPolicyName(state); + indexPatterns.push( + ...allTemplates.reduce((accum, template) => { + if (template.index_lifecycle_name === selectedPolicyName) { + accum.push(...template.index_patterns); + } + return accum; + }, []) + ); + } + + return indexPatterns; +}; + +export const getTemplateDiff = state => { + const fullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; + return { + originalFullIndexTemplate: fullIndexTemplate, + newFullIndexTemplate: merge(cloneDeep(fullIndexTemplate), { + settings: { + index: { + number_of_shards: '' + getSelectedPrimaryShardCount(state), + number_of_replicas: '' + getSelectedReplicaCount(state), + lifecycle: { + name: getSelectedPolicyName(state) + }, + routing: { + allocation: { + include: { + sattr_name: getSelectedNodeAttrs(state), + } + } + } + } + } + }), + // modifications: { + // settings: { + // index: { + // number_of_shards: getSelectedPrimaryShardCount(state), + // number_of_replicas: getSelectedReplicaCount(state), + // lifecycle: { + // name: getSelectedPolicyName(state), + // } + // } + // } + // } + }; +}; + +export const getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount = state => { + const primaryShardCount = getSelectedPrimaryShardCount(state); + const selectedNodeAttrsCount = getNodesFromSelectedNodeAttrs(state); + + if (selectedNodeAttrsCount === null) { + return false; + } + + return primaryShardCount > selectedNodeAttrsCount; +}; + +export const getIndexTemplatePatch = state => { + return { + indexTemplate: getSelectedIndexTemplateName(state), + primaryShardCount: getSelectedPrimaryShardCount(state), + replicaCount: getSelectedReplicaCount(state), + lifecycleName: getSelectedPolicyName(state), + nodeAttrs: getSelectedNodeAttrs(state) + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js new file mode 100644 index 0000000000000..235ebb4dd2062 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -0,0 +1,200 @@ +/* + * 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 { + PHASE_HOT, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE, + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_SIZE_STORED, + STRUCTURE_INDEX_TEMPLATE, + STRUCTURE_CONFIGURATION, + STRUCTURE_NODE_ATTRS, + STRUCTURE_PRIMARY_NODES, + STRUCTURE_REPLICAS, + STRUCTURE_TEMPLATE_SELECTION, + STRUCTURE_TEMPLATE_NAME, + STRUCTURE_POLICY_NAME, + STRUCTURE_POLICY_CONFIGURATION, + STRUCTURE_INDEX_NAME, + STRUCTURE_ALIAS_NAME, + ERROR_STRUCTURE, + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS +} from '../constants'; +import { + getPhase, + getPhases, + phaseToES, + getSelectedPolicyName, + getSelectedIndexTemplateName, + isNumber, + getSelectedNodeAttrs, + getSelectedPrimaryShardCount, + getSelectedReplicaCount, + getSaveAsNewPolicy, + getSelectedOriginalPolicyName, + getBootstrapEnabled, + getIndexName, + getAliasName, +} from '.'; + +export const validatePhase = (type, phase) => { + const errors = {}; + + if (!phase[PHASE_ENABLED]) { + return errors; + } + + if (phase[PHASE_ROLLOVER_ENABLED]) { + if ( + !isNumber(phase[PHASE_ROLLOVER_MAX_AGE]) && + !isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED]) + ) { + errors[PHASE_ROLLOVER_MAX_AGE] = [ + 'A rollover requires a max age, max docs, or max size.' + ]; + errors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ + 'A rollover requires a max age, max docs, or max size.' + ]; + } + } + + for (const numberedAttribute of PHASE_ATTRIBUTES_THAT_ARE_NUMBERS) { + if (phase.hasOwnProperty(numberedAttribute) && !phase[numberedAttribute] === '') { + if (!isNumber(phase[numberedAttribute])) { + errors[numberedAttribute].push('A number is required.'); + } + else if (phase[numberedAttribute] < 0) { + errors[numberedAttribute].push('Only positive numbers allowed.'); + } + } + } + + return errors; +}; + +export const validateLifecycle = state => { + // This method of deep copy does not always work but it should be fine here + const errors = JSON.parse(JSON.stringify(ERROR_STRUCTURE)); + + if (!getSelectedIndexTemplateName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][ + STRUCTURE_TEMPLATE_NAME + ].push('An index template is required.'); + } + + if (!getSelectedNodeAttrs(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_NODE_ATTRS + ].push('A node must be selected.'); + } + + if (!isNumber(getSelectedPrimaryShardCount(state))) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_PRIMARY_NODES + ].push('A value is required.'); + } + else if (getSelectedPrimaryShardCount(state) < 0) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_PRIMARY_NODES + ].push('Only positive numbers allowed.'); + } + + if (!isNumber(getSelectedReplicaCount(state))) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_REPLICAS + ].push('A value is required.'); + } + else if (getSelectedReplicaCount(state) < 0) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_REPLICAS + ].push('Only positive numbers allowed.'); + } + + if (!getSelectedPolicyName(state)) { + errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('A policy name is required.'); + } + + if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { + errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('The policy name must be different.'); + } + + // if (getSaveAsNewPolicy(state)) { + // const policyNames = getAllPolicyNamesFromTemplates(state); + // if (policyNames.includes(getSelectedPolicyName(state))) { + // errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('That policy name is already used.'); + // } + // } + + if (getBootstrapEnabled(state) && !getIndexName(state)) { + errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_INDEX_NAME].push('An index name is required.'); + } + + if (getBootstrapEnabled(state) && !getAliasName(state)) { + errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_ALIAS_NAME].push('An alias name is required.'); + } + + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT], + ...validatePhase(PHASE_HOT, getPhase(state, PHASE_HOT)) + }; + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM], + ...validatePhase(PHASE_WARM, getPhase(state, PHASE_WARM)) + }; + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD], + ...validatePhase(PHASE_COLD, getPhase(state, PHASE_COLD)) + }; + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE], + ...validatePhase(PHASE_DELETE, getPhase(state, PHASE_DELETE)) + }; + + return errors; +}; + +export const getLifecycle = state => { + const phases = Object.entries(getPhases(state)).reduce( + (accum, [phaseName, phase]) => { + // Hot is ALWAYS enabled + if (phaseName === PHASE_HOT) { + phase[PHASE_ENABLED] = true; + } + + if (phase[PHASE_ENABLED]) { + accum[phaseName] = phaseToES(state, phase); + + // These seem to be constants + // TODO: verify this assumption + if (phaseName === PHASE_HOT) { + accum[phaseName].after = '0s'; + } + + if (phaseName === PHASE_DELETE) { + accum[phaseName].actions = { + ...accum[phaseName].actions, + delete: {} + }; + } + } + return accum; + }, + {} + ); + + return { + name: getSelectedPolicyName(state), + //type, TODO: figure this out (jsut store it and not let the user change it?) + phases + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js new file mode 100644 index 0000000000000..1ad1e11e57045 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js @@ -0,0 +1,46 @@ +/* + * 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 { createSelector } from 'reselect'; + +export const getNodes = state => state.nodes.nodes; +export const getNodeOptions = createSelector( + [ + state => getNodes(state), + ], + nodes => { + if (!nodes) { + return []; + } + + const options = Object.keys(nodes).map(attrs => ({ + text: attrs, + value: attrs, + })); + + options.sort((a, b) => a.value.localeCompare(b.value)); + options.unshift({ + text: '', + value: undefined, + }); + + return options; + } +); + +export const getSelectedPrimaryShardCount = state => state.nodes.selectedPrimaryShardCount; +export const getSelectedReplicaCount = state => state.nodes.selectedReplicaCount; +export const getSelectedNodeAttrs = state => state.nodes.selectedNodeAttrs; +export const getNodesFromSelectedNodeAttrs = state => { + const nodes = getNodes(state)[getSelectedNodeAttrs(state)]; + if (nodes) { + return nodes.length; + } + return null; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js new file mode 100644 index 0000000000000..f0818f043ca94 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -0,0 +1,232 @@ +/* + * 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 { + defaultHotPhase, + defaultWarmPhase, + defaultColdPhase, + defaultDeletePhase +} from '../reducers/policies'; +import { + PHASE_HOT, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_NODE_ATTRS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ENABLED, + PHASE_ROLLOVER_ALIAS, + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, + MAX_SIZE_TYPE_DOCUMENT +} from '../constants'; +import { getIndexTemplates } from '.'; + +export const getPolicies = state => state.policies.policies; +export const getSelectedPolicy = state => state.policies.selectedPolicy; +export const getSelectedOriginalPolicyName = state => state.policies.originalPolicyName; + +export const getSaveAsNewPolicy = state => + state.policies.selectedPolicy.saveAsNew; + +export const getSelectedPolicyName = state => { + if (!getSaveAsNewPolicy(state)) { + return getSelectedOriginalPolicyName(state); + } + return state.policies.selectedPolicy.name; +}; + +export const getAllPolicyNamesFromTemplates = state => { + return getIndexTemplates(state).map(template => template.index_lifecycle_name).filter(name => name); +}; + +export const getPhases = state => state.policies.selectedPolicy.phases; +export const getPhase = (state, phase) => + getPhases(state)[phase]; +export const getPhaseData = (state, phase, key) => { + if (PHASE_ATTRIBUTES_THAT_ARE_NUMBERS.includes(key)) { + return parseInt(getPhase(state, phase)[key]); + } + return getPhase(state, phase)[key]; +}; + +export const splitSizeAndUnits = field => { + let size; + let units; + + const result = /(\d+)(\w+)/.exec(field); + if (result) { + size = parseInt(result[1]) || 0; + units = result[2]; + } + + return { + size, + units + }; +}; + +export const isNumber = value => typeof value === 'number'; + +export const phaseFromES = (phase, defaultPolicy) => { + const policy = { ...defaultPolicy }; + + if (!phase) { + return policy; + } + + policy[PHASE_ENABLED] = true; + + if (phase.after) { + const { size: after, units: afterUnits } = splitSizeAndUnits( + phase.after + ); + policy[PHASE_ROLLOVER_AFTER] = after; + policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + } + + if (phase.actions) { + const actions = phase.actions; + + if (actions.rollover) { + const rollover = actions.rollover; + policy[PHASE_ROLLOVER_ENABLED] = true; + if (rollover.max_age) { + const { size: maxAge, units: maxAgeUnits } = splitSizeAndUnits( + rollover.max_age + ); + policy[PHASE_ROLLOVER_MAX_AGE] = maxAge; + policy[PHASE_ROLLOVER_MAX_AGE_UNITS] = maxAgeUnits; + } + if (rollover.max_size) { + const { size: maxSize, units: maxSizeUnits } = splitSizeAndUnits( + rollover.max_size + ); + policy[PHASE_ROLLOVER_MAX_SIZE_STORED] = maxSize; + policy[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] = maxSizeUnits; + } + if (rollover.max_docs) { + policy[PHASE_ROLLOVER_MAX_SIZE_STORED] = rollover.max_docs; + policy[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] = MAX_SIZE_TYPE_DOCUMENT; + } + } else { + policy[PHASE_ROLLOVER_ENABLED] = false; + } + + if (actions.allocate) { + const allocate = actions.allocate; + if (allocate.require) { + policy[PHASE_NODE_ATTRS] = allocate.require._name; + } + } + + if (actions.forcemerge) { + const forcemerge = actions.forcemerge; + policy[PHASE_FORCE_MERGE_ENABLED] = true; + policy[PHASE_FORCE_MERGE_SEGMENTS] = forcemerge.max_num_segments; + } + + if (actions.shrink) { + policy[PHASE_PRIMARY_SHARD_COUNT] = actions.shrink.number_of_shards; + } + + if (actions.replicas) { + const replicas = actions.replicas; + policy[PHASE_REPLICA_COUNT] = replicas.number_of_replicas; + } + } + + return policy; +}; + +export const policyFromES = ({ name, type, phases }) => { + return { + name, + type, + phases: { + [PHASE_HOT]: phaseFromES(phases[PHASE_HOT], defaultHotPhase), + [PHASE_WARM]: phaseFromES(phases[PHASE_WARM], defaultWarmPhase), + [PHASE_COLD]: phaseFromES(phases[PHASE_COLD], defaultColdPhase), + [PHASE_DELETE]: phaseFromES(phases[PHASE_DELETE], defaultDeletePhase) + } + }; +}; + +export const phaseToES = (state, phase) => { + const esPhase = {}; + + if (!phase[PHASE_ENABLED]) { + return esPhase; + } + + if (isNumber(phase[PHASE_ROLLOVER_AFTER])) { + esPhase.after = `${phase[PHASE_ROLLOVER_AFTER]}${phase[PHASE_ROLLOVER_AFTER_UNITS]}`; + } + + esPhase.actions = {}; + + if (phase[PHASE_ROLLOVER_ENABLED]) { + esPhase.actions.rollover = { + alias: phase[PHASE_ROLLOVER_ALIAS], + }; + + if (isNumber(phase[PHASE_ROLLOVER_MAX_AGE])) { + esPhase.actions.rollover.max_age = `${phase[PHASE_ROLLOVER_MAX_AGE]}${ + phase[PHASE_ROLLOVER_MAX_AGE_UNITS] + }`; + } else if (isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED])) { + if (phase[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] === MAX_SIZE_TYPE_DOCUMENT) { + esPhase.actions.rollover.max_docs = phase[PHASE_ROLLOVER_MAX_SIZE_STORED]; + } else { + esPhase.actions.rollover.max_size = `${phase[PHASE_ROLLOVER_MAX_SIZE_STORED]}${ + phase[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] + }`; + } + } + } + + if (phase[PHASE_NODE_ATTRS]) { + esPhase.actions.allocate = { + include: {}, // TODO: this seems to be a constant, confirm? + exclude: {}, // TODO: this seems to be a constant, confirm? + require: { + _name: phase[PHASE_NODE_ATTRS] + } + }; + } + + if (phase[PHASE_FORCE_MERGE_ENABLED]) { + esPhase.actions.forcemerge = { + max_num_segments: phase[PHASE_FORCE_MERGE_SEGMENTS] + }; + } + + if (isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + esPhase.actions.shrink = { + number_of_shards: phase[PHASE_PRIMARY_SHARD_COUNT] + }; + } + + if (isNumber(phase[PHASE_REPLICA_COUNT])) { + esPhase.actions.replicas = { + number_of_replicas: phase[PHASE_REPLICA_COUNT] + }; + } + + return esPhase; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/store.js b/x-pack/plugins/index_lifecycle_management/public/store/store.js new file mode 100644 index 0000000000000..3566efa9fff10 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/store.js @@ -0,0 +1,21 @@ +/* + * 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 { createStore, applyMiddleware, compose } from 'redux'; +import thunk from 'redux-thunk'; + +import { indexLifecycleManagement } from './reducers/'; + +export const indexLifecycleManagementStore = (initialState = {}) => { + const enhancers = [ applyMiddleware(thunk) ]; + + window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); + return createStore( + indexLifecycleManagement, + initialState, + compose(...enhancers) + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js new file mode 100644 index 0000000000000..b9a77a1a0362b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { once } from 'lodash'; + +const callWithRequest = once((server) => { + const cluster = server.plugins.elasticsearch.getCluster('data'); + return cluster.callWithRequest; +}); + +export const callWithRequestFactory = (server, request) => { + return (...args) => { + return callWithRequest(server)(request, ...args); + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js new file mode 100644 index 0000000000000..787814d87dff9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js @@ -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 { callWithRequestFactory } from './call_with_request_factory'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js new file mode 100644 index 0000000000000..19a7b56759269 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js @@ -0,0 +1,146 @@ +/* + * 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'; +import { set } from 'lodash'; +import { checkLicense } from '../check_license'; + +describe('check_license', function () { + + let mockLicenseInfo; + beforeEach(() => mockLicenseInfo = {}); + + describe('license information is undefined', () => { + beforeEach(() => mockLicenseInfo = undefined); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is not available', () => { + beforeEach(() => mockLicenseInfo.isAvailable = () => false); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is available', () => { + beforeEach(() => { + mockLicenseInfo.isAvailable = () => true; + set(mockLicenseInfo, 'license.getType', () => 'basic'); + }); + + describe('& license is trial, standard, gold, platinum', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set isAvailable to true', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it ('should set enableLinks to true', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); + }); + + it('should not set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it ('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); + + describe('& license is basic', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set isAvailable to true', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it ('should set enableLinks to true', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); + }); + + it('should not set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js new file mode 100644 index 0000000000000..8a5a7d7029b71 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js @@ -0,0 +1,58 @@ +/* + * 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 function checkLicense(xpackLicenseInfo) { + const pluginName = 'Index Management'; + + // If, for some reason, we cannot get the license information + // from Elasticsearch, assume worst case and disable + if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { + return { + isAvailable: false, + showLinks: true, + enableLinks: false, + message: `You cannot use ${pluginName} because license information is not available at this time.` + }; + } + + const VALID_LICENSE_MODES = [ + 'trial', + 'basic', + 'standard', + 'gold', + 'platinum' + ]; + + const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); + const isLicenseActive = xpackLicenseInfo.license.isActive(); + const licenseType = xpackLicenseInfo.license.getType(); + + // License is not valid + if (!isLicenseModeValid) { + return { + isAvailable: false, + showLinks: false, + message: `Your ${licenseType} license does not support ${pluginName}. Please upgrade your license.` + }; + } + + // License is valid but not active + if (!isLicenseActive) { + return { + isAvailable: false, + showLinks: true, + enableLinks: false, + message: `You cannot use ${pluginName} because your ${licenseType} license has expired.` + }; + } + + // License is valid and active + return { + isAvailable: true, + showLinks: true, + enableLinks: true + }; +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js new file mode 100644 index 0000000000000..f2c070fd44b6e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js @@ -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 { checkLicense } from './check_license'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js new file mode 100644 index 0000000000000..443744ccb0cc8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js @@ -0,0 +1,21 @@ +/* + * 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'; +import { wrapCustomError } from '../wrap_custom_error'; + +describe('wrap_custom_error', () => { + describe('#wrapCustomError', () => { + it('should return a Boom object', () => { + const originalError = new Error('I am an error'); + const statusCode = 404; + const wrappedError = wrapCustomError(originalError, statusCode); + + expect(wrappedError.isBoom).to.be(true); + expect(wrappedError.output.statusCode).to.equal(statusCode); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js new file mode 100644 index 0000000000000..394c182140000 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js @@ -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 expect from 'expect.js'; +import { wrapEsError } from '../wrap_es_error'; + +describe('wrap_es_error', () => { + describe('#wrapEsError', () => { + + let originalError; + beforeEach(() => { + originalError = new Error('I am an error'); + originalError.statusCode = 404; + }); + + it('should return a Boom object', () => { + const wrappedError = wrapEsError(originalError); + + expect(wrappedError.isBoom).to.be(true); + }); + + it('should return the correct Boom object', () => { + const wrappedError = wrapEsError(originalError); + + expect(wrappedError.output.statusCode).to.be(originalError.statusCode); + expect(wrappedError.output.payload.message).to.be(originalError.message); + }); + + it('should return the correct Boom object with custom message', () => { + const wrappedError = wrapEsError(originalError, { 404: 'No encontrado!' }); + + expect(wrappedError.output.statusCode).to.be(originalError.statusCode); + expect(wrappedError.output.payload.message).to.be('No encontrado!'); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js new file mode 100644 index 0000000000000..6d6a336417bef --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { wrapUnknownError } from '../wrap_unknown_error'; + +describe('wrap_unknown_error', () => { + describe('#wrapUnknownError', () => { + it('should return a Boom object', () => { + const originalError = new Error('I am an error'); + const wrappedError = wrapUnknownError(originalError); + + expect(wrappedError.isBoom).to.be(true); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js new file mode 100644 index 0000000000000..f275f15637091 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { wrapCustomError } from './wrap_custom_error'; +export { wrapEsError } from './wrap_es_error'; +export { wrapUnknownError } from './wrap_unknown_error'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js new file mode 100644 index 0000000000000..890a366ac65c1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +/** + * Wraps a custom error into a Boom error response and returns it + * + * @param err Object error + * @param statusCode Error status code + * @return Object Boom error response + */ +export function wrapCustomError(err, statusCode) { + return Boom.wrap(err, statusCode); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js new file mode 100644 index 0000000000000..6ac4d50c7e0fe --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js @@ -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 Boom from 'boom'; + +/** + * Wraps an error thrown by the ES JS client into a Boom error response and returns it + * + * @param err Object Error thrown by ES JS client + * @param statusCodeToMessageMap Object Optional map of HTTP status codes => error messages + * @return Object Boom error response + */ +export function wrapEsError(err, statusCodeToMessageMap = {}) { + + const statusCode = err.statusCode; + + // If no custom message if specified for the error's status code, just + // wrap the error as a Boom error response and return it + if (!statusCodeToMessageMap[statusCode]) { + return Boom.wrap(err, err.statusCode); + } + + // Otherwise, use the custom message to create a Boom error response and + // return it + const message = statusCodeToMessageMap[statusCode]; + return Boom.create(statusCode, message); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js new file mode 100644 index 0000000000000..b0cdced7adbef --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +/** + * Wraps an unknown error into a Boom error response and returns it + * + * @param err Object Unknown error + * @return Object Boom error response + */ +export function wrapUnknownError(err) { + return Boom.wrap(err); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js new file mode 100644 index 0000000000000..d50ff9480d3e4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { isEsErrorFactory } from '../is_es_error_factory'; +import { set } from 'lodash'; + +class MockAbstractEsError {} + +describe('is_es_error_factory', () => { + + let mockServer; + let isEsError; + + beforeEach(() => { + const mockEsErrors = { + _Abstract: MockAbstractEsError + }; + mockServer = {}; + set(mockServer, 'plugins.elasticsearch.getCluster', () => ({ errors: mockEsErrors })); + + isEsError = isEsErrorFactory(mockServer); + }); + + describe('#isEsErrorFactory', () => { + + it('should return a function', () => { + expect(isEsError).to.be.a(Function); + }); + + describe('returned function', () => { + + it('should return true if passed-in err is a known esError', () => { + const knownEsError = new MockAbstractEsError(); + expect(isEsError(knownEsError)).to.be(true); + }); + + it('should return false if passed-in err is not a known esError', () => { + const unknownEsError = {}; + expect(isEsError(unknownEsError)).to.be(false); + + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js new file mode 100644 index 0000000000000..441648a8701e0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js @@ -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 { isEsErrorFactory } from './is_es_error_factory'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js new file mode 100644 index 0000000000000..80daac5bd496d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js @@ -0,0 +1,18 @@ +/* + * 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 { memoize } from 'lodash'; + +const esErrorsFactory = memoize((server) => { + return server.plugins.elasticsearch.getCluster('admin').errors; +}); + +export function isEsErrorFactory(server) { + const esErrors = esErrorsFactory(server); + return function isEsError(err) { + return err instanceof esErrors._Abstract; + }; +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js new file mode 100644 index 0000000000000..b72f8cc769731 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js @@ -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 expect from 'expect.js'; +import { licensePreRoutingFactory } from '../license_pre_routing_factory'; + +describe('license_pre_routing_factory', () => { + describe('#reportingFeaturePreRoutingFactory', () => { + let mockServer; + let mockLicenseCheckResults; + + beforeEach(() => { + mockServer = { + plugins: { + xpack_main: { + info: { + feature: () => ({ + getLicenseCheckResults: () => mockLicenseCheckResults + }) + } + } + } + }; + }); + + it('only instantiates one instance per server', () => { + const firstInstance = licensePreRoutingFactory(mockServer); + const secondInstance = licensePreRoutingFactory(mockServer); + + expect(firstInstance).to.be(secondInstance); + }); + + describe('isAvailable is false', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: false + }; + }); + + it ('replies with 403', (done) => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + licensePreRouting(stubRequest, (response) => { + expect(response).to.be.an(Error); + expect(response.isBoom).to.be(true); + expect(response.output.statusCode).to.be(403); + done(); + }); + }); + }); + + describe('isAvailable is true', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: true + }; + }); + + it ('replies with nothing', (done) => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + licensePreRouting(stubRequest, (response) => { + expect(response).to.be(undefined); + done(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js new file mode 100644 index 0000000000000..0743e443955f4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js @@ -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 { licensePreRoutingFactory } from './license_pre_routing_factory'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js new file mode 100644 index 0000000000000..b3720ab265393 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { once } from 'lodash'; +import { wrapCustomError } from '../error_wrappers'; +import { PLUGIN } from '../../../common/constants'; + +export const licensePreRoutingFactory = once((server) => { + const xpackMainPlugin = server.plugins.xpack_main; + + // License checking and enable/disable logic + function licensePreRouting(request, reply) { + const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); + if (!licenseCheckResults.isAvailable) { + const error = new Error(licenseCheckResults.message); + const statusCode = 403; + const wrappedError = wrapCustomError(error, statusCode); + reply(wrappedError); + } else { + reply(); + } + } + + return licensePreRouting; +}); + diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js new file mode 100644 index 0000000000000..7b0f97c38d129 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js @@ -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 { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js new file mode 100644 index 0000000000000..b05946e60b330 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js @@ -0,0 +1,21 @@ +/* + * 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 { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; +import { checkLicense } from '../check_license'; +import { PLUGIN } from '../../../common/constants'; + +export function registerLicenseChecker(server) { + const xpackMainPlugin = server.plugins.xpack_main; + const watcherPlugin = server.plugins.watcher; + + mirrorPluginStatus(xpackMainPlugin, watcherPlugin); + xpackMainPlugin.status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js new file mode 100644 index 0000000000000..915fb695bb468 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js @@ -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 { registerIndicesRoutes } from './register_indices_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js new file mode 100644 index 0000000000000..ad7128903b99c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js @@ -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 { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; + +async function bootstrap(callWithRequest, payload) { + await callWithRequest('indices.create', { + index: payload.indexName, + aliases: { + [payload.aliasName]: {} + }, + settings: { + 'index.lifecycle.rollover_alias': payload.aliasName, + } + }); +} + +export function registerBootstrapRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/indices/bootstrap', + method: 'POST', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await bootstrap(callWithRequest, request.payload); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [licensePreRouting] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js new file mode 100644 index 0000000000000..73541518350f7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; + +async function fetchTemplates(callWithRequest) { + const params = { + method: 'GET', + path: '/_template', + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [404] + }; + + return await callWithRequest('transport.request', params); +} + +async function getAffectedIndices( + callWithRequest, + indexTemplateName, + policyName +) { + const templates = await fetchTemplates(callWithRequest); + + if (!templates || templates.length === 0) { + return []; + } + + const indexPatterns = Object.entries(templates).reduce((accum, [templateName, template]) => { + if (templateName === indexTemplateName) { + accum.push(...template.index_patterns); + } else if ( + template.settings && + template.settings.index && + template.settings.index.lifecycle && + template.settings.index.lifecycle.name === policyName + ) { + accum.push(...template.index_patterns); + } + return accum; + }, []); + + if (!indexPatterns || indexPatterns.length === 0) { + return []; + } + + const indices = await callWithRequest('indices.get', { + index: indexPatterns + }); + + if (!indices) { + return []; + } + + return Object.keys(indices); +} + +export function registerGetAffectedRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: + '/api/index_lifecycle_management/indices/affected/{indexTemplateName}/{policyName}', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await getAffectedIndices( + callWithRequest, + request.params.indexTemplateName, + request.params.policyName + ); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [licensePreRouting] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js new file mode 100644 index 0000000000000..1f94eb13d125e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js @@ -0,0 +1,13 @@ +/* + * 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 { registerBootstrapRoute } from './register_bootstrap_route'; +import { registerGetAffectedRoute } from './register_get_affected_route'; + +export function registerIndicesRoutes(server) { + registerBootstrapRoute(server); + registerGetAffectedRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js new file mode 100644 index 0000000000000..17f52a723405d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js @@ -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 { registerLifecycleRoutes } from './register_lifecycle_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js new file mode 100644 index 0000000000000..84e2637ab5a4c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -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 { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { merge } from 'lodash'; + +async function createLifecycle(callWithRequest, lifecycle) { + const body = { + policy: { + phases: lifecycle.phases, + } + }; + const params = { + method: 'PUT', + path: `/_xpack/index_lifecycle/${lifecycle.name}`, + ignore: [ 404 ], + body, + }; + + return await callWithRequest('transport.request', params); +} + +async function getIndexTemplate(callWithRequest, indexTemplate) { + const response = await callWithRequest('indices.getTemplate', { name: indexTemplate }); + return response[indexTemplate]; +} + +async function updateIndexTemplate(callWithRequest, indexTemplatePatch) { + // Fetch existing template + const template = await getIndexTemplate(callWithRequest, indexTemplatePatch.indexTemplate); + merge(template, { + settings: { + index: { + number_of_shards: indexTemplatePatch.primaryShardCount, + number_of_replicas: indexTemplatePatch.replicaCount, + lifecycle: { + name: indexTemplatePatch.lifecycleName, + }, + routing: { + allocation: { + include: { + sattr_name: indexTemplatePatch.nodeAttrs, + } + } + } + } + } + }); + + const params = { + method: 'PUT', + path: `/_template/${indexTemplatePatch.indexTemplate}`, + ignore: [ 404 ], + body: template, + }; + + return await callWithRequest('transport.request', params); +} + +export function registerCreateRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/lifecycle', + method: 'POST', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await createLifecycle(callWithRequest, request.payload.lifecycle); + const response2 = await updateIndexTemplate(callWithRequest, request.payload.indexTemplatePatch); + reply([response, response2]); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js new file mode 100644 index 0000000000000..ba179d14b8112 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js @@ -0,0 +1,11 @@ +/* + * 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 { registerCreateRoute } from './register_create_route'; + +export function registerLifecycleRoutes(server) { + registerCreateRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js new file mode 100644 index 0000000000000..ef0ac271ae60e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js @@ -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 { registerNodesRoutes } from './register_nodes_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js new file mode 100644 index 0000000000000..9320030ba41ed --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js @@ -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 { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +function formatStats(stats) { + return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { + const attributes = stats.attributes || {}; + for (const [key, value] of Object.entries(attributes)) { + const attributeString = `${key}:${value}`; + accum[attributeString] = accum[attributeString] || []; + accum[attributeString].push(nodeId); + } + return accum; + }, {}); +} + +async function fetchNodeStats(callWithRequest) { + const params = { + format: 'json' + }; + + return await callWithRequest('nodes.stats', params); +} + +export function registerListRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/nodes/list', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const stats = await fetchNodeStats(callWithRequest); + const response = formatStats(stats); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js new file mode 100644 index 0000000000000..2ce0bdbe77c44 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js @@ -0,0 +1,11 @@ +/* + * 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 { registerListRoute } from './register_list_route'; + +export function registerNodesRoutes(server) { + registerListRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js new file mode 100644 index 0000000000000..7c6103a3389ab --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js @@ -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 { registerPoliciesRoutes } from './register_policies_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js new file mode 100644 index 0000000000000..6ec6745e80b21 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +function formatHits(hits) { + return Object.keys(hits).reduce((accum, lifecycleName) => { + const hit = hits[lifecycleName]; + accum.push({ + ...hit, + name: lifecycleName, + }); + return accum; + }, []); +} + +async function fetchPolicies(callWithRequest) { + const params = { + method: 'GET', + path: '/_xpack/index_lifecycle', + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +export function registerFetchRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/policies', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const hits = await fetchPolicies(callWithRequest); + const response = formatHits(hits); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js new file mode 100644 index 0000000000000..676121eadccb1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js @@ -0,0 +1,11 @@ +/* + * 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 { registerFetchRoute } from './register_fetch_route'; + +export function registerPoliciesRoutes(server) { + registerFetchRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js new file mode 100644 index 0000000000000..dc9a0acaaf09b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js @@ -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 { registerTemplatesRoutes } from './register_templates_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js new file mode 100644 index 0000000000000..37d15cf60a477 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +async function formatTemplates(templates, callWithRequest) { + const formattedTemplates = []; + const templateNames = Object.keys(templates); + for (const templateName of templateNames) { + const { settings, index_patterns } = templates[templateName]; // eslint-disable-line camelcase + const formattedTemplate = { + index_lifecycle_name: settings.index && settings.index.lifecycle ? settings.index.lifecycle.name : undefined, + index_patterns, + name: templateName, + }; + + const { indices } = await fetchIndices(index_patterns, callWithRequest); + formattedTemplate.indices = indices ? Object.keys(indices) : []; + formattedTemplates.push(formattedTemplate); + } + return formattedTemplates; +} + +async function fetchTemplates(callWithRequest) { + const params = { + method: 'GET', + path: '/_template', + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +async function fetchIndices(indexPatterns, callWithRequest) { + const params = { + method: 'GET', + path: `/${indexPatterns}/_stats`, + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +export function registerFetchRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/templates', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const hits = await fetchTemplates(callWithRequest); + const templates = formatTemplates(hits, callWithRequest); + reply(templates); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js new file mode 100644 index 0000000000000..690960e953c8c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +async function fetchTemplate(callWithRequest, templateName) { + const params = { + method: 'GET', + path: `/_template/${templateName}`, + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +export function registerGetRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/template/{templateName}', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + const templateName = request.params.templateName; + + try { + const template = await fetchTemplate(callWithRequest, templateName); + reply(template[templateName]); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js new file mode 100644 index 0000000000000..9750c0157b965 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js @@ -0,0 +1,16 @@ +/* + * 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 { registerFetchRoute } from './register_fetch_route'; +import { registerGetRoute } from './register_get_route'; + +export function registerTemplatesRoutes(server) { + registerFetchRoute(server); + registerGetRoute(server); +} diff --git a/x-pack/yarn.lock b/x-pack/yarn.lock index b33007ceb65d6..43681e79f6615 100644 --- a/x-pack/yarn.lock +++ b/x-pack/yarn.lock @@ -2004,6 +2004,10 @@ dfa@^1.0.0: dependencies: babel-runtime "^6.11.6" +diff-match-patch@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.0.tgz#1cc3c83a490d67f95d91e39f6ad1f2e086b63048" + diff@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" @@ -6078,6 +6082,16 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-ace@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-6.0.0.tgz#c211c21825f27343a7392f102493dc3ae099886d" + dependencies: + brace "^0.11.0" + diff-match-patch "^1.0.0" + lodash.get "^4.4.2" + lodash.isequal "^4.1.1" + prop-types "^15.5.8" + react-ace@^5.5.0, react-ace@^5.9.0: version "5.9.0" resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-5.9.0.tgz#427a1cc4869b960a6f9748aa7eb169a9269fc336" From 318964f8024542149ee849e3154baa44aea594ff Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 27 Apr 2018 10:34:09 -0400 Subject: [PATCH 003/102] Updates --- .../wizard/components/review/review.js | 77 +++++++++---------- .../public/store/selectors/index_template.js | 49 +++++------- 2 files changed, 59 insertions(+), 67 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index d5fbb7991bdf3..aa74c7398b907 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ - - - -import React, { Component } from 'react'; +import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; import DiffEditor from 'react-ace/lib/diff'; @@ -44,14 +41,14 @@ export class Review extends Component { saveAsNewPolicy: PropTypes.bool.isRequired, originalPolicyName: PropTypes.string, bootstrapEnabled: PropTypes.bool.isRequired, - aliasName: PropTypes.string.isRequired + aliasName: PropTypes.string.isRequired, }; constructor(props) { super(props); this.state = { selectedTab: 1, - affectedIndices: [] + affectedIndices: [], }; } @@ -72,7 +69,7 @@ export class Review extends Component { templateDiff, lifecycle, bootstrapEnabled, - aliasName + aliasName, } = this.props; const { affectedIndices } = this.state; @@ -91,7 +88,8 @@ export class Review extends Component {

- Index templates affected by this change (to the selected policy): + Index templates affected by this change (to + the selected policy):

    {affectedIndexTemplates.map(template => ( @@ -108,7 +106,8 @@ export class Review extends Component {

    - Indices affected by this change (to the selected policy): + Indices affected by this change (to the + selected policy):

      {affectedIndices.map(index =>
    • {index}
    • )} @@ -123,14 +122,9 @@ export class Review extends Component {

      New alias

      -

      Point to these new aliases going forward:

      +

      Point to this new alias going forward:

        -
      • - READ: {aliasName} -
      • -
      • - WRITE: {aliasName} -
      • +
      • {aliasName}
      @@ -138,30 +132,35 @@ export class Review extends Component { ) : null} - -

      - We will be changing the index template named `{ - selectedIndexTemplateName - }` -

      -
      - - - + {templateDiff.hasChanged ? ( + + +

      + We will be changing the index template named `{ + selectedIndexTemplateName + }` +

      +
      + + + + +
      + ) : null} { return indexPatterns; }; +// const hasJSONChanged = (json1, json2) => JSON.stringify(json1) !== JSON.stringify(json2); export const getTemplateDiff = state => { - const fullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; - return { - originalFullIndexTemplate: fullIndexTemplate, - newFullIndexTemplate: merge(cloneDeep(fullIndexTemplate), { - settings: { - index: { - number_of_shards: '' + getSelectedPrimaryShardCount(state), - number_of_replicas: '' + getSelectedReplicaCount(state), - lifecycle: { - name: getSelectedPolicyName(state) - }, - routing: { - allocation: { - include: { - sattr_name: getSelectedNodeAttrs(state), - } + const originalFullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; + const newFullIndexTemplate = merge(cloneDeep(originalFullIndexTemplate), { + settings: { + index: { + number_of_shards: '' + getSelectedPrimaryShardCount(state), + number_of_replicas: '' + getSelectedReplicaCount(state), + lifecycle: { + name: getSelectedPolicyName(state) + }, + routing: { + allocation: { + include: { + sattr_name: getSelectedNodeAttrs(state), } } } } - }), - // modifications: { - // settings: { - // index: { - // number_of_shards: getSelectedPrimaryShardCount(state), - // number_of_replicas: getSelectedReplicaCount(state), - // lifecycle: { - // name: getSelectedPolicyName(state), - // } - // } - // } - // } + } + }); + + return { + originalFullIndexTemplate, + newFullIndexTemplate, + hasChanged: true//hasJSONChanged(originalFullIndexTemplate, newFullIndexTemplate), }; }; From ecdfeabdd69e56cd18c68befd931165b53e85909 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 30 Apr 2018 14:22:55 -0400 Subject: [PATCH 004/102] Fix errors and add flyout for node details --- .../public/api/index.js | 5 + .../public/lib/find_errors.js | 13 ++- .../components/configuration/configuration.js | 96 ++++++++++++------- .../components/node_attrs_details/index.js | 7 ++ .../node_attrs_details.container.js | 17 ++++ .../node_attrs_details/node_attrs_details.js | 77 +++++++++++++++ .../components/cold_phase/cold_phase.js | 48 +++++++--- .../components/warm_phase/warm_phase.js | 77 +++++++++------ .../policy_configuration.js | 27 ++++++ .../policy_selection/policy_selection.js | 40 +++----- .../wizard/components/review/review.js | 9 +- .../public/sections/wizard/wizard.js | 38 +++++--- .../public/store/actions/nodes.js | 31 +++++- .../public/store/reducers/nodes.js | 16 +++- .../public/store/selectors/nodes.js | 19 ++-- .../server/routes/api/nodes/constants.js | 11 +++ .../api/nodes/register_details_route.js | 65 +++++++++++++ .../routes/api/nodes/register_list_route.js | 9 +- .../routes/api/nodes/register_nodes_routes.js | 2 + 19 files changed, 460 insertions(+), 147 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index 02018804fa462..1ce9bdb957c40 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -16,6 +16,11 @@ export async function loadNodes() { return response.data; } +export async function loadNodeDetails(selectedNodeAttrs) { + const response = await httpClient.get(`${apiPrefix}/nodes/${selectedNodeAttrs}/details`); + return response.data; +} + export async function loadIndexTemplates() { const response = await httpClient.get(`${apiPrefix}/templates`); return response.data; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js index 38d600622a8d4..39d260b05301e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js @@ -5,15 +5,18 @@ */ export const hasErrors = (object, keysToIgnore = []) => { - const errors = []; + let errors = false; for (const [key, value] of Object.entries(object)) { if (keysToIgnore.includes(key)) continue; if (Array.isArray(value) && value.length > 0) { - return true; - errors.push(...value); + errors = true; + break; } else if (value) { - return hasErrors(value, keysToIgnore); + errors = hasErrors(value, keysToIgnore); + if (errors) { + break; + } } } - return false; + return errors; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index a88ef8e7a97ee..7acd83c378a41 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ - - - -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { @@ -18,17 +15,20 @@ import { EuiSelect, EuiFieldNumber, EuiHorizontalRule, - EuiCallOut + EuiCallOut, + EuiFormRow, + EuiButtonEmpty, } from '@elastic/eui'; import { STRUCTURE_NODE_ATTRS, STRUCTURE_PRIMARY_NODES, - STRUCTURE_REPLICAS + STRUCTURE_REPLICAS, } from '../../../../../../store/constants'; import { ErrableFormRow } from '../../../../form_errors'; +import { NodeAttrsDetails } from '../../../node_attrs_details'; -export class Configuration extends PureComponent { +export class Configuration extends Component { static propTypes = { fetchNodes: PropTypes.func.isRequired, setSelectedNodeAttrs: PropTypes.func.isRequired, @@ -38,19 +38,27 @@ export class Configuration extends PureComponent { selectedPrimaryShardCount: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, selectedNodeAttrs: PropTypes.string.isRequired, nodeOptions: PropTypes.array.isRequired, selectedReplicaCount: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, isShowingErrors: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, - isPrimaryShardCountHigherThanSelectedNodeAttrsCount: PropTypes.bool.isRequired, + isPrimaryShardCountHigherThanSelectedNodeAttrsCount: + PropTypes.bool.isRequired, }; + constructor(props) { + super(props); + this.state = { + isShowingNodeDetailsFlyout: false, + }; + } + componentWillMount() { this.props.fetchNodes(); } @@ -68,7 +76,7 @@ export class Configuration extends PureComponent { selectedNodeAttrs, errors, isShowingErrors, - isPrimaryShardCountHigherThanSelectedNodeAttrsCount + isPrimaryShardCountHigherThanSelectedNodeAttrsCount, } = this.props; const primaryNodeErrors = isPrimaryShardCountHigherThanSelectedNodeAttrsCount ? ( @@ -78,7 +86,8 @@ export class Configuration extends PureComponent { color="warning" iconType="help" > - The selected primary shard count is higher than the number of nodes matching the selected attributes. + The selected primary shard count is higher than the number of nodes + matching the selected attributes. ) : null; @@ -89,29 +98,42 @@ export class Configuration extends PureComponent {

      Configure options

      - - { - await setSelectedNodeAttrs(e.target.value); - validate(); - }} - options={nodeOptions} - /> - + + + + { + await setSelectedNodeAttrs(e.target.value); + validate(); + }} + options={nodeOptions} + /> + + + {selectedNodeAttrs ? ( + + + + this.setState({ isShowingNodeDetailsFlyout: true }) + } + > + See more details about these nodes + + + + ) : null} + - -

      - Optimize these values for throughput. (Add more) -

      + +

      Optimize these values for throughput. (Add more)

      @@ -148,6 +170,12 @@ export class Configuration extends PureComponent { + {this.state.isShowingNodeDetailsFlyout ? ( + this.setState({ isShowingNodeDetailsFlyout: false })} + /> + ) : null} {primaryNodeErrors}
diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js new file mode 100644 index 0000000000000..885e965c46c4b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js @@ -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 { NodeAttrsDetails } from './node_attrs_details.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js new file mode 100644 index 0000000000000..8887b13740e04 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js @@ -0,0 +1,17 @@ +/* + * 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 { connect } from 'react-redux'; +import { NodeAttrsDetails as PresentationComponent } from './node_attrs_details'; +import { getNodeDetails } from '../../../../store/selectors'; +import { fetchNodeDetails } from '../../../../store/actions'; + +export const NodeAttrsDetails = connect( + (state, ownProps) => ({ + details: getNodeDetails(state, ownProps.selectedNodeAttrs), + }), + { fetchNodeDetails } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js new file mode 100644 index 0000000000000..0af37a84918a8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js @@ -0,0 +1,77 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyout, + EuiTitle, + EuiInMemoryTable, + EuiSpacer, + EuiButtonEmpty, + EuiCallOut, +} from '@elastic/eui'; + +export class NodeAttrsDetails extends PureComponent { + static propTypes = { + fetchNodeDetails: PropTypes.func.isRequired, + close: PropTypes.func.isRequired, + + details: PropTypes.array, + selectedNodeAttrs: PropTypes.string.isRequired, + }; + + componentWillMount() { + this.props.fetchNodeDetails(this.props.selectedNodeAttrs); + } + + render() { + const { selectedNodeAttrs, details, close } = this.props; + + return ( + + + +

+ Below is a list of nodes that contain the attribute: `{ + selectedNodeAttrs + }` +

+
+ + + Be aware that the nodes listed here only directly match the node + attribute string and other nodes might be affected by this policy + due to other allocation rules. + + + +
+ + + Close + + +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 7436558c68f46..392783c8587dd 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -40,6 +40,7 @@ export class ColdPhase extends PureComponent { static propTypes = { setPhaseData: PropTypes.func.isRequired, validate: PropTypes.func.isRequired, + showNodeDetailsFlyout: PropTypes.func.isRequired, isShowingErrors: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, @@ -70,6 +71,7 @@ export class ColdPhase extends PureComponent { const { setPhaseData, validate, + showNodeDetailsFlyout, phaseData, nodeOptions, @@ -170,21 +172,37 @@ export class ColdPhase extends PureComponent { - - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - + + + + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + {phaseData[PHASE_NODE_ATTRS] ? ( + + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} + > + See more details about these nodes + + + + ) : null} + diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index dfd70d0876767..4ddab994b65d7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -4,9 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import React, { Component } from 'react'; import PropTypes from 'prop-types'; @@ -35,7 +32,7 @@ import { PHASE_PRIMARY_SHARD_COUNT, PHASE_REPLICA_COUNT, PHASE_ROLLOVER_AFTER, - PHASE_ROLLOVER_AFTER_UNITS + PHASE_ROLLOVER_AFTER_UNITS, } from '../../../../../../store/constants'; import { ErrableFormRow } from '../../../../form_errors'; @@ -43,6 +40,7 @@ export class WarmPhase extends Component { static propTypes = { setPhaseData: PropTypes.func.isRequired, validate: PropTypes.func.isRequired, + showNodeDetailsFlyout: PropTypes.func.isRequired, isShowingErrors: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, @@ -53,40 +51,40 @@ export class WarmPhase extends Component { [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, - [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, }).isRequired, hotPhaseReplicaCount: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, hotPhasePrimaryShardCount: PropTypes.oneOfType([ PropTypes.number, - PropTypes.string + PropTypes.string, ]).isRequired, - nodeOptions: PropTypes.array.isRequired + nodeOptions: PropTypes.array.isRequired, }; constructor(props) { super(props); this.state = { - applyOnRollover: false + applyOnRollover: false, }; } @@ -98,6 +96,7 @@ export class WarmPhase extends Component { const { validate, setPhaseData, + showNodeDetailsFlyout, phaseData, hotPhaseReplicaCount, @@ -105,7 +104,7 @@ export class WarmPhase extends Component { nodeOptions, errors, isShowingErrors, - hotPhaseRolloverEnabled + hotPhaseRolloverEnabled, } = this.props; return ( @@ -122,7 +121,7 @@ export class WarmPhase extends Component { width: 64, lineHeight: '64px', textAlign: 'center', - color: 'white' + color: 'white', }} > @@ -213,7 +212,7 @@ export class WarmPhase extends Component { }} options={[ { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } + { value: 'h', text: 'hours' }, ]} /> @@ -223,21 +222,37 @@ export class WarmPhase extends Component { - - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - + + + + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + {phaseData[PHASE_NODE_ATTRS] ? ( + + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} + > + See more details about these nodes + + + + ) : null} + { + this.setState({ isShowingNodeDetailsFlyout: true, selectedNodeAttrsForDetails }); + } + render() { const { setSelectedPolicyName, @@ -82,6 +91,7 @@ export class PolicyConfiguration extends Component { setIndexName, setAliasName, validate, + back, affectedIndexTemplates, selectedIndexTemplateName, @@ -141,12 +151,14 @@ export class PolicyConfiguration extends Component { @@ -240,6 +252,14 @@ export class PolicyConfiguration extends Component { + + Back + +    Next + + {this.state.isShowingNodeDetailsFlyout ? ( + this.setState({ isShowingNodeDetailsFlyout: false })} + /> + ) : null} ); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index 52fe3a6cb1806..73cd4b58228c1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -18,7 +18,9 @@ import { EuiTitle, EuiSpacer, EuiPanel, - EuiText + EuiText, + EuiHorizontalRule, + EuiButtonEmpty } from '@elastic/eui'; export class PolicySelection extends Component { @@ -26,6 +28,7 @@ export class PolicySelection extends Component { fetchPolicies: PropTypes.func.isRequired, setSelectedPolicy: PropTypes.func.isRequired, done: PropTypes.func.isRequired, + back: PropTypes.func.isRequired, existingPolicyName: PropTypes.string.isRequired, policies: PropTypes.array.isRequired @@ -41,7 +44,7 @@ export class PolicySelection extends Component { } render() { - const { policies, existingPolicyName } = this.props; + const { policies, existingPolicyName, back } = this.props; return (
@@ -57,30 +60,6 @@ export class PolicySelection extends Component {

- {/* - - - - Hot - - - - - Warm - - - - - Cold - - - - - Delete - - - - */} @@ -111,6 +90,15 @@ export class PolicySelection extends Component { ))} + + + + Back +
); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index aa74c7398b907..c1273f5175210 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -24,6 +24,7 @@ import { EuiFlexGrid, EuiPanel, EuiText, + EuiButtonEmpty } from '@elastic/eui'; import { getAffectedIndices } from '../../../../api'; @@ -32,6 +33,7 @@ export class Review extends Component { setSelectedPolicyName: PropTypes.func.isRequired, setSaveAsNewPolicy: PropTypes.func.isRequired, done: PropTypes.func.isRequired, + back: PropTypes.func.isRequired, selectedIndexTemplateName: PropTypes.string.isRequired, affectedIndexTemplates: PropTypes.array.isRequired, @@ -63,6 +65,7 @@ export class Review extends Component { render() { const { done, + back, selectedIndexTemplateName, affectedIndexTemplates, @@ -132,7 +135,6 @@ export class Review extends Component { ) : null} - {templateDiff.hasChanged ? ( @@ -161,7 +163,10 @@ export class Review extends Component { ) : null} - + + Back + +    { this.setState({ - selectedStep + selectedStep, }); }; @@ -101,17 +98,28 @@ export class Wizard extends Component { /> ); case 2: - return this.onSelectedStepChanged(3)} />; + return ( + this.onSelectedStepChanged(3)} + back={() => this.onSelectedStepChanged(1)} + /> + ); case 3: return ( this.onSelectedStepChanged(4)} + back={() => this.onSelectedStepChanged(2)} /> ); case 4: - return ; + return ( + this.onSelectedStepChanged(3)} + /> + ); } } @@ -121,29 +129,29 @@ export class Wizard extends Component { title: 'Select a template', isSelected: this.state.selectedStep === 1, isComplete: this.state.selectedStep > 1, - onClick: () => this.onSelectedStepChanged(1) + onClick: () => this.onSelectedStepChanged(1), }, { title: 'Select or create policy', isSelected: this.state.selectedStep === 2, isComplete: this.state.selectedStep > 2, disabled: this.state.selectedStep < 2, - onClick: () => this.onSelectedStepChanged(2) + onClick: () => this.onSelectedStepChanged(2), }, { title: 'Configure policy', isSelected: this.state.selectedStep === 3, isComplete: this.state.selectedStep > 3, disabled: this.state.selectedStep < 3, - onClick: () => this.onSelectedStepChanged(3) + onClick: () => this.onSelectedStepChanged(3), }, { title: 'Review and save', isSelected: this.state.selectedStep === 4, isComplete: this.state.selectedStep > 4, disabled: this.state.selectedStep < 4, - onClick: () => this.onSelectedStepChanged(4) - } + onClick: () => this.onSelectedStepChanged(4), + }, ]; return ( diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js index 11b0a77be8106..e357b6d6587f3 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js @@ -6,20 +6,41 @@ import { createAction } from 'redux-actions'; import { toastNotifications } from 'ui/notify'; -import { loadNodes } from '../../api'; +import { loadNodes, loadNodeDetails } from '../../api'; export const setSelectedNodeAttrs = createAction('SET_SELECTED_NODE_ATTRS'); -export const setSelectedPrimaryShardCount = createAction('SET_SELECTED_PRIMARY_SHARED_COUNT'); -export const setSelectedReplicaCount = createAction('SET_SELECTED_REPLICA_COUNT'); +export const setSelectedPrimaryShardCount = createAction( + 'SET_SELECTED_PRIMARY_SHARED_COUNT' +); +export const setSelectedReplicaCount = createAction( + 'SET_SELECTED_REPLICA_COUNT' +); export const fetchedNodes = createAction('FETCHED_NODES'); export const fetchNodes = () => async dispatch => { let nodes; try { nodes = await loadNodes(); - } - catch (err) { + } catch (err) { return toastNotifications.addDanger(err.data.message); } dispatch(fetchedNodes(nodes)); }; + +export const fetchedNodeDetails = createAction( + 'FETCHED_NODE_DETAILS', + (selectedNodeAttrs, details) => ({ + selectedNodeAttrs, + details, + }) +); +export const fetchNodeDetails = selectedNodeAttrs => async dispatch => { + let details; + try { + details = await loadNodeDetails(selectedNodeAttrs); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedNodeDetails(selectedNodeAttrs, details)); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js index 88d5b9a0f6d53..a5a372ed17a8a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js @@ -9,7 +9,8 @@ import { fetchedNodes, setSelectedNodeAttrs, setSelectedPrimaryShardCount, - setSelectedReplicaCount + setSelectedReplicaCount, + fetchedNodeDetails } from '../actions/nodes'; const defaultState = { @@ -17,7 +18,8 @@ const defaultState = { selectedNodeAttrs: '', selectedPrimaryShardCount: 1, selectedReplicaCount: 1, - nodes: [] + nodes: [], + details: {}, }; export const nodes = handleActions( @@ -29,6 +31,16 @@ export const nodes = handleActions( nodes }; }, + [fetchedNodeDetails](state, { payload }) { + const { selectedNodeAttrs, details } = payload; + return { + ...state, + details: { + ...state.details, + [selectedNodeAttrs]: details, + } + }; + }, [setSelectedNodeAttrs](state, { payload: selectedNodeAttrs }) { return { ...state, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js index 1ad1e11e57045..ca0d06f9f2dde 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js @@ -4,23 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import { createSelector } from 'reselect'; export const getNodes = state => state.nodes.nodes; export const getNodeOptions = createSelector( - [ - state => getNodes(state), - ], + [state => getNodes(state)], nodes => { if (!nodes) { return []; } const options = Object.keys(nodes).map(attrs => ({ - text: attrs, + text: `${attrs} (${nodes[attrs].length})`, value: attrs, })); @@ -34,8 +29,10 @@ export const getNodeOptions = createSelector( } ); -export const getSelectedPrimaryShardCount = state => state.nodes.selectedPrimaryShardCount; -export const getSelectedReplicaCount = state => state.nodes.selectedReplicaCount; +export const getSelectedPrimaryShardCount = state => + state.nodes.selectedPrimaryShardCount; +export const getSelectedReplicaCount = state => + state.nodes.selectedReplicaCount; export const getSelectedNodeAttrs = state => state.nodes.selectedNodeAttrs; export const getNodesFromSelectedNodeAttrs = state => { const nodes = getNodes(state)[getSelectedNodeAttrs(state)]; @@ -44,3 +41,7 @@ export const getNodesFromSelectedNodeAttrs = state => { } return null; }; + +export const getNodeDetails = (state, selectedNodeAttrs) => { + return state.nodes.details[selectedNodeAttrs]; +}; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js new file mode 100644 index 0000000000000..d6a9dd774e206 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const NODE_ATTRS_KEYS_TO_IGNORE = [ + 'ml.enabled', + 'ml.machine_memory', + 'ml.max_open_jobs' +]; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js new file mode 100644 index 0000000000000..9ab30dd0f2f7e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +function formatStats(stats, nodeAttrs) { + return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { + const attributes = stats.attributes || {}; + for (const [key, value] of Object.entries(attributes)) { + if (`${key}:${value}` === nodeAttrs) { + accum.push({ + nodeId, + stats, + }); + break; + } + } + return accum; + }, []); +} + +async function fetchNodeStats(callWithRequest) { + const params = { + format: 'json' + }; + + return await callWithRequest('nodes.stats', params); +} + +export function registerDetailsRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/nodes/{nodeAttrs}/details', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const stats = await fetchNodeStats(callWithRequest); + const response = formatStats(stats, request.params.nodeAttrs); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js index 9320030ba41ed..7b82619665412 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js @@ -11,14 +11,17 @@ import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { NODE_ATTRS_KEYS_TO_IGNORE } from './constants'; function formatStats(stats) { return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { const attributes = stats.attributes || {}; for (const [key, value] of Object.entries(attributes)) { - const attributeString = `${key}:${value}`; - accum[attributeString] = accum[attributeString] || []; - accum[attributeString].push(nodeId); + if (!NODE_ATTRS_KEYS_TO_IGNORE.includes(key)) { + const attributeString = `${key}:${value}`; + accum[attributeString] = accum[attributeString] || []; + accum[attributeString].push(nodeId); + } } return accum; }, {}); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js index 2ce0bdbe77c44..341f1d4f1ebf3 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js @@ -5,7 +5,9 @@ */ import { registerListRoute } from './register_list_route'; +import { registerDetailsRoute } from './register_details_route'; export function registerNodesRoutes(server) { registerListRoute(server); + registerDetailsRoute(server); } From 0eface25e8f04bbb42bdd901672813adafe10d81 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 4 May 2018 13:36:14 -0400 Subject: [PATCH 005/102] New diff tool --- .../public/lib/diff_ace_addons.js | 119 + .../public/lib/diff_tools.js | 85 + .../wizard/components/review/diff_view.js | 63 + .../wizard/components/review/review.js | 10 +- .../wizard/components/review/review.less | 10 +- .../public/sections/wizard/diff.json | 2879 +++++++++++++++++ .../public/sections/wizard/wizard.js | 7 + 7 files changed, 3166 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js new file mode 100644 index 0000000000000..5e34f8f2e436a --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js @@ -0,0 +1,119 @@ +/* + * 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 ace from 'brace'; +import { ADDITION_PREFIX, REMOVAL_PREFIX } from './diff_tools'; + +function findInObject(key, obj) { + const objKeys = Object.keys(obj); + for (const objKey of objKeys) { + if (objKey === key) { + return obj[objKey]; + } + if (typeof obj[objKey] === 'object' && !Array.isArray(obj[objKey])) { + const item = findInObject(key, obj[objKey]); + if (item !== false) { + return item; + } + } + } + return false; +} + +function getDiffClasses(key, val, jsonObject) { + // const debug = false;//key === 'sattr_name'; + + let value = val; + if (value.endsWith(',')) { + value = value.slice(0, -1); + } + if (value.startsWith('"')) { + value = value.slice(1, -1); + } + + const additionValue = findInObject(`${ADDITION_PREFIX}${key}`, jsonObject); + const removalValue = findInObject(`${REMOVAL_PREFIX}${key}`, jsonObject); + + const isAddition = Array.isArray(additionValue) + ? !!additionValue.find(v => v === value) + : additionValue === value; + const isRemoval = Array.isArray(removalValue) + ? !!removalValue.find(v => v === value) + : removalValue === value; + + let diffClasses = ''; + if (isAddition) { + diffClasses = 'diff_addition ace_variable'; + } else if (isRemoval) { + diffClasses = 'diff_removal ace_variable'; + } else { + diffClasses = 'variable'; + } + + // debug && console.log(`getDiffClasses() + // key='${key}' + // value='${value}' + // additionValue='${additionValue}' + // removalValue='${removalValue}' + // isAddition=${isAddition} + // isRemoval=${isRemoval} + // diffClasses='${diffClasses}' + // `); + + return diffClasses; +} + +export const addDiffAddonsForAce = jsonObject => { + const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules') + .JsonHighlightRules; + class DiffJsonHighlightRules extends JsonHighlightRules { + constructor(props) { + super(props); + this.$rules = new JsonHighlightRules().getRules(); + + let currentArrayKey; + this.addRules({ + start: [ + { + token: (key, val) => { + return getDiffClasses(key, val, jsonObject); + }, + regex: '(?:"([\\w-+]+)"\\s*:\\s*([^\\n\\[]+)$)', + }, + { + token: key => { + currentArrayKey = key; + return 'variable'; + }, + next: 'array', + regex: '(?:"([\\w-+]+)"\\s*:\\s*\\[$)', + }, + ...this.$rules.start, + ], + array: [ + { + token: val => { + return getDiffClasses(currentArrayKey, val, jsonObject); + }, + next: 'start', + regex: '\\s*"([^"]+)"\\s*', + }, + ], + }); + } + } + + const JsonMode = ace.acequire('ace/mode/json').Mode; + class DiffJsonMode extends JsonMode { + constructor(props) { + super(props); + this.HighlightRules = DiffJsonHighlightRules; + } + } + + ace.define('ace/mode/diff_json', [], () => ({ + Mode: DiffJsonMode, + })); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js new file mode 100644 index 0000000000000..03dc6bf91014d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const ADDITION_PREFIX = '$'; +export const REMOVAL_PREFIX = '^'; +const removePrefixRegex = new RegExp( + `(\\${ADDITION_PREFIX}|\\${REMOVAL_PREFIX})`, + 'g' +); + +const isObject = value => typeof value === 'object' && !Array.isArray(value); +const isDifferent = (obj, key, value) => { + // If the object does not contain the key, then ignore since it's not a removal or addition + if (!obj.hasOwnProperty(key)) { + return false; + } + + // If we're dealing with an array, we need something better than a simple === comparison + if (Array.isArray(value)) { + return JSON.stringify(value) !== JSON.stringify(obj[key]); + } + + // If the value is an object, do not try and compare as this is called in a recursive function + // so safely ignore + if (typeof value === 'object') { + return false; + } + + // We should be dealing with primitives so do a basic comparison + return obj[key] !== value; +}; +const getAdditions = obj => { + const result = {}; + for (const [key, value] of Object.entries(obj)) { + if (isObject(value)) { + result[`${ADDITION_PREFIX}${key}`] = getAdditions(value); + } else { + result[`${ADDITION_PREFIX}${key}`] = value; + } + } + return result; +}; + +export const removePrefixes = str => str.replace(removePrefixRegex, ''); + +export const mergeAndPreserveDuplicateKeys = (source, target, result = {}) => { + for (const [key, value] of Object.entries(source)) { + // const debug = key === 'fooobar'; + // debug && console.log('mergeAndPreserveDuplicateKeys', key, value, target); + if (isDifferent(target, key, value)) { + // debug && console.log('isDifferent'); + result[`${REMOVAL_PREFIX}${key}`] = value; + result[`${ADDITION_PREFIX}${key}`] = target[key]; + } else if (isObject(value)) { + // debug && console.log('value is object', target[key]); + if (target.hasOwnProperty(key)) { + result[key] = mergeAndPreserveDuplicateKeys(value, target[key]); + } else { + result[key] = value; + } + } else { + result[key] = value; + } + } + + for (const [key, value] of Object.entries(target)) { + if ( + result.hasOwnProperty(key) || + result.hasOwnProperty(`${REMOVAL_PREFIX}${key}`) || + result.hasOwnProperty(`${ADDITION_PREFIX}${key}`) + ) { + continue; + } + + if (isObject(value)) { + result[`${ADDITION_PREFIX}${key}`] = getAdditions(value); + } else { + result[`${ADDITION_PREFIX}${key}`] = value; + } + } + return result; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js new file mode 100644 index 0000000000000..f76022a06359a --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js @@ -0,0 +1,63 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { EuiCodeEditor } from '@elastic/eui'; + +import 'brace/mode/json'; +import { + mergeAndPreserveDuplicateKeys, + removePrefixes, +} from '../../../../lib/diff_tools'; +import { addDiffAddonsForAce } from '../../../../lib/diff_ace_addons'; + +export class DiffView extends PureComponent { + static propTypes = { + templateDiff: PropTypes.shape({ + originalFullIndexTemplate: PropTypes.object.isRequired, + newFullIndexTemplate: PropTypes.object.isRequired, + }).isRequired, + }; + + render() { + const { + templateDiff: { originalFullIndexTemplate, newFullIndexTemplate }, + } = this.props; + + // console.log(JSON.stringify(this.props)); + + const mergedJson = mergeAndPreserveDuplicateKeys( + originalFullIndexTemplate, + newFullIndexTemplate + ); + + // console.log('mergedJson', mergedJson); + + // Strip the ^ and $ characters + const mergedJsonAsString = removePrefixes( + JSON.stringify(mergedJson, null, 2) + ); + + addDiffAddonsForAce(mergedJson); + + return ( + + ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index c1273f5175210..66c54a738142f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -7,7 +7,7 @@ import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; -import DiffEditor from 'react-ace/lib/diff'; +// import DiffEditor from 'react-ace/lib/diff'; import './review.less'; import 'brace/theme/github'; @@ -27,6 +27,7 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { getAffectedIndices } from '../../../../api'; +import { DiffView } from './diff_view'; export class Review extends Component { static propTypes = { @@ -145,7 +146,10 @@ export class Review extends Component { - + {/* + /> */} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less index 862b1bd58e8c8..e826e78a7bf7e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less @@ -1,5 +1,7 @@ -.codeMarker { - background: #FFF677; - position:absolute; - z-index:20 +.ace_diff_addition { + background-color: #e6ffed; +} + +.ace_diff_removal { + background-color: #ffeef0; } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json new file mode 100644 index 0000000000000..f7e2243cbddea --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json @@ -0,0 +1,2879 @@ +{ + "originalFullIndexTemplate": { + "order": 1, + "index_patterns": ["filebeat-7.0.0-alpha1-*"], + "settings": { + "index": { + "lifecycle": { + "name": "my_policy4" + }, + "routing": { + "allocation": { + "include": { + "sattr_name": "lala" + } + } + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "refresh_interval": "5s", + "number_of_shards": "3", + "number_of_routing_shards": "30", + "number_of_replicas": "0" + } + }, + "mappings": { + "doc": { + "dynamic_templates": [{ + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + }], + "properties": { + "tags": { + "type": "keyword", + "ignore_above": 1024 + }, + "error": { + "properties": { + "code": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + } + } + }, + "message": { + "type": "text", + "norms": false + }, + "read_timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "apache2": { + "properties": { + "access": { + "properties": { + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "user_agent": { + "properties": { + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "minor": { + "type": "long" + }, + "major": { + "type": "long" + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "os_minor": { + "type": "long" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "remote_ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "agent": { + "type": "text", + "norms": false + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "error": { + "properties": { + "client": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "pid": { + "type": "long" + }, + "tid": { + "type": "long" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "icinga": { + "properties": { + "main": { + "properties": { + "facility": { + "type": "keyword", + "ignore_above": 1024 + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "norms": false, + "type": "text" + } + } + }, + "startup": { + "properties": { + "facility": { + "type": "keyword", + "ignore_above": 1024 + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "norms": false, + "type": "text" + } + } + }, + "debug": { + "properties": { + "message": { + "type": "text", + "norms": false + }, + "facility": { + "ignore_above": 1024, + "type": "keyword" + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "logstash": { + "properties": { + "log": { + "properties": { + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "module": { + "type": "keyword", + "ignore_above": 1024 + }, + "thread": { + "type": "text", + "norms": false + }, + "log_event": { + "type": "object" + }, + "message": { + "type": "text", + "norms": false + } + } + }, + "slowlog": { + "properties": { + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "event": { + "type": "text", + "norms": false + }, + "plugin_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "took_in_nanos": { + "type": "long" + }, + "plugin_params": { + "type": "text", + "norms": false + }, + "plugin_params_object": { + "type": "object" + }, + "message": { + "norms": false, + "type": "text" + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "thread": { + "type": "text", + "norms": false + }, + "plugin_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "took_in_millis": { + "type": "long" + } + } + } + } + }, + "mysql": { + "properties": { + "slowlog": { + "properties": { + "rows_sent": { + "type": "long" + }, + "id": { + "type": "long" + }, + "rows_examined": { + "type": "long" + }, + "timestamp": { + "type": "long" + }, + "query": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "host": { + "type": "keyword", + "ignore_above": 1024 + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "query_time": { + "properties": { + "sec": { + "type": "float" + } + } + }, + "lock_time": { + "properties": { + "sec": { + "type": "float" + } + } + } + } + }, + "error": { + "properties": { + "thread_id": { + "type": "long" + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "beat": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "image": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "labels": { + "type": "object" + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "fileset": { + "properties": { + "module": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "@timestamp": { + "type": "date" + }, + "meta": { + "properties": { + "cloud": { + "properties": { + "provider": { + "type": "keyword", + "ignore_above": 1024 + }, + "instance_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "instance_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "machine_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "project_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "region": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "kafka": { + "properties": { + "log": { + "properties": { + "trace": { + "properties": { + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "full": { + "type": "text", + "norms": false + } + } + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "component": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "type": "text", + "norms": false + } + } + } + } + }, + "redis": { + "properties": { + "slowlog": { + "properties": { + "id": { + "type": "long" + }, + "key": { + "type": "keyword", + "ignore_above": 1024 + }, + "args": { + "type": "keyword", + "ignore_above": 1024 + }, + "cmd": { + "type": "keyword", + "ignore_above": 1024 + }, + "duration": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "log": { + "properties": { + "pid": { + "type": "long" + }, + "role": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + } + } + } + } + }, + "input": { + "properties": { + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "iis": { + "properties": { + "access": { + "properties": { + "server_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "port": { + "type": "long" + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "server_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "cookie": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "win32_status": { + "type": "long" + }, + "agent": { + "type": "text", + "norms": false + }, + "user_agent": { + "properties": { + "major": { + "type": "long" + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_minor": { + "type": "long" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "os_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "minor": { + "type": "long" + } + } + }, + "query_string": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + }, + "sub_status": { + "type": "long" + }, + "request_time_ms": { + "type": "long" + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "site_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "body_received": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "error": { + "properties": { + "remote_port": { + "type": "long" + }, + "server_port": { + "type": "long" + }, + "response_code": { + "type": "long" + }, + "reason_phrase": { + "type": "keyword", + "ignore_above": 1024 + }, + "queue_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "geoip": { + "properties": { + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server_ip": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "fields": { + "type": "object" + }, + "kubernetes": { + "properties": { + "container": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "image": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "pod": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "namespace": { + "type": "keyword", + "ignore_above": 1024 + }, + "node": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "labels": { + "type": "object" + }, + "annotations": { + "type": "object" + } + } + }, + "source": { + "type": "keyword", + "ignore_above": 1024 + }, + "auditd": { + "properties": { + "log": { + "properties": { + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + } + } + }, + "old_ses": { + "type": "keyword", + "ignore_above": 1024 + }, + "new_ses": { + "type": "keyword", + "ignore_above": 1024 + }, + "res": { + "type": "keyword", + "ignore_above": 1024 + }, + "sequence": { + "type": "long" + }, + "item": { + "type": "keyword", + "ignore_above": 1024 + }, + "items": { + "type": "keyword", + "ignore_above": 1024 + }, + "record_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "old_auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ppid": { + "type": "keyword", + "ignore_above": 1024 + }, + "a0": { + "type": "keyword", + "ignore_above": 1024 + }, + "new_auid": { + "type": "keyword", + "ignore_above": 1024 + }, + "acct": { + "type": "keyword", + "ignore_above": 1024 + }, + "pid": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "nginx": { + "properties": { + "access": { + "properties": { + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "user_agent": { + "properties": { + "os_minor": { + "type": "long" + }, + "major": { + "type": "long" + }, + "minor": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + }, + "agent": { + "type": "text", + "norms": false + }, + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "error": { + "properties": { + "pid": { + "type": "long" + }, + "tid": { + "type": "long" + }, + "connection_id": { + "type": "long" + }, + "message": { + "type": "text", + "norms": false + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "offset": { + "type": "long" + }, + "stream": { + "type": "keyword", + "ignore_above": 1024 + }, + "postgresql": { + "properties": { + "log": { + "properties": { + "timezone": { + "type": "keyword", + "ignore_above": 1024 + }, + "thread_id": { + "type": "long" + }, + "database": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "timestamp": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "duration": { + "type": "float" + }, + "query": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "traefik": { + "properties": { + "access": { + "properties": { + "referrer": { + "type": "keyword", + "ignore_above": 1024 + }, + "geoip": { + "properties": { + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "user_agent": { + "properties": { + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "minor": { + "type": "long" + }, + "os_minor": { + "type": "long" + }, + "major": { + "type": "long" + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "os_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "frontend_name": { + "type": "text", + "norms": false + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "request_count": { + "type": "long" + }, + "backend_url": { + "type": "text", + "norms": false + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "agent": { + "type": "text", + "norms": false + } + } + } + } + }, + "prospector": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "osquery": { + "properties": { + "result": { + "properties": { + "host_identifier": { + "type": "keyword", + "ignore_above": 1024 + }, + "unix_time": { + "type": "long" + }, + "calendar_time": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "action": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "system": { + "properties": { + "syslog": { + "properties": { + "program": { + "type": "keyword", + "ignore_above": 1024 + }, + "pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "type": "keyword", + "ignore_above": 1024 + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "auth": { + "properties": { + "groupadd": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "type": "long" + } + } + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "pid": { + "type": "long" + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "useradd": { + "properties": { + "home": { + "type": "keyword", + "ignore_above": 1024 + }, + "shell": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "uid": { + "type": "long" + }, + "gid": { + "type": "long" + } + } + }, + "sudo": { + "properties": { + "error": { + "type": "keyword", + "ignore_above": 1024 + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "pwd": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "command": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "program": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssh": { + "properties": { + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + } + } + }, + "event": { + "ignore_above": 1024, + "type": "keyword" + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "ip": { + "type": "ip" + }, + "dropped_ip": { + "type": "ip" + }, + "port": { + "type": "long" + }, + "signature": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + } + }, + "_meta": { + "version": "7.0.0-alpha1" + }, + "date_detection": false + } + }, + "aliases": {} + }, + "newFullIndexTemplate": { + "order": 1, + "index_patterns": ["filebeat-7.0.0-alpha1-*"], + "settings": { + "index": { + "lifecycle": { + "name": "my_policy41" + }, + "routing": { + "allocation": { + "include": { + "sattr_name": "warm_node:true" + } + } + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "refresh_interval": "5s", + "number_of_shards": "3", + "number_of_routing_shards": "30", + "number_of_replicas": "1" + } + }, + "mappings": { + "doc": { + "dynamic_templates": [{ + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + }], + "properties": { + "tags": { + "type": "keyword", + "ignore_above": 1024 + }, + "error": { + "properties": { + "code": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + } + } + }, + "message": { + "type": "text", + "norms": false + }, + "read_timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "apache2": { + "properties": { + "access": { + "properties": { + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "user_agent": { + "properties": { + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "minor": { + "type": "long" + }, + "major": { + "type": "long" + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "os_minor": { + "type": "long" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "remote_ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "agent": { + "type": "text", + "norms": false + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "error": { + "properties": { + "client": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "pid": { + "type": "long" + }, + "tid": { + "type": "long" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "icinga": { + "properties": { + "main": { + "properties": { + "facility": { + "type": "keyword", + "ignore_above": 1024 + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "norms": false, + "type": "text" + } + } + }, + "startup": { + "properties": { + "facility": { + "type": "keyword", + "ignore_above": 1024 + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "norms": false, + "type": "text" + } + } + }, + "debug": { + "properties": { + "message": { + "type": "text", + "norms": false + }, + "facility": { + "ignore_above": 1024, + "type": "keyword" + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "logstash": { + "properties": { + "log": { + "properties": { + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "module": { + "type": "keyword", + "ignore_above": 1024 + }, + "thread": { + "type": "text", + "norms": false + }, + "log_event": { + "type": "object" + }, + "message": { + "type": "text", + "norms": false + } + } + }, + "slowlog": { + "properties": { + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "event": { + "type": "text", + "norms": false + }, + "plugin_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "took_in_nanos": { + "type": "long" + }, + "plugin_params": { + "type": "text", + "norms": false + }, + "plugin_params_object": { + "type": "object" + }, + "message": { + "norms": false, + "type": "text" + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "thread": { + "type": "text", + "norms": false + }, + "plugin_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "took_in_millis": { + "type": "long" + } + } + } + } + }, + "mysql": { + "properties": { + "slowlog": { + "properties": { + "rows_sent": { + "type": "long" + }, + "id": { + "type": "long" + }, + "rows_examined": { + "type": "long" + }, + "timestamp": { + "type": "long" + }, + "query": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "host": { + "type": "keyword", + "ignore_above": 1024 + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "query_time": { + "properties": { + "sec": { + "type": "float" + } + } + }, + "lock_time": { + "properties": { + "sec": { + "type": "float" + } + } + } + } + }, + "error": { + "properties": { + "thread_id": { + "type": "long" + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "beat": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "image": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "labels": { + "type": "object" + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "fileset": { + "properties": { + "module": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "@timestamp": { + "type": "date" + }, + "meta": { + "properties": { + "cloud": { + "properties": { + "provider": { + "type": "keyword", + "ignore_above": 1024 + }, + "instance_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "instance_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "machine_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "project_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "region": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "kafka": { + "properties": { + "log": { + "properties": { + "trace": { + "properties": { + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "full": { + "type": "text", + "norms": false + } + } + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "component": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "type": "text", + "norms": false + } + } + } + } + }, + "redis": { + "properties": { + "slowlog": { + "properties": { + "id": { + "type": "long" + }, + "key": { + "type": "keyword", + "ignore_above": 1024 + }, + "args": { + "type": "keyword", + "ignore_above": 1024 + }, + "cmd": { + "type": "keyword", + "ignore_above": 1024 + }, + "duration": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "log": { + "properties": { + "pid": { + "type": "long" + }, + "role": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + } + } + } + } + }, + "input": { + "properties": { + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "iis": { + "properties": { + "access": { + "properties": { + "server_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "port": { + "type": "long" + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "server_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "cookie": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "win32_status": { + "type": "long" + }, + "agent": { + "type": "text", + "norms": false + }, + "user_agent": { + "properties": { + "major": { + "type": "long" + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_minor": { + "type": "long" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "os_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "minor": { + "type": "long" + } + } + }, + "query_string": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + }, + "sub_status": { + "type": "long" + }, + "request_time_ms": { + "type": "long" + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "site_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "body_received": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "error": { + "properties": { + "remote_port": { + "type": "long" + }, + "server_port": { + "type": "long" + }, + "response_code": { + "type": "long" + }, + "reason_phrase": { + "type": "keyword", + "ignore_above": 1024 + }, + "queue_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "geoip": { + "properties": { + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server_ip": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "fields": { + "type": "object" + }, + "kubernetes": { + "properties": { + "container": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "image": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "pod": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "namespace": { + "type": "keyword", + "ignore_above": 1024 + }, + "node": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "labels": { + "type": "object" + }, + "annotations": { + "type": "object" + } + } + }, + "source": { + "type": "keyword", + "ignore_above": 1024 + }, + "auditd": { + "properties": { + "log": { + "properties": { + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + } + } + }, + "old_ses": { + "type": "keyword", + "ignore_above": 1024 + }, + "new_ses": { + "type": "keyword", + "ignore_above": 1024 + }, + "res": { + "type": "keyword", + "ignore_above": 1024 + }, + "sequence": { + "type": "long" + }, + "item": { + "type": "keyword", + "ignore_above": 1024 + }, + "items": { + "type": "keyword", + "ignore_above": 1024 + }, + "record_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "old_auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ppid": { + "type": "keyword", + "ignore_above": 1024 + }, + "a0": { + "type": "keyword", + "ignore_above": 1024 + }, + "new_auid": { + "type": "keyword", + "ignore_above": 1024 + }, + "acct": { + "type": "keyword", + "ignore_above": 1024 + }, + "pid": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "nginx": { + "properties": { + "access": { + "properties": { + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "user_agent": { + "properties": { + "os_minor": { + "type": "long" + }, + "major": { + "type": "long" + }, + "minor": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + }, + "agent": { + "type": "text", + "norms": false + }, + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "error": { + "properties": { + "pid": { + "type": "long" + }, + "tid": { + "type": "long" + }, + "connection_id": { + "type": "long" + }, + "message": { + "type": "text", + "norms": false + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "offset": { + "type": "long" + }, + "stream": { + "type": "keyword", + "ignore_above": 1024 + }, + "postgresql": { + "properties": { + "log": { + "properties": { + "timezone": { + "type": "keyword", + "ignore_above": 1024 + }, + "thread_id": { + "type": "long" + }, + "database": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "timestamp": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "duration": { + "type": "float" + }, + "query": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "traefik": { + "properties": { + "access": { + "properties": { + "referrer": { + "type": "keyword", + "ignore_above": 1024 + }, + "geoip": { + "properties": { + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "body_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "user_agent": { + "properties": { + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "minor": { + "type": "long" + }, + "os_minor": { + "type": "long" + }, + "major": { + "type": "long" + }, + "patch": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_major": { + "type": "long" + }, + "os_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "frontend_name": { + "type": "text", + "norms": false + }, + "http_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "response_code": { + "type": "long" + }, + "remote_ip": { + "type": "keyword", + "ignore_above": 1024 + }, + "user_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "request_count": { + "type": "long" + }, + "backend_url": { + "type": "text", + "norms": false + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "agent": { + "type": "text", + "norms": false + } + } + } + } + }, + "prospector": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "osquery": { + "properties": { + "result": { + "properties": { + "host_identifier": { + "type": "keyword", + "ignore_above": 1024 + }, + "unix_time": { + "type": "long" + }, + "calendar_time": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "action": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "system": { + "properties": { + "syslog": { + "properties": { + "program": { + "type": "keyword", + "ignore_above": 1024 + }, + "pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "type": "keyword", + "ignore_above": 1024 + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "auth": { + "properties": { + "groupadd": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "type": "long" + } + } + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "pid": { + "type": "long" + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "useradd": { + "properties": { + "home": { + "type": "keyword", + "ignore_above": 1024 + }, + "shell": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "uid": { + "type": "long" + }, + "gid": { + "type": "long" + } + } + }, + "sudo": { + "properties": { + "error": { + "type": "keyword", + "ignore_above": 1024 + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "pwd": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + }, + "command": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "timestamp": { + "type": "keyword", + "ignore_above": 1024 + }, + "program": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssh": { + "properties": { + "geoip": { + "properties": { + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + } + } + }, + "event": { + "ignore_above": 1024, + "type": "keyword" + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "ip": { + "type": "ip" + }, + "dropped_ip": { + "type": "ip" + }, + "port": { + "type": "long" + }, + "signature": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + } + }, + "_meta": { + "version": "7.0.0-alpha1" + }, + "date_detection": false + } + }, + "aliases": {} + }, + "hasChanged": true +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index 1e2b4c4ac68c8..9a73050b124ff 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -24,6 +24,8 @@ import { STRUCTURE_INDEX_TEMPLATE, STRUCTURE_POLICY_CONFIGURATION, } from '../../store/constants'; +// import { DiffView } from './components/review/diff_view'; +// import diff from './diff.json'; export class Wizard extends Component { static propTypes = { @@ -154,6 +156,8 @@ export class Wizard extends Component { }, ]; + // const templateDiff = diff; + return ( @@ -166,6 +170,9 @@ export class Wizard extends Component { + {/* */} {this.renderContent()} ); From 9e5e2c4c47d0b1caf8ec0c16e88aa120227fd3e4 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 7 May 2018 11:04:47 -0400 Subject: [PATCH 006/102] Scroll to change for review diff --- .../public/lib/diff_ace_addons.js | 2 +- .../public/lib/diff_tools.js | 25 +- .../wizard/components/review/diff_view.js | 78 +- .../public/sections/wizard/diff.json | 3149 ++--------------- 4 files changed, 358 insertions(+), 2896 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js index 5e34f8f2e436a..f1f3b0a018d94 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js @@ -23,7 +23,7 @@ function findInObject(key, obj) { } function getDiffClasses(key, val, jsonObject) { - // const debug = false;//key === 'sattr_name'; + // const debug = key === 'react'; let value = val; if (value.endsWith(',')) { diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js index 03dc6bf91014d..d5baf8ded58a9 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js @@ -4,13 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ADDITION_PREFIX = '$'; -export const REMOVAL_PREFIX = '^'; +export const ADDITION_PREFIX = '$$$'; +export const REMOVAL_PREFIX = '^^^'; +const escapePrefix = prefix => prefix.split('').map(i => `\\${i}`).join(''); const removePrefixRegex = new RegExp( - `(\\${ADDITION_PREFIX}|\\${REMOVAL_PREFIX})`, + `(${escapePrefix(ADDITION_PREFIX)})|(${escapePrefix(REMOVAL_PREFIX)})`, 'g' ); +export const isBoolean = value => JSON.parse(value) === true || JSON.parse(value) === false; const isObject = value => typeof value === 'object' && !Array.isArray(value); const isDifferent = (obj, key, value) => { // If the object does not contain the key, then ignore since it's not a removal or addition @@ -46,7 +48,7 @@ const getAdditions = obj => { export const removePrefixes = str => str.replace(removePrefixRegex, ''); -export const mergeAndPreserveDuplicateKeys = (source, target, result = {}) => { +export const mergeAndPreserveDuplicateKeys = (source, target, result = {}, changes = []) => { for (const [key, value] of Object.entries(source)) { // const debug = key === 'fooobar'; // debug && console.log('mergeAndPreserveDuplicateKeys', key, value, target); @@ -54,10 +56,17 @@ export const mergeAndPreserveDuplicateKeys = (source, target, result = {}) => { // debug && console.log('isDifferent'); result[`${REMOVAL_PREFIX}${key}`] = value; result[`${ADDITION_PREFIX}${key}`] = target[key]; + changes.push({ + key, + original: value, + updated: target[key], + }); } else if (isObject(value)) { // debug && console.log('value is object', target[key]); if (target.hasOwnProperty(key)) { - result[key] = mergeAndPreserveDuplicateKeys(value, target[key]); + const recurseResult = mergeAndPreserveDuplicateKeys(value, target[key]); + result[key] = recurseResult.result; + changes.push(...recurseResult.changes); } else { result[key] = value; } @@ -80,6 +89,10 @@ export const mergeAndPreserveDuplicateKeys = (source, target, result = {}) => { } else { result[`${ADDITION_PREFIX}${key}`] = value; } + changes.push({ + key, + updated: result[`${ADDITION_PREFIX}${key}`], + }); } - return result; + return { result, changes }; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js index f76022a06359a..27d206d1f797b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js @@ -4,11 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { PureComponent } from 'react'; +import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { EuiCodeEditor } from '@elastic/eui'; - +import { + EuiCodeEditor, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, +} from '@elastic/eui'; +import ace from 'brace'; import 'brace/mode/json'; import { mergeAndPreserveDuplicateKeys, @@ -24,6 +32,14 @@ export class DiffView extends PureComponent { }).isRequired, }; + scrollToKey = (key, value) => { + const editorDom = this.aceEditor.aceEditor.refEditor; + const editor = ace.edit(editorDom); + const escapedValue = value.replace(/\^/, '\\^'); + const range = editor.find(new RegExp(`"${key}"\\s*:\\s*"*(${escapedValue})"*`), { regex: true }); + editor.gotoLine(range.start.row + 1, range.start.column); + } + render() { const { templateDiff: { originalFullIndexTemplate, newFullIndexTemplate }, @@ -31,12 +47,12 @@ export class DiffView extends PureComponent { // console.log(JSON.stringify(this.props)); - const mergedJson = mergeAndPreserveDuplicateKeys( + const { result: mergedJson, changes } = mergeAndPreserveDuplicateKeys( originalFullIndexTemplate, newFullIndexTemplate ); - // console.log('mergedJson', mergedJson); + // console.log('mergedJson', mergedJson, changes); // Strip the ^ and $ characters const mergedJsonAsString = removePrefixes( @@ -46,18 +62,46 @@ export class DiffView extends PureComponent { addDiffAddonsForAce(mergedJson); return ( - + + + + {changes.map(({ key, original, updated }) => ( + + + this.scrollToKey(key, updated)}> + {key} + + + + {original ? ( + + Changing `{original}` to `{updated}`` + + ) : ( + Adding with `{updated}` + )} + + + ))} + + + + (this.aceEditor = aceEditor)} + mode="diff_json" + theme="github" + width="100%" + value={mergedJsonAsString} + setOptions={{ + useWorker: false, + readOnly: true, + }} + editorProps={{ + $blockScrolling: Infinity, + }} + /> + + ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json index f7e2243cbddea..0411f08b735f4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json @@ -1,2879 +1,284 @@ { "originalFullIndexTemplate": { - "order": 1, - "index_patterns": ["filebeat-7.0.0-alpha1-*"], - "settings": { - "index": { - "lifecycle": { - "name": "my_policy4" - }, - "routing": { - "allocation": { - "include": { - "sattr_name": "lala" - } - } - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "refresh_interval": "5s", - "number_of_shards": "3", - "number_of_routing_shards": "30", - "number_of_replicas": "0" - } - }, - "mappings": { - "doc": { - "dynamic_templates": [{ - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - }], - "properties": { - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "error": { - "properties": { - "code": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "read_timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "apache2": { - "properties": { - "access": { - "properties": { - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "user_agent": { - "properties": { - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "minor": { - "type": "long" - }, - "major": { - "type": "long" - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "os_minor": { - "type": "long" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "remote_ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "agent": { - "type": "text", - "norms": false - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "client": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "pid": { - "type": "long" - }, - "tid": { - "type": "long" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "icinga": { - "properties": { - "main": { - "properties": { - "facility": { - "type": "keyword", - "ignore_above": 1024 - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "norms": false, - "type": "text" - } - } - }, - "startup": { - "properties": { - "facility": { - "type": "keyword", - "ignore_above": 1024 - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "norms": false, - "type": "text" - } - } - }, - "debug": { - "properties": { - "message": { - "type": "text", - "norms": false - }, - "facility": { - "ignore_above": 1024, - "type": "keyword" - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "logstash": { - "properties": { - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "thread": { - "type": "text", - "norms": false - }, - "log_event": { - "type": "object" - }, - "message": { - "type": "text", - "norms": false - } - } - }, - "slowlog": { - "properties": { - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "event": { - "type": "text", - "norms": false - }, - "plugin_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "took_in_nanos": { - "type": "long" - }, - "plugin_params": { - "type": "text", - "norms": false - }, - "plugin_params_object": { - "type": "object" - }, - "message": { - "norms": false, - "type": "text" - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "thread": { - "type": "text", - "norms": false - }, - "plugin_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "took_in_millis": { - "type": "long" - } - } - } - } - }, - "mysql": { - "properties": { - "slowlog": { - "properties": { - "rows_sent": { - "type": "long" - }, - "id": { - "type": "long" - }, - "rows_examined": { - "type": "long" - }, - "timestamp": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "host": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "query_time": { - "properties": { - "sec": { - "type": "float" - } - } - }, - "lock_time": { - "properties": { - "sec": { - "type": "float" - } - } - } - } - }, - "error": { - "properties": { - "thread_id": { - "type": "long" - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "beat": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "image": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "labels": { - "type": "object" - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "fileset": { - "properties": { - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "@timestamp": { - "type": "date" - }, - "meta": { - "properties": { - "cloud": { - "properties": { - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "machine_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "project_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "kafka": { - "properties": { - "log": { - "properties": { - "trace": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "full": { - "type": "text", - "norms": false - } - } - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "component": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "type": "text", - "norms": false - } - } - } - } - }, - "redis": { - "properties": { - "slowlog": { - "properties": { - "id": { - "type": "long" - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "cmd": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "log": { - "properties": { - "pid": { - "type": "long" - }, - "role": { - "type": "keyword", - "ignore_above": 1024 - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - } - } - } - } - }, - "input": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "iis": { - "properties": { - "access": { - "properties": { - "server_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "cookie": { - "type": "keyword", - "ignore_above": 1024 - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "win32_status": { - "type": "long" - }, - "agent": { - "type": "text", - "norms": false - }, - "user_agent": { - "properties": { - "major": { - "type": "long" - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_minor": { - "type": "long" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "os_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "minor": { - "type": "long" - } - } - }, - "query_string": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - }, - "sub_status": { - "type": "long" - }, - "request_time_ms": { - "type": "long" - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "site_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "body_received": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "error": { - "properties": { - "remote_port": { - "type": "long" - }, - "server_port": { - "type": "long" - }, - "response_code": { - "type": "long" - }, - "reason_phrase": { - "type": "keyword", - "ignore_above": 1024 - }, - "queue_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - }, - "geoip": { - "properties": { - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server_ip": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "fields": { - "type": "object" - }, - "kubernetes": { - "properties": { - "container": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pod": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "namespace": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "annotations": { - "type": "object" - } - } - }, - "source": { - "type": "keyword", - "ignore_above": 1024 - }, - "auditd": { - "properties": { - "log": { - "properties": { - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - } - } - }, - "old_ses": { - "type": "keyword", - "ignore_above": 1024 - }, - "new_ses": { - "type": "keyword", - "ignore_above": 1024 - }, - "res": { - "type": "keyword", - "ignore_above": 1024 - }, - "sequence": { - "type": "long" - }, - "item": { - "type": "keyword", - "ignore_above": 1024 - }, - "items": { - "type": "keyword", - "ignore_above": 1024 - }, - "record_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "old_auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ppid": { - "type": "keyword", - "ignore_above": 1024 - }, - "a0": { - "type": "keyword", - "ignore_above": 1024 - }, - "new_auid": { - "type": "keyword", - "ignore_above": 1024 - }, - "acct": { - "type": "keyword", - "ignore_above": 1024 - }, - "pid": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "nginx": { - "properties": { - "access": { - "properties": { - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "user_agent": { - "properties": { - "os_minor": { - "type": "long" - }, - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - }, - "agent": { - "type": "text", - "norms": false - }, - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "error": { - "properties": { - "pid": { - "type": "long" - }, - "tid": { - "type": "long" - }, - "connection_id": { - "type": "long" - }, - "message": { - "type": "text", - "norms": false - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "offset": { - "type": "long" - }, - "stream": { - "type": "keyword", - "ignore_above": 1024 - }, - "postgresql": { - "properties": { - "log": { - "properties": { - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "thread_id": { - "type": "long" - }, - "database": { - "type": "keyword", - "ignore_above": 1024 - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "timestamp": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "float" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "traefik": { - "properties": { - "access": { - "properties": { - "referrer": { - "type": "keyword", - "ignore_above": 1024 - }, - "geoip": { - "properties": { - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "user_agent": { - "properties": { - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "minor": { - "type": "long" - }, - "os_minor": { - "type": "long" - }, - "major": { - "type": "long" - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "os_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "frontend_name": { - "type": "text", - "norms": false - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "request_count": { - "type": "long" - }, - "backend_url": { - "type": "text", - "norms": false - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "agent": { - "type": "text", - "norms": false - } - } - } - } - }, - "prospector": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "osquery": { - "properties": { - "result": { - "properties": { - "host_identifier": { - "type": "keyword", - "ignore_above": 1024 - }, - "unix_time": { - "type": "long" - }, - "calendar_time": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "action": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "system": { - "properties": { - "syslog": { - "properties": { - "program": { - "type": "keyword", - "ignore_above": 1024 - }, - "pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "type": "keyword", - "ignore_above": 1024 - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "auth": { - "properties": { - "groupadd": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "type": "long" - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "pid": { - "type": "long" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "useradd": { - "properties": { - "home": { - "type": "keyword", - "ignore_above": 1024 - }, - "shell": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "long" - }, - "gid": { - "type": "long" - } - } - }, - "sudo": { - "properties": { - "error": { - "type": "keyword", - "ignore_above": 1024 - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "pwd": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "command": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "program": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "type": "keyword", - "ignore_above": 1024 - }, - "ssh": { - "properties": { - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - } - } - }, - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "dropped_ip": { - "type": "ip" - }, - "port": { - "type": "long" - }, - "signature": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - } - }, - "_meta": { - "version": "7.0.0-alpha1" - }, - "date_detection": false - } - }, - "aliases": {} + "@elastic/eui": "v0.0.44", + "@elastic/filesaver": "1.1.2", + "@elastic/numeral": "2.3.2", + "@elastic/ui-ace": "0.2.3", + "@kbn/babel-preset": "link:packages/kbn-babel-preset", + "@kbn/datemath": "link:packages/kbn-datemath", + "@kbn/pm": "link:packages/kbn-pm", + "@kbn/test-subj-selector": "link:packages/kbn-test-subj-selector", + "@kbn/ui-framework": "link:packages/kbn-ui-framework", + "JSONStream": "1.1.1", + "accept-language-parser": "1.2.0", + "angular": "1.6.9", + "angular-aria": "1.6.6", + "angular-elastic": "2.5.0", + "angular-recursion": "^1.0.5", + "angular-route": "1.4.7", + "angular-sanitize": "1.5.7", + "angular-sortable-view": "0.0.15", + "autoprefixer": "6.5.4", + "babel-core": "6.21.0", + "babel-loader": "7.1.2", + "babel-polyfill": "6.20.0", + "babel-register": "6.18.0", + "bluebird": "2.9.34", + "body-parser": "1.12.0", + "boom": "5.2.0", + "brace": "0.10.0", + "bunyan": "1.7.1", + "cache-loader": "1.0.3", + "chalk": "2.3.0", + "check-hash": "1.0.1", + "color": "1.0.3", + "commander": "2.8.1", + "compare-versions": "3.1.0", + "css-loader": "0.28.7", + "custom-event-polyfill": "^0.3.0", + "d3": "3.5.6", + "d3-cloud": "1.2.1", + "dragula": "3.7.0", + "elasticsearch": "^14.2.1", + "elasticsearch-browser": "^14.2.1", + "encode-uri-query": "1.0.0", + "even-better": "7.0.2", + "expiry-js": "0.1.7", + "extract-text-webpack-plugin": "3.0.1", + "file-loader": "1.1.4", + "font-awesome": "4.4.0", + "glob": "5.0.13", + "glob-all": "3.0.1", + "good-squeeze": "2.1.0", + "h2o2": "5.1.1", + "handlebars": "4.0.5", + "hapi": "14.2.0", + "hjson": "3.1.0", + "http-proxy-agent": "1.0.0", + "https-proxy-agent": "2.1.1", + "inert": "4.0.2", + "jade": "1.11.0", + "jade-loader": "0.8.0", + "joi": "10.4.1", + "jquery": "^3.3.1", + "js-yaml": "3.4.1", + "json-stringify-pretty-compact": "1.0.4", + "json-stringify-safe": "5.0.1", + "jstimezonedetect": "1.0.5", + "leaflet": "1.0.3", + "leaflet-draw": "0.4.10", + "leaflet-responsive-popup": "0.2.0", + "leaflet-vega": "^0.8.6", + "leaflet.heat": "0.2.0", + "less": "2.7.1", + "less-loader": "4.0.5", + "lodash": "3.10.1", + "lru-cache": "4.1.1", + "markdown-it": "8.3.2", + "minimatch": "2.0.10", + "mkdirp": "0.5.1", + "moment": "^2.20.1", + "moment-timezone": "^0.5.14", + "mustache": "2.3.0", + "ngreact": "0.5.1", + "no-ui-slider": "1.2.0", + "node-fetch": "1.3.2", + "pegjs": "0.9.0", + "postcss-loader": "2.0.6", + "prop-types": "15.5.8", + "proxy-from-env": "1.0.0", + "querystring-browser": "1.0.4", + "raw-loader": "0.5.1", + "react": "^16.2.0", + "react-addons-shallow-compare": "15.6.2", + "react-anything-sortable": "^1.7.4", + "react-color": "^2.13.8", + "react-dom": "^16.2.0", + "react-grid-layout": "^0.16.2", + "react-input-range": "^1.3.0", + "react-markdown": "^3.1.4", + "react-redux": "^5.0.6", + "react-router-dom": "4.2.2", + "react-select": "^1.2.0", + "react-sizeme": "^2.3.6", + "react-toggle": "4.0.2", + "reactcss": "1.2.3", + "redux": "3.7.2", + "redux-actions": "2.2.1", + "redux-thunk": "2.2.0", + "regression": "2.0.0", + "request": "^2.85.0", + "reselect": "^3.0.1", + "resize-observer-polyfill": "1.2.1", + "rimraf": "2.4.3", + "rison-node": "1.0.0", + "rxjs": "5.4.3", + "script-loader": "0.7.2", + "semver": "5.1.0", + "style-loader": "0.19.0", + "tar": "2.2.0", + "tinygradient": "0.3.0", + "tinymath": "0.2.1", + "topojson-client": "3.0.0", + "trunc-html": "1.0.2", + "trunc-text": "1.0.2", + "uglifyjs-webpack-plugin": "0.4.6", + "ui-select": "0.19.6", + "url-loader": "0.5.9", + "uuid": "3.0.1", + "validate-npm-package-name": "2.2.2", + "vega-lib": "^3.3.1", + "vega-lite": "^2.4.0", + "vega-tooltip": "^0.9.14", + "vega-schema-url-parser": "1.0.0", + "vision": "4.1.0", + "webpack": "3.6.0", + "webpack-merge": "4.1.0", + "whatwg-fetch": "^2.0.3", + "wreck": "12.4.0", + "x-pack": "link:x-pack", + "yauzl": "2.7.0" }, "newFullIndexTemplate": { - "order": 1, - "index_patterns": ["filebeat-7.0.0-alpha1-*"], - "settings": { - "index": { - "lifecycle": { - "name": "my_policy41" - }, - "routing": { - "allocation": { - "include": { - "sattr_name": "warm_node:true" - } - } - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "refresh_interval": "5s", - "number_of_shards": "3", - "number_of_routing_shards": "30", - "number_of_replicas": "1" - } - }, - "mappings": { - "doc": { - "dynamic_templates": [{ - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - }], - "properties": { - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "error": { - "properties": { - "code": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "read_timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "apache2": { - "properties": { - "access": { - "properties": { - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "user_agent": { - "properties": { - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "minor": { - "type": "long" - }, - "major": { - "type": "long" - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "os_minor": { - "type": "long" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "remote_ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "agent": { - "type": "text", - "norms": false - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "client": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "pid": { - "type": "long" - }, - "tid": { - "type": "long" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "icinga": { - "properties": { - "main": { - "properties": { - "facility": { - "type": "keyword", - "ignore_above": 1024 - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "norms": false, - "type": "text" - } - } - }, - "startup": { - "properties": { - "facility": { - "type": "keyword", - "ignore_above": 1024 - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "norms": false, - "type": "text" - } - } - }, - "debug": { - "properties": { - "message": { - "type": "text", - "norms": false - }, - "facility": { - "ignore_above": 1024, - "type": "keyword" - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "logstash": { - "properties": { - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "thread": { - "type": "text", - "norms": false - }, - "log_event": { - "type": "object" - }, - "message": { - "type": "text", - "norms": false - } - } - }, - "slowlog": { - "properties": { - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "event": { - "type": "text", - "norms": false - }, - "plugin_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "took_in_nanos": { - "type": "long" - }, - "plugin_params": { - "type": "text", - "norms": false - }, - "plugin_params_object": { - "type": "object" - }, - "message": { - "norms": false, - "type": "text" - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "thread": { - "type": "text", - "norms": false - }, - "plugin_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "took_in_millis": { - "type": "long" - } - } - } - } - }, - "mysql": { - "properties": { - "slowlog": { - "properties": { - "rows_sent": { - "type": "long" - }, - "id": { - "type": "long" - }, - "rows_examined": { - "type": "long" - }, - "timestamp": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "host": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "query_time": { - "properties": { - "sec": { - "type": "float" - } - } - }, - "lock_time": { - "properties": { - "sec": { - "type": "float" - } - } - } - } - }, - "error": { - "properties": { - "thread_id": { - "type": "long" - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "beat": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "image": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "labels": { - "type": "object" - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "fileset": { - "properties": { - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "@timestamp": { - "type": "date" - }, - "meta": { - "properties": { - "cloud": { - "properties": { - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "machine_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "project_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "kafka": { - "properties": { - "log": { - "properties": { - "trace": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "full": { - "type": "text", - "norms": false - } - } - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "component": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "type": "text", - "norms": false - } - } - } - } - }, - "redis": { - "properties": { - "slowlog": { - "properties": { - "id": { - "type": "long" - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "cmd": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "log": { - "properties": { - "pid": { - "type": "long" - }, - "role": { - "type": "keyword", - "ignore_above": 1024 - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - } - } - } - } - }, - "input": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "iis": { - "properties": { - "access": { - "properties": { - "server_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "cookie": { - "type": "keyword", - "ignore_above": 1024 - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "win32_status": { - "type": "long" - }, - "agent": { - "type": "text", - "norms": false - }, - "user_agent": { - "properties": { - "major": { - "type": "long" - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_minor": { - "type": "long" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "os_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "minor": { - "type": "long" - } - } - }, - "query_string": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - }, - "sub_status": { - "type": "long" - }, - "request_time_ms": { - "type": "long" - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "site_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "body_received": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "error": { - "properties": { - "remote_port": { - "type": "long" - }, - "server_port": { - "type": "long" - }, - "response_code": { - "type": "long" - }, - "reason_phrase": { - "type": "keyword", - "ignore_above": 1024 - }, - "queue_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - }, - "geoip": { - "properties": { - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server_ip": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "fields": { - "type": "object" - }, - "kubernetes": { - "properties": { - "container": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pod": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "namespace": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "annotations": { - "type": "object" - } - } - }, - "source": { - "type": "keyword", - "ignore_above": 1024 - }, - "auditd": { - "properties": { - "log": { - "properties": { - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - } - } - }, - "old_ses": { - "type": "keyword", - "ignore_above": 1024 - }, - "new_ses": { - "type": "keyword", - "ignore_above": 1024 - }, - "res": { - "type": "keyword", - "ignore_above": 1024 - }, - "sequence": { - "type": "long" - }, - "item": { - "type": "keyword", - "ignore_above": 1024 - }, - "items": { - "type": "keyword", - "ignore_above": 1024 - }, - "record_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "old_auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ppid": { - "type": "keyword", - "ignore_above": 1024 - }, - "a0": { - "type": "keyword", - "ignore_above": 1024 - }, - "new_auid": { - "type": "keyword", - "ignore_above": 1024 - }, - "acct": { - "type": "keyword", - "ignore_above": 1024 - }, - "pid": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "nginx": { - "properties": { - "access": { - "properties": { - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "user_agent": { - "properties": { - "os_minor": { - "type": "long" - }, - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - }, - "agent": { - "type": "text", - "norms": false - }, - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "error": { - "properties": { - "pid": { - "type": "long" - }, - "tid": { - "type": "long" - }, - "connection_id": { - "type": "long" - }, - "message": { - "type": "text", - "norms": false - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "offset": { - "type": "long" - }, - "stream": { - "type": "keyword", - "ignore_above": 1024 - }, - "postgresql": { - "properties": { - "log": { - "properties": { - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "thread_id": { - "type": "long" - }, - "database": { - "type": "keyword", - "ignore_above": 1024 - }, - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "timestamp": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "float" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "traefik": { - "properties": { - "access": { - "properties": { - "referrer": { - "type": "keyword", - "ignore_above": 1024 - }, - "geoip": { - "properties": { - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "body_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "user_agent": { - "properties": { - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "minor": { - "type": "long" - }, - "os_minor": { - "type": "long" - }, - "major": { - "type": "long" - }, - "patch": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "type": "keyword", - "ignore_above": 1024 - }, - "os_major": { - "type": "long" - }, - "os_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "frontend_name": { - "type": "text", - "norms": false - }, - "http_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "response_code": { - "type": "long" - }, - "remote_ip": { - "type": "keyword", - "ignore_above": 1024 - }, - "user_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "request_count": { - "type": "long" - }, - "backend_url": { - "type": "text", - "norms": false - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "agent": { - "type": "text", - "norms": false - } - } - } - } - }, - "prospector": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "osquery": { - "properties": { - "result": { - "properties": { - "host_identifier": { - "type": "keyword", - "ignore_above": 1024 - }, - "unix_time": { - "type": "long" - }, - "calendar_time": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "action": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "system": { - "properties": { - "syslog": { - "properties": { - "program": { - "type": "keyword", - "ignore_above": 1024 - }, - "pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "type": "keyword", - "ignore_above": 1024 - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "auth": { - "properties": { - "groupadd": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "type": "long" - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "pid": { - "type": "long" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "useradd": { - "properties": { - "home": { - "type": "keyword", - "ignore_above": 1024 - }, - "shell": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "long" - }, - "gid": { - "type": "long" - } - } - }, - "sudo": { - "properties": { - "error": { - "type": "keyword", - "ignore_above": 1024 - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "pwd": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - }, - "command": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "timestamp": { - "type": "keyword", - "ignore_above": 1024 - }, - "program": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "type": "keyword", - "ignore_above": 1024 - }, - "ssh": { - "properties": { - "geoip": { - "properties": { - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - } - } - }, - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "dropped_ip": { - "type": "ip" - }, - "port": { - "type": "long" - }, - "signature": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - } - }, - "_meta": { - "version": "7.0.0-alpha1" - }, - "date_detection": false - } - }, - "aliases": {} + "@elastic/eui": "v0.0.44", + "@elastic/filesaver": "1.1.2", + "@elastic/numeral": "2.3.2", + "@elastic/ui-ace": "0.2.3", + "@kbn/babel-preset": "link:packages/kbn-babel-preset", + "@kbn/datemath": "link:packages/kbn-datemath", + "@kbn/pm": "link:packages/kbn-pm", + "@kbn/test-subj-selector": "link:packages/kbn-test-subj-selector", + "@kbn/ui-framework": "link:packages/kbn-ui-framework", + "JSONStream": "1.1.1", + "accept-language-parser": "1.2.0", + "angular": "1.6.9", + "angular-aria": "1.6.6", + "angular-elastic": "2.5.0", + "angular-recursion": "^1.0.5", + "angular-route": "1.4.7", + "angular-sanitize": "1.5.7", + "angular-sortable-view": "0.0.15", + "autoprefixer": "6.5.4", + "babel-core": "6.21.0", + "babel-loader": "7.1.2", + "babel-polyfill": "6.20.0", + "babel-register": "6.18.0", + "bluebird": "2.9.34", + "body-parser": "1.12.0", + "boom": "5.2.0", + "brace": "0.10.0", + "bunyan": "1.7.1", + "cache-loader": "1.0.3", + "chalk": "2.3.0", + "check-hash": "1.0.1", + "color": "1.0.3", + "commander": "2.8.1", + "compare-versions": "3.1.0", + "css-loader": "0.28.7", + "custom-event-polyfill": "^0.3.0", + "d3": "3.5.6", + "d3-cloud": "1.2.1", + "dragula": "3.7.0", + "elasticsearch": "^14.2.1", + "elasticsearch-browser": "^14.2.1", + "encode-uri-query": "1.0.0", + "even-better": "7.0.2", + "expiry-js": "0.1.7", + "extract-text-webpack-plugin": "3.0.1", + "file-loader": "1.1.4", + "font-awesome": "4.4.0", + "glob": "5.0.13", + "glob-all": "3.0.1", + "good-squeeze": "2.1.0", + "h2o2": "5.1.1", + "handlebars": "4.0.5", + "hapi": "14.2.0", + "hjson": "3.1.0", + "http-proxy-agent": "1.0.0", + "https-proxy-agent": "2.1.1", + "inert": "4.0.2", + "jade": "1.11.0", + "jade-loader": "0.8.0", + "joi": "10.4.1", + "jquery": "^3.3.1", + "js-yaml": "3.4.1", + "json-stringify-pretty-compact": "1.0.4", + "json-stringify-safe": "5.0.1", + "jstimezonedetect": "1.0.5", + "leaflet": "1.0.3", + "leaflet-draw": "0.4.10", + "leaflet-responsive-popup": "0.2.0", + "leaflet-vega": "^0.8.6", + "leaflet.heat": "0.2.0", + "less": "2.7.1", + "less-loader": "4.0.5", + "lodash": "3.10.1", + "lru-cache": "4.1.1", + "markdown-it": "8.3.2", + "minimatch": "2.0.10", + "mkdirp": "0.5.1", + "moment": "^2.20.1", + "moment-timezone": "^0.5.14", + "mustache": "2.3.0", + "ngreact": "0.5.1", + "no-ui-slider": "1.2.0", + "node-fetch": "1.3.2", + "pegjs": "0.9.0", + "postcss-loader": "2.0.6", + "prop-types": "15.5.8", + "proxy-from-env": "1.0.0", + "querystring-browser": "1.0.4", + "raw-loader": "0.5.1", + "react": "^16.3.0", + "react-addons-shallow-compare": "15.6.2", + "react-anything-sortable": "^1.7.4", + "react-color": "^2.13.8", + "react-dom": "^16.3.0", + "react-grid-layout": "^0.16.2", + "react-input-range": "^1.3.0", + "react-markdown": "^3.1.4", + "react-redux": "^5.0.6", + "react-router-dom": "4.2.2", + "react-select": "^1.2.0", + "react-sizeme": "^2.3.6", + "react-toggle": "4.0.2", + "reactcss": "1.2.3", + "redux": "3.7.2", + "redux-actions": "2.2.1", + "redux-thunk": "2.2.0", + "regression": "2.0.0", + "request": "^2.85.0", + "reselect": "^3.0.1", + "resize-observer-polyfill": "1.2.1", + "rimraf": "2.4.3", + "rison-node": "1.0.0", + "rxjs": "5.4.3", + "script-loader": "0.7.2", + "semver": "5.1.0", + "style-loader": "0.19.0", + "tar": "2.2.0", + "tinygradient": "0.3.0", + "tinymath": "0.2.1", + "topojson-client": "3.0.0", + "trunc-html": "1.0.2", + "trunc-text": "1.0.2", + "uglifyjs-webpack-plugin": "0.4.6", + "ui-select": "0.19.6", + "url-loader": "0.5.9", + "uuid": "3.0.2", + "validate-npm-package-name": "2.2.2", + "vega-lib": "^3.3.1", + "vega-lite": "^2.4.0", + "vega-tooltip": "^0.9.14", + "vega-schema-url-parser": "1.0.0", + "vision": "4.1.0", + "webpack": "3.6.0", + "webpack-merge": "4.1.0", + "whatwg-fetch": "^2.0.3", + "wreck": "12.4.0", + "x-pack": "link:x-pack", + "yauzl": "2.7.0", + "zzz": "1.0.0" }, "hasChanged": true } From b2251b63d5f9afcca9073f33eb1bd9f1647322df Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 7 May 2018 13:51:02 -0400 Subject: [PATCH 007/102] Some feedback on copy --- .../components/configuration/configuration.js | 32 ++++++++++++----- .../template_selection/template_selection.js | 12 ++----- .../index_template/index_template.js | 35 ++++++++++++++----- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index 7acd83c378a41..0709a5d9c41d6 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; import { @@ -18,6 +18,8 @@ import { EuiCallOut, EuiFormRow, EuiButtonEmpty, + EuiIconTip, + EuiLink, } from '@elastic/eui'; import { STRUCTURE_NODE_ATTRS, @@ -82,12 +84,11 @@ export class Configuration extends Component { const primaryNodeErrors = isPrimaryShardCountHigherThanSelectedNodeAttrsCount ? ( - The selected primary shard count is higher than the number of nodes - matching the selected attributes. + The shard count should be lower than the number of nodes that match the selected attributes. ) : null; @@ -101,7 +102,16 @@ export class Configuration extends Component { + Where do you want your hot indices to live +   + + + } errorKey={STRUCTURE_NODE_ATTRS} isShowingErrors={isShowingErrors} errors={errors} @@ -125,15 +135,21 @@ export class Configuration extends Component { this.setState({ isShowingNodeDetailsFlyout: true }) } > - See more details about these nodes + View node details ) : null} - -

Optimize these values for throughput. (Add more)

+ +

+ The best way to determine how many shards you need is to benchmark + using realistic data and queries on your hardware.{' '} + + Learn more. + +

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 5828749b4f70c..808c0e1dc3caa 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -7,7 +7,7 @@ import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { EuiSelect, EuiLink } from '@elastic/eui'; +import { EuiSelect } from '@elastic/eui'; import { ErrableFormRow } from '../../../../form_errors'; import { STRUCTURE_TEMPLATE_NAME } from '../../../../../../store/constants'; @@ -57,18 +57,10 @@ export class TemplateSelection extends PureComponent { {/* {noMatchingIndicesWarning} */} - Learn how to{' '} - - add a new index template - . - - } > { this.setState({ isShowingErrors: true }); if (await this.validate()) { this.props.done(); } - } + }; render() { const { errors } = this.props; @@ -58,6 +60,21 @@ export class IndexTemplate extends Component {

Select a template

+ + + + +
+ A template defines the settings, mappings, and aliases to apply + when you create an index. +
+
+ + + Learn more + +
+
Date: Fri, 11 May 2018 14:59:02 -0400 Subject: [PATCH 008/102] Updating copy, moving components around and fixing bugs with the diff view --- .../public/api/index.js | 7 +- .../public/lib/diff_ace_addons.js | 48 +-- .../public/lib/diff_tools.js | 68 +++- .../components/configuration/configuration.js | 76 ++--- .../template_selection.container.js | 16 +- .../template_selection/template_selection.js | 75 +++-- .../index_template/index_template.js | 5 +- .../node_attrs_details.container.js | 3 +- .../node_attrs_details/node_attrs_details.js | 34 +- .../components/cold_phase/cold_phase.js | 59 ++-- .../components/delete_phase/delete_phase.js | 3 +- .../components/hot_phase/hot_phase.js | 42 +-- .../components/warm_phase/warm_phase.js | 215 ++++++++----- .../policy_configuration.container.js | 7 +- .../policy_configuration.js | 95 +++--- .../policy_selection/policy_selection.js | 25 +- .../wizard/components/review/diff_view.js | 18 +- .../components/review/review.container.js | 12 +- .../wizard/components/review/review.js | 135 ++++++-- .../public/sections/wizard/diff.json | 303 ++---------------- .../public/sections/wizard/wizard.js | 47 +-- .../public/store/constants.js | 14 +- .../public/store/reducers/policies.js | 7 +- .../public/store/selectors/index_template.js | 20 +- .../public/store/selectors/lifecycle.js | 51 ++- .../public/store/selectors/policies.js | 1 + .../indices/register_get_affected_route.js | 28 +- .../api/templates/register_fetch_route.js | 1 + 28 files changed, 720 insertions(+), 695 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index 1ce9bdb957c40..c82f07b7d6556 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -11,6 +11,8 @@ export const setHttpClient = (client) => { }; const apiPrefix = chrome.addBasePath('/api/index_lifecycle_management'); +console.log('apiPrefix', apiPrefix); + export async function loadNodes() { const response = await httpClient.get(`${apiPrefix}/nodes/list`); return response.data; @@ -47,6 +49,9 @@ export async function bootstrap(indexName, aliasName) { } export async function getAffectedIndices(indexTemplateName, policyName) { - const response = await httpClient.get(`${apiPrefix}/indices/affected/${indexTemplateName}/${policyName}`); + const path = policyName + ? `${apiPrefix}/indices/affected/${indexTemplateName}/${encodeURIComponent(policyName)}` + : `${apiPrefix}/indices/affected/${indexTemplateName}`; + const response = await httpClient.get(path); return response.data; } diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js index f1f3b0a018d94..31e64a3c5b1d7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js @@ -6,24 +6,30 @@ import ace from 'brace'; import { ADDITION_PREFIX, REMOVAL_PREFIX } from './diff_tools'; -function findInObject(key, obj) { +function findInObject(key, obj, debug) { + // debug && console.log('findInObject()', key, obj); const objKeys = Object.keys(obj); for (const objKey of objKeys) { if (objKey === key) { + // debug && console.log('findInObject() FOUND', key); return obj[objKey]; } if (typeof obj[objKey] === 'object' && !Array.isArray(obj[objKey])) { - const item = findInObject(key, obj[objKey]); + const item = findInObject(key, obj[objKey], debug); if (item !== false) { + // debug && console.log('findInObject() FOUND 2', key); return item; } } } + // debug && console.log('findInObject() NOT FOUND', key); return false; } function getDiffClasses(key, val, jsonObject) { - // const debug = key === 'react'; + const debug = false;//key === 'name' && val === '"t"'; + + debug && console.log('getDiffClasses()', key, val); let value = val; if (value.endsWith(',')) { @@ -33,15 +39,15 @@ function getDiffClasses(key, val, jsonObject) { value = value.slice(1, -1); } - const additionValue = findInObject(`${ADDITION_PREFIX}${key}`, jsonObject); - const removalValue = findInObject(`${REMOVAL_PREFIX}${key}`, jsonObject); + const additionValue = findInObject(`${ADDITION_PREFIX}${key}`, jsonObject, debug); + const removalValue = findInObject(`${REMOVAL_PREFIX}${key}`, jsonObject, debug); const isAddition = Array.isArray(additionValue) ? !!additionValue.find(v => v === value) - : additionValue === value; + : (additionValue === value || (additionValue && value === '{')); const isRemoval = Array.isArray(removalValue) ? !!removalValue.find(v => v === value) - : removalValue === value; + : (removalValue === value || (removalValue && value === '{')); let diffClasses = ''; if (isAddition) { @@ -52,20 +58,24 @@ function getDiffClasses(key, val, jsonObject) { diffClasses = 'variable'; } - // debug && console.log(`getDiffClasses() - // key='${key}' - // value='${value}' - // additionValue='${additionValue}' - // removalValue='${removalValue}' - // isAddition=${isAddition} - // isRemoval=${isRemoval} - // diffClasses='${diffClasses}' - // `); + debug && console.log(`getDiffClasses() + key='${key}' + value='${value}' + additionValue='${additionValue}' + removalValue='${removalValue}' + isAddition=${isAddition} + isRemoval=${isRemoval} + diffClasses='${diffClasses}' + `); return diffClasses; } -export const addDiffAddonsForAce = jsonObject => { +let currentJsonObject; +const getCurrentJsonObject = () => currentJsonObject; +export const setCurrentJsonObject = jsonObject => currentJsonObject = jsonObject; + +export const addDiffAddonsForAce = () => { const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules') .JsonHighlightRules; class DiffJsonHighlightRules extends JsonHighlightRules { @@ -78,7 +88,7 @@ export const addDiffAddonsForAce = jsonObject => { start: [ { token: (key, val) => { - return getDiffClasses(key, val, jsonObject); + return getDiffClasses(key, val, getCurrentJsonObject()); }, regex: '(?:"([\\w-+]+)"\\s*:\\s*([^\\n\\[]+)$)', }, @@ -95,7 +105,7 @@ export const addDiffAddonsForAce = jsonObject => { array: [ { token: val => { - return getDiffClasses(currentArrayKey, val, jsonObject); + return getDiffClasses(currentArrayKey, val, getCurrentJsonObject()); }, next: 'start', regex: '\\s*"([^"]+)"\\s*', diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js index d5baf8ded58a9..30e4dacbb3cfb 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js @@ -6,13 +6,18 @@ export const ADDITION_PREFIX = '$$$'; export const REMOVAL_PREFIX = '^^^'; -const escapePrefix = prefix => prefix.split('').map(i => `\\${i}`).join(''); +const escapePrefix = prefix => + prefix + .split('') + .map(i => `\\${i}`) + .join(''); const removePrefixRegex = new RegExp( `(${escapePrefix(ADDITION_PREFIX)})|(${escapePrefix(REMOVAL_PREFIX)})`, 'g' ); -export const isBoolean = value => JSON.parse(value) === true || JSON.parse(value) === false; +export const isBoolean = value => + JSON.parse(value) === true || JSON.parse(value) === false; const isObject = value => typeof value === 'object' && !Array.isArray(value); const isDifferent = (obj, key, value) => { // If the object does not contain the key, then ignore since it's not a removal or addition @@ -46,9 +51,49 @@ const getAdditions = obj => { return result; }; -export const removePrefixes = str => str.replace(removePrefixRegex, ''); +export const removePrefixes = obj => { + if (typeof obj === 'string') { + return obj.replace(removePrefixRegex, ''); + } + + if (!obj || typeof obj !== 'object') { + return obj; + } + + return Object.keys(obj).reduce( + (newObj, key) => ({ + ...newObj, + [key.replace(removePrefixRegex, '')]: obj[key] && typeof obj[key] === 'object' ? + removePrefixes(obj[key]) : + obj[key], + }), {} + ); +}; -export const mergeAndPreserveDuplicateKeys = (source, target, result = {}, changes = []) => { +const normalizeChange = (key, value) => { + if (typeof value === 'string') { + return { + key: removePrefixes(key), + value: removePrefixes(value) + }; + } + return Object.entries(value).reduce((accum, [key, value]) => { + if (typeof value === 'string') { + return { + key: removePrefixes(key), + value: removePrefixes(value) + }; + } + return normalizeChange(key, value); + }, {}); +}; + +export const mergeAndPreserveDuplicateKeys = ( + source, + target, + result = {}, + changes = [] +) => { for (const [key, value] of Object.entries(source)) { // const debug = key === 'fooobar'; // debug && console.log('mergeAndPreserveDuplicateKeys', key, value, target); @@ -58,8 +103,8 @@ export const mergeAndPreserveDuplicateKeys = (source, target, result = {}, chang result[`${ADDITION_PREFIX}${key}`] = target[key]; changes.push({ key, - original: value, - updated: target[key], + original: removePrefixes(value), + updated: removePrefixes(target[key]), }); } else if (isObject(value)) { // debug && console.log('value is object', target[key]); @@ -89,10 +134,15 @@ export const mergeAndPreserveDuplicateKeys = (source, target, result = {}, chang } else { result[`${ADDITION_PREFIX}${key}`] = value; } + + const normalized = normalizeChange(key, result[`${ADDITION_PREFIX}${key}`]); changes.push({ - key, - updated: result[`${ADDITION_PREFIX}${key}`], + key: normalized.key, + updated: normalized.value, }); } - return { result, changes }; + return { + result, + changes + }; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index 0709a5d9c41d6..922e593529283 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -16,7 +16,6 @@ import { EuiFieldNumber, EuiHorizontalRule, EuiCallOut, - EuiFormRow, EuiButtonEmpty, EuiIconTip, EuiLink, @@ -99,54 +98,45 @@ export class Configuration extends Component {

Configure options

- - - - Where do you want your hot indices to live -   - -
+ + Where do you want your hot indices to live +   + + + } + errorKey={STRUCTURE_NODE_ATTRS} + isShowingErrors={isShowingErrors} + errors={errors} + helpText={selectedNodeAttrs ? ( + + this.setState({ isShowingNodeDetailsFlyout: true }) } - errorKey={STRUCTURE_NODE_ATTRS} - isShowingErrors={isShowingErrors} - errors={errors} > - { - await setSelectedNodeAttrs(e.target.value); - validate(); - }} - options={nodeOptions} - /> - -
- {selectedNodeAttrs ? ( - - - - this.setState({ isShowingNodeDetailsFlyout: true }) - } - > - View node details - - - + View node details + ) : null} -
- - + > + { + await setSelectedNodeAttrs(e.target.value); + validate(); + }} + options={nodeOptions} + /> + +

The best way to determine how many shards you need is to benchmark using realistic data and queries on your hardware.{' '} - + Learn more.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js index 8a5f80515d899..fb11120d37d3c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js @@ -9,19 +9,33 @@ import { TemplateSelection as PresentationComponent } from './template_selection import { getIndexTemplateOptions, getSelectedIndexTemplateName, + getIndexName, + getAliasName, + getBootstrapEnabled, + getSelectedIndexTemplateIndices, } from '../../../../../../store/selectors'; import { fetchIndexTemplates, - setSelectedIndexTemplate + setSelectedIndexTemplate, + setAliasName, + setBootstrapEnabled, + setIndexName } from '../../../../../../store/actions'; export const TemplateSelection = connect( state => ({ templateOptions: getIndexTemplateOptions(state), selectedIndexTemplateName: getSelectedIndexTemplateName(state), + bootstrapEnabled: getBootstrapEnabled(state), + aliasName: getAliasName(state), + indexName: getIndexName(state), + selectedIndexTemplateIndices: getSelectedIndexTemplateIndices(state), }), { fetchIndexTemplates, setSelectedIndexTemplate, + setBootstrapEnabled, + setIndexName, + setAliasName, } )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 808c0e1dc3caa..a158b406762d5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -7,9 +7,13 @@ import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { EuiSelect } from '@elastic/eui'; +import { EuiSelect, EuiFormRow, EuiSwitch, EuiFieldText } from '@elastic/eui'; import { ErrableFormRow } from '../../../../form_errors'; -import { STRUCTURE_TEMPLATE_NAME } from '../../../../../../store/constants'; +import { + STRUCTURE_TEMPLATE_NAME, + STRUCTURE_INDEX_NAME, + STRUCTURE_ALIAS_NAME, +} from '../../../../../../store/constants'; export class TemplateSelection extends PureComponent { static propTypes = { @@ -31,31 +35,22 @@ export class TemplateSelection extends PureComponent { const { setSelectedIndexTemplate, validate, + setBootstrapEnabled, + setIndexName, + setAliasName, + bootstrapEnabled, templateOptions, + selectedIndexTemplateIndices, + indexName, + aliasName, selectedIndexTemplateName, errors, isShowingErrors, } = this.props; - // const noMatchingIndicesWarning = - // affectedIndices.length > 0 ? ( - // - //

- // The selected index template `{selectedIndexTemplateName}` matches - // existing indices which will not be affected by these changes. - //

- //
- // ) : null; - return ( - {/* {noMatchingIndicesWarning} */} + {selectedIndexTemplateName && selectedIndexTemplateIndices.length === 0 ? ( + + + setBootstrapEnabled(e.target.checked)} + label={Create an index and alias for this template} + /> + + {bootstrapEnabled ? ( + + + { + await setIndexName(e.target.value); + validate(); + }} + /> + + + { + await setAliasName(e.target.value); + validate(); + }} + /> + + + ) : null} + + ) : null} ); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js index 62d2c71597a8a..b80cbaf77d919 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js @@ -6,6 +6,7 @@ import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; import { TemplateSelection } from './components/template_selection'; import { Configuration } from './components/configuration'; @@ -48,6 +49,8 @@ export class IndexTemplate extends Component { this.setState({ isShowingErrors: true }); if (await this.validate()) { this.props.done(); + } else { + toastNotifications.addDanger('Please fix errors on the page.'); } }; @@ -65,7 +68,7 @@ export class IndexTemplate extends Component {
- A template defines the settings, mappings, and aliases to apply + An index template defines the settings, mappings, and aliases to apply when you create an index.
diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js index 8887b13740e04..3128a38c2c34f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js @@ -6,12 +6,13 @@ import { connect } from 'react-redux'; import { NodeAttrsDetails as PresentationComponent } from './node_attrs_details'; -import { getNodeDetails } from '../../../../store/selectors'; +import { getNodeDetails, getExistingAllocationRules } from '../../../../store/selectors'; import { fetchNodeDetails } from '../../../../store/actions'; export const NodeAttrsDetails = connect( (state, ownProps) => ({ details: getNodeDetails(state, ownProps.selectedNodeAttrs), + allocationRules: getExistingAllocationRules(state), }), { fetchNodeDetails } )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js index 0af37a84918a8..6bb03853e8f31 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { PureComponent } from 'react'; +import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; import { @@ -25,6 +25,7 @@ export class NodeAttrsDetails extends PureComponent { details: PropTypes.array, selectedNodeAttrs: PropTypes.string.isRequired, + allocationRules: PropTypes.object, }; componentWillMount() { @@ -32,29 +33,28 @@ export class NodeAttrsDetails extends PureComponent { } render() { - const { selectedNodeAttrs, details, close } = this.props; + const { selectedNodeAttrs, allocationRules, details, close } = this.props; return ( -

- Below is a list of nodes that contain the attribute: `{ - selectedNodeAttrs - }` -

+

Nodes that contain the attribute: `{selectedNodeAttrs}`

- - Be aware that the nodes listed here only directly match the node - attribute string and other nodes might be affected by this policy - due to other allocation rules. - - + {allocationRules ? ( + + + Be aware that this index template has existing allocation rules + which will affect the list of nodes these indices can be allocated to. + + + + ) : null}

- This phase is optional. Re-allocate your indices again and - modify the number of replicas. + This phase is optional. Your read-only index is queried less frequently. + Use this phase when the index no longer needs to be on the most performant hardware.

@@ -172,37 +169,29 @@ export class ColdPhase extends PureComponent { - - - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - - - {phaseData[PHASE_NODE_ATTRS] ? ( - - - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} - > - See more details about these nodes - - - + View node details + ) : null} - + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + @@ -229,7 +218,7 @@ export class ColdPhase extends PureComponent { setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) } > - Set to same as warm phase + Use number in warm phase diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 9b1998df49500..ed28165c9ccc5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -86,8 +86,7 @@ export class DeletePhase extends PureComponent {

- This phase is optional. Delete your indices after a - configured amount of time. + This phase is optional. Your data is no longer useful. Define how long you want to retain it.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 4111e11a31c4d..ca35b917b0e7c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -4,9 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; @@ -21,7 +18,8 @@ import { EuiAccordion, EuiFieldNumber, EuiSelect, - EuiSwitch + EuiSwitch, + EuiLink } from '@elastic/eui'; import { PHASE_ROLLOVER_ALIAS, @@ -93,7 +91,7 @@ export class HotPhase extends PureComponent { -

This phase is required. Rollover data by time and size.

+

This phase is required. Your index is being queried and in active writing mode.

{isShowingErrors ? ( @@ -111,7 +109,7 @@ export class HotPhase extends PureComponent { >
-

Rollover configuration

+

Rollover condition

+ + +

+ + Rollover the index when it gets too big or too old. The alias will switch to the new index. + +   + + Learn more. + +

+
{phaseData[PHASE_ROLLOVER_ENABLED] ? ( - {/* - - - { - showErrorsFor(PHASE_ROLLOVER_ALIAS); - setPhaseData(PHASE_ROLLOVER_ALIAS, e.target.value); - }} - /> - - - */}

- This phase is optional. Re-allocate indices, redefine number - of active shards, replicas, and compress even further. + This phase is optional. Your index is frequently queried, + but is read-only. Use this phase to optimize for search.

@@ -171,7 +173,7 @@ export class WarmPhase extends Component { { await this.setState({ applyOnRollover: e.target.checked }); @@ -222,37 +224,34 @@ export class WarmPhase extends Component { - - - - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - - - {phaseData[PHASE_NODE_ATTRS] ? ( - - - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} - > - See more details about these nodes - - - - ) : null} - + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS]) + } + > + View node details + + ) : null + } + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + - Set to same as hot phase + Use number in hot phase @@ -293,56 +292,89 @@ export class WarmPhase extends Component {

Shrink

+ + + Shrink the index into a new index with fewer primary shards.{' '} + + Learn more. + + + - - - - { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - e.target.value - ); - validate(); - }} - /> - - - - - { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - hotPhasePrimaryShardCount - ); - validate(); - }} - > - Set to same as hot phase - - - - + { + await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); + validate(); + }} + label="Enable shrink" + /> - + + + {phaseData[PHASE_SHRINK_ENABLED] ? ( + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + e.target.value + ); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + hotPhasePrimaryShardCount + ); + validate(); + }} + > + Use number in hot phase + + + + + + + + ) : null}

Force merge

+ + + Reduce the number of segments in your shard by and merging smaller + files and clearing deleted ones.{' '} + + Learn More + + + + { await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); @@ -352,20 +384,25 @@ export class WarmPhase extends Component { - - { - await setPhaseData(PHASE_FORCE_MERGE_SEGMENTS, e.target.value); - validate(); - }} - /> - + {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( + + { + await setPhaseData( + PHASE_FORCE_MERGE_SEGMENTS, + e.target.value + ); + validate(); + }} + /> + + ) : null}
); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js index a64806993fb5c..29da1a719d9b5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js @@ -4,9 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import { connect } from 'react-redux'; import { PolicyConfiguration as PresentationComponent } from './policy_configuration'; import { @@ -17,7 +14,8 @@ import { getIndexName, getAliasName, getSaveAsNewPolicy, - getSelectedOriginalPolicyName + getSelectedOriginalPolicyName, + getIsSelectedPolicySet } from '../../../../store/selectors'; import { setBootstrapEnabled, @@ -29,6 +27,7 @@ import { export const PolicyConfiguration = connect( state => ({ + isSelectedPolicySet: getIsSelectedPolicySet(state), selectedPolicyName: getSelectedPolicyName(state), selectedIndexTemplateName: getSelectedIndexTemplateName(state), affectedIndexTemplates: getAffectedIndexTemplates(state), diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index 29ef2b1c5cfb4..df1c4199ab507 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -4,18 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; import { EuiTitle, EuiSpacer, - EuiCallOut, EuiHorizontalRule, EuiButton, - EuiFormRow, - EuiSwitch, - EuiFieldText, EuiButtonEmpty, } from '@elastic/eui'; import { HotPhase } from './components/hot_phase'; @@ -27,13 +24,10 @@ import { PHASE_COLD, PHASE_DELETE, PHASE_WARM, - STRUCTURE_POLICY_NAME, - STRUCTURE_INDEX_NAME, - STRUCTURE_ALIAS_NAME, } from '../../../../store/constants'; import { hasErrors } from '../../../../lib/find_errors'; -import { ErrableFormRow } from '../../form_errors'; import { NodeAttrsDetails } from '../node_attrs_details'; +import { PolicySelection } from '../policy_selection/policy_selection.container'; export class PolicyConfiguration extends Component { static propTypes = { @@ -76,6 +70,8 @@ export class PolicyConfiguration extends Component { this.setState({ isShowingErrors: true }); if (await this.validate()) { this.props.done(); + } else { + toastNotifications.addDanger('Please fix errors on the page.'); } }; @@ -85,62 +81,55 @@ export class PolicyConfiguration extends Component { render() { const { - setSelectedPolicyName, - setSaveAsNewPolicy, - setBootstrapEnabled, - setIndexName, - setAliasName, - validate, back, - affectedIndexTemplates, - selectedIndexTemplateName, selectedPolicyName, - saveAsNewPolicy, + isSelectedPolicySet, errors, - bootstrapEnabled, - indexName, - aliasName, - originalPolicyName, } = this.props; const { isShowingErrors } = this.state; - const singleTemplate = ( - - This policy is only attached to the selected template{' '} - {selectedIndexTemplateName}. - - ); + if (!isSelectedPolicySet) { + return ( + + ); + } - const multiTemplate = ( - - This policy is attached to{' '} - {affectedIndexTemplates.length - 1} other template(s){' '} - besides {selectedIndexTemplateName}. - - ); + // const singleTemplate = ( + // + // This policy is only attached to the selected template{' '} + // {selectedIndexTemplateName}. + // + // ); + + // const multiTemplate = ( + // + // This policy is attached to{' '} + // {affectedIndexTemplates.length - 1} other template(s){' '} + // besides {selectedIndexTemplateName}. + // + // ); - const warningMessage = - affectedIndexTemplates.length === 1 && - affectedIndexTemplates[0] === selectedIndexTemplateName - ? singleTemplate - : multiTemplate; + // const warningMessage = + // affectedIndexTemplates.length === 1 && + // affectedIndexTemplates[0] === selectedIndexTemplateName + // ? singleTemplate + // : multiTemplate; return (
+ + -

Edit policy: {selectedPolicyName}

+

+ {!selectedPolicyName ? 'Edit new policy' : `Edit policy ${selectedPolicyName}`} +

+
+ + +
Configure the phases of your data and when to transition between them. Only the hot phase is required.
- - - Only the hot phase is required. - - } - /> - + {/* @@ -248,9 +237,9 @@ export class PolicyConfiguration extends Component { ) : null} - + */} - + {/* */} -

Select a policy to start from

+

Select a policy

- You can edit existing policies and save them under a new name - later + An index lifecycle policy is a blueprint for transitioning your data over time. + You can create a new policy or edit an existing policy and save it with a new name.

@@ -90,15 +85,15 @@ export class PolicySelection extends Component { ))} - + {/* */} - Back - +
*/}
); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js index 27d206d1f797b..77809dc140e53 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js @@ -22,7 +22,7 @@ import { mergeAndPreserveDuplicateKeys, removePrefixes, } from '../../../../lib/diff_tools'; -import { addDiffAddonsForAce } from '../../../../lib/diff_ace_addons'; +import { addDiffAddonsForAce, setCurrentJsonObject } from '../../../../lib/diff_ace_addons'; export class DiffView extends PureComponent { static propTypes = { @@ -35,8 +35,11 @@ export class DiffView extends PureComponent { scrollToKey = (key, value) => { const editorDom = this.aceEditor.aceEditor.refEditor; const editor = ace.edit(editorDom); - const escapedValue = value.replace(/\^/, '\\^'); + const escapedValue = value.replace(/\^/g, '\\^'); const range = editor.find(new RegExp(`"${key}"\\s*:\\s*"*(${escapedValue})"*`), { regex: true }); + if (!range) { + return; + } editor.gotoLine(range.start.row + 1, range.start.column); } @@ -52,14 +55,17 @@ export class DiffView extends PureComponent { newFullIndexTemplate ); - // console.log('mergedJson', mergedJson, changes); + console.log('mergedJson', mergedJson, changes); // Strip the ^ and $ characters const mergedJsonAsString = removePrefixes( JSON.stringify(mergedJson, null, 2) ); - addDiffAddonsForAce(mergedJson); + // console.log('mergedJsonAsString', mergedJsonAsString); + + setCurrentJsonObject(mergedJson); + addDiffAddonsForAce(); return ( @@ -75,10 +81,10 @@ export class DiffView extends PureComponent { {original ? ( - Changing `{original}` to `{updated}`` + Changing `{JSON.stringify(original)}` to `{JSON.stringify(updated)}`` ) : ( - Adding with `{updated}` + Adding with `{JSON.stringify(updated)}` )} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js index a809576f77dd9..6c4ee3b274347 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js @@ -19,10 +19,14 @@ import { getSelectedOriginalPolicyName, getAliasName, getBootstrapEnabled, + getIndexName, } from '../../../../store/selectors'; import { setSelectedPolicyName, setSaveAsNewPolicy, + setAliasName, + setIndexName, + setBootstrapEnabled, } from '../../../../store/actions'; export const Review = connect( @@ -33,13 +37,19 @@ export const Review = connect( lifecycle: getLifecycle(state), bootstrapEnabled: getBootstrapEnabled(state), aliasName: getAliasName(state), - selectedIndexTemplateName: getSelectedIndexTemplateName(state), selectedPolicyName: getSelectedPolicyName(state), saveAsNewPolicy: getSaveAsNewPolicy(state), originalPolicyName: getSelectedOriginalPolicyName(state), + + /* start might go away */ + indexName: getIndexName(state), + /* end might go away */ }), { setSelectedPolicyName, setSaveAsNewPolicy, + setBootstrapEnabled, + setIndexName, + setAliasName, } )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index 66c54a738142f..5c61331258f9f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -6,6 +6,7 @@ import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; // import DiffEditor from 'react-ace/lib/diff'; import './review.less'; @@ -24,10 +25,17 @@ import { EuiFlexGrid, EuiPanel, EuiText, - EuiButtonEmpty + EuiButtonEmpty, + EuiFormRow, + EuiSwitch, + EuiFieldText, + EuiLoadingSpinner, } from '@elastic/eui'; import { getAffectedIndices } from '../../../../api'; import { DiffView } from './diff_view'; +import { ErrableFormRow } from '../../form_errors'; +import { STRUCTURE_POLICY_NAME } from '../../../../store/constants'; +import { hasErrors } from '../../../../lib/find_errors'; export class Review extends Component { static propTypes = { @@ -52,22 +60,68 @@ export class Review extends Component { this.state = { selectedTab: 1, affectedIndices: [], + isLoadingAffectedIndices: false, }; + + this.currentFetchTimeout = null; + } + + fetchAffectedIndices = () => { + if (this.currentFetchTimeout) { + clearTimeout(this.currentFetchTimeout); + } + + this.setState({ isLoadingAffectedIndices: true }); + this.currentFetchTimeout = setTimeout(async () => { + const affectedIndices = await getAffectedIndices( + this.props.selectedIndexTemplateName, + this.props.selectedPolicyName + ); + this.setState({ affectedIndices, isLoadingAffectedIndices: false }); + }, 500); } async componentWillMount() { - const affectedIndices = await getAffectedIndices( - this.props.selectedIndexTemplateName, - this.props.selectedPolicyName - ); - this.setState({ affectedIndices }); + this.fetchAffectedIndices(); } + async componentWillReceiveProps(nextProps) { + if (nextProps.selectedPolicyName !== this.props.selectedPolicyName) { + this.fetchAffectedIndices(); + } + } + + validate = async () => { + await this.props.validate(); + const noErrors = !hasErrors(this.props.errors); + return noErrors; + }; + + submit = async () => { + this.setState({ isShowingErrors: true }); + if (await this.validate()) { + this.props.done(); + } else { + toastNotifications.addDanger('Please fix errors on the page.'); + } + }; + render() { const { done, back, + /* Start might move */ + setSelectedPolicyName, + setSaveAsNewPolicy, + validate, + + errors, + selectedPolicyName, + saveAsNewPolicy, + originalPolicyName, + /* End might move */ + selectedIndexTemplateName, affectedIndexTemplates, templateDiff, @@ -76,25 +130,62 @@ export class Review extends Component { aliasName, } = this.props; - const { affectedIndices } = this.state; + const { affectedIndices, isLoadingAffectedIndices, isShowingErrors } = this.state; + + // console.log('render', affectedIndices); return (
- + {/*

Changes that will occur

+ */} + + + {originalPolicyName ? ( + + { + await setSaveAsNewPolicy(e.target.checked); + validate(); + }} + label={ + + Save this as a new policy so it does not + effect other templates. + + } + /> + + ) : null} + {saveAsNewPolicy ? ( + + { + await setSelectedPolicyName(e.target.value); + validate(); + }} + /> + + ) : null} + + -

{affectedIndexTemplates.length}

+

{affectedIndexTemplates.length} Affected Index {affectedIndexTemplates.length === 1 ? 'Template' : 'Templates' }

-

- Index templates affected by this change (to - the selected policy): -

    {affectedIndexTemplates.map(template => (
  • {template}
  • @@ -106,16 +197,16 @@ export class Review extends Component { -

    {affectedIndices.length}

    +

    {affectedIndices.length} Affected {affectedIndices.length === 1 ? 'Index' : 'Indices' }

    -

    - Indices affected by this change (to the - selected policy): -

    -
      - {affectedIndices.map(index =>
    • {index}
    • )} -
    + { isLoadingAffectedIndices ? ( + + ) : ( +
      + {affectedIndices.map(index =>
    • {index}
    • )} +
    + ) }
    @@ -177,7 +268,7 @@ export class Review extends Component { iconType="check" onClick={() => done(lifecycle)} > - Looks good, make these changes + Looks good, save changes
); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json index 0411f08b735f4..9ce2e9b307baa 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json @@ -1,284 +1,33 @@ { "originalFullIndexTemplate": { - "@elastic/eui": "v0.0.44", - "@elastic/filesaver": "1.1.2", - "@elastic/numeral": "2.3.2", - "@elastic/ui-ace": "0.2.3", - "@kbn/babel-preset": "link:packages/kbn-babel-preset", - "@kbn/datemath": "link:packages/kbn-datemath", - "@kbn/pm": "link:packages/kbn-pm", - "@kbn/test-subj-selector": "link:packages/kbn-test-subj-selector", - "@kbn/ui-framework": "link:packages/kbn-ui-framework", - "JSONStream": "1.1.1", - "accept-language-parser": "1.2.0", - "angular": "1.6.9", - "angular-aria": "1.6.6", - "angular-elastic": "2.5.0", - "angular-recursion": "^1.0.5", - "angular-route": "1.4.7", - "angular-sanitize": "1.5.7", - "angular-sortable-view": "0.0.15", - "autoprefixer": "6.5.4", - "babel-core": "6.21.0", - "babel-loader": "7.1.2", - "babel-polyfill": "6.20.0", - "babel-register": "6.18.0", - "bluebird": "2.9.34", - "body-parser": "1.12.0", - "boom": "5.2.0", - "brace": "0.10.0", - "bunyan": "1.7.1", - "cache-loader": "1.0.3", - "chalk": "2.3.0", - "check-hash": "1.0.1", - "color": "1.0.3", - "commander": "2.8.1", - "compare-versions": "3.1.0", - "css-loader": "0.28.7", - "custom-event-polyfill": "^0.3.0", - "d3": "3.5.6", - "d3-cloud": "1.2.1", - "dragula": "3.7.0", - "elasticsearch": "^14.2.1", - "elasticsearch-browser": "^14.2.1", - "encode-uri-query": "1.0.0", - "even-better": "7.0.2", - "expiry-js": "0.1.7", - "extract-text-webpack-plugin": "3.0.1", - "file-loader": "1.1.4", - "font-awesome": "4.4.0", - "glob": "5.0.13", - "glob-all": "3.0.1", - "good-squeeze": "2.1.0", - "h2o2": "5.1.1", - "handlebars": "4.0.5", - "hapi": "14.2.0", - "hjson": "3.1.0", - "http-proxy-agent": "1.0.0", - "https-proxy-agent": "2.1.1", - "inert": "4.0.2", - "jade": "1.11.0", - "jade-loader": "0.8.0", - "joi": "10.4.1", - "jquery": "^3.3.1", - "js-yaml": "3.4.1", - "json-stringify-pretty-compact": "1.0.4", - "json-stringify-safe": "5.0.1", - "jstimezonedetect": "1.0.5", - "leaflet": "1.0.3", - "leaflet-draw": "0.4.10", - "leaflet-responsive-popup": "0.2.0", - "leaflet-vega": "^0.8.6", - "leaflet.heat": "0.2.0", - "less": "2.7.1", - "less-loader": "4.0.5", - "lodash": "3.10.1", - "lru-cache": "4.1.1", - "markdown-it": "8.3.2", - "minimatch": "2.0.10", - "mkdirp": "0.5.1", - "moment": "^2.20.1", - "moment-timezone": "^0.5.14", - "mustache": "2.3.0", - "ngreact": "0.5.1", - "no-ui-slider": "1.2.0", - "node-fetch": "1.3.2", - "pegjs": "0.9.0", - "postcss-loader": "2.0.6", - "prop-types": "15.5.8", - "proxy-from-env": "1.0.0", - "querystring-browser": "1.0.4", - "raw-loader": "0.5.1", - "react": "^16.2.0", - "react-addons-shallow-compare": "15.6.2", - "react-anything-sortable": "^1.7.4", - "react-color": "^2.13.8", - "react-dom": "^16.2.0", - "react-grid-layout": "^0.16.2", - "react-input-range": "^1.3.0", - "react-markdown": "^3.1.4", - "react-redux": "^5.0.6", - "react-router-dom": "4.2.2", - "react-select": "^1.2.0", - "react-sizeme": "^2.3.6", - "react-toggle": "4.0.2", - "reactcss": "1.2.3", - "redux": "3.7.2", - "redux-actions": "2.2.1", - "redux-thunk": "2.2.0", - "regression": "2.0.0", - "request": "^2.85.0", - "reselect": "^3.0.1", - "resize-observer-polyfill": "1.2.1", - "rimraf": "2.4.3", - "rison-node": "1.0.0", - "rxjs": "5.4.3", - "script-loader": "0.7.2", - "semver": "5.1.0", - "style-loader": "0.19.0", - "tar": "2.2.0", - "tinygradient": "0.3.0", - "tinymath": "0.2.1", - "topojson-client": "3.0.0", - "trunc-html": "1.0.2", - "trunc-text": "1.0.2", - "uglifyjs-webpack-plugin": "0.4.6", - "ui-select": "0.19.6", - "url-loader": "0.5.9", - "uuid": "3.0.1", - "validate-npm-package-name": "2.2.2", - "vega-lib": "^3.3.1", - "vega-lite": "^2.4.0", - "vega-tooltip": "^0.9.14", - "vega-schema-url-parser": "1.0.0", - "vision": "4.1.0", - "webpack": "3.6.0", - "webpack-merge": "4.1.0", - "whatwg-fetch": "^2.0.3", - "wreck": "12.4.0", - "x-pack": "link:x-pack", - "yauzl": "2.7.0" + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1", + "lifecycle": { + "name": "my_policy" + } + } + } }, "newFullIndexTemplate": { - "@elastic/eui": "v0.0.44", - "@elastic/filesaver": "1.1.2", - "@elastic/numeral": "2.3.2", - "@elastic/ui-ace": "0.2.3", - "@kbn/babel-preset": "link:packages/kbn-babel-preset", - "@kbn/datemath": "link:packages/kbn-datemath", - "@kbn/pm": "link:packages/kbn-pm", - "@kbn/test-subj-selector": "link:packages/kbn-test-subj-selector", - "@kbn/ui-framework": "link:packages/kbn-ui-framework", - "JSONStream": "1.1.1", - "accept-language-parser": "1.2.0", - "angular": "1.6.9", - "angular-aria": "1.6.6", - "angular-elastic": "2.5.0", - "angular-recursion": "^1.0.5", - "angular-route": "1.4.7", - "angular-sanitize": "1.5.7", - "angular-sortable-view": "0.0.15", - "autoprefixer": "6.5.4", - "babel-core": "6.21.0", - "babel-loader": "7.1.2", - "babel-polyfill": "6.20.0", - "babel-register": "6.18.0", - "bluebird": "2.9.34", - "body-parser": "1.12.0", - "boom": "5.2.0", - "brace": "0.10.0", - "bunyan": "1.7.1", - "cache-loader": "1.0.3", - "chalk": "2.3.0", - "check-hash": "1.0.1", - "color": "1.0.3", - "commander": "2.8.1", - "compare-versions": "3.1.0", - "css-loader": "0.28.7", - "custom-event-polyfill": "^0.3.0", - "d3": "3.5.6", - "d3-cloud": "1.2.1", - "dragula": "3.7.0", - "elasticsearch": "^14.2.1", - "elasticsearch-browser": "^14.2.1", - "encode-uri-query": "1.0.0", - "even-better": "7.0.2", - "expiry-js": "0.1.7", - "extract-text-webpack-plugin": "3.0.1", - "file-loader": "1.1.4", - "font-awesome": "4.4.0", - "glob": "5.0.13", - "glob-all": "3.0.1", - "good-squeeze": "2.1.0", - "h2o2": "5.1.1", - "handlebars": "4.0.5", - "hapi": "14.2.0", - "hjson": "3.1.0", - "http-proxy-agent": "1.0.0", - "https-proxy-agent": "2.1.1", - "inert": "4.0.2", - "jade": "1.11.0", - "jade-loader": "0.8.0", - "joi": "10.4.1", - "jquery": "^3.3.1", - "js-yaml": "3.4.1", - "json-stringify-pretty-compact": "1.0.4", - "json-stringify-safe": "5.0.1", - "jstimezonedetect": "1.0.5", - "leaflet": "1.0.3", - "leaflet-draw": "0.4.10", - "leaflet-responsive-popup": "0.2.0", - "leaflet-vega": "^0.8.6", - "leaflet.heat": "0.2.0", - "less": "2.7.1", - "less-loader": "4.0.5", - "lodash": "3.10.1", - "lru-cache": "4.1.1", - "markdown-it": "8.3.2", - "minimatch": "2.0.10", - "mkdirp": "0.5.1", - "moment": "^2.20.1", - "moment-timezone": "^0.5.14", - "mustache": "2.3.0", - "ngreact": "0.5.1", - "no-ui-slider": "1.2.0", - "node-fetch": "1.3.2", - "pegjs": "0.9.0", - "postcss-loader": "2.0.6", - "prop-types": "15.5.8", - "proxy-from-env": "1.0.0", - "querystring-browser": "1.0.4", - "raw-loader": "0.5.1", - "react": "^16.3.0", - "react-addons-shallow-compare": "15.6.2", - "react-anything-sortable": "^1.7.4", - "react-color": "^2.13.8", - "react-dom": "^16.3.0", - "react-grid-layout": "^0.16.2", - "react-input-range": "^1.3.0", - "react-markdown": "^3.1.4", - "react-redux": "^5.0.6", - "react-router-dom": "4.2.2", - "react-select": "^1.2.0", - "react-sizeme": "^2.3.6", - "react-toggle": "4.0.2", - "reactcss": "1.2.3", - "redux": "3.7.2", - "redux-actions": "2.2.1", - "redux-thunk": "2.2.0", - "regression": "2.0.0", - "request": "^2.85.0", - "reselect": "^3.0.1", - "resize-observer-polyfill": "1.2.1", - "rimraf": "2.4.3", - "rison-node": "1.0.0", - "rxjs": "5.4.3", - "script-loader": "0.7.2", - "semver": "5.1.0", - "style-loader": "0.19.0", - "tar": "2.2.0", - "tinygradient": "0.3.0", - "tinymath": "0.2.1", - "topojson-client": "3.0.0", - "trunc-html": "1.0.2", - "trunc-text": "1.0.2", - "uglifyjs-webpack-plugin": "0.4.6", - "ui-select": "0.19.6", - "url-loader": "0.5.9", - "uuid": "3.0.2", - "validate-npm-package-name": "2.2.2", - "vega-lib": "^3.3.1", - "vega-lite": "^2.4.0", - "vega-tooltip": "^0.9.14", - "vega-schema-url-parser": "1.0.0", - "vision": "4.1.0", - "webpack": "3.6.0", - "webpack-merge": "4.1.0", - "whatwg-fetch": "^2.0.3", - "wreck": "12.4.0", - "x-pack": "link:x-pack", - "yauzl": "2.7.0", - "zzz": "1.0.0" + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1", + "number_of_replicas": "1", + "lifecycle": { + "name": "my_policy4" + }, + "routing": { + "allocation": { + "include": { + "sattr_name": "node" + } + } + } + } + } }, "hasChanged": true } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index 9a73050b124ff..63d6a7c00a83e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { toastNotifications } from 'ui/notify'; import { IndexTemplate } from './components/index_template'; -import { PolicySelection } from './components/policy_selection'; +// import { PolicySelection } from './components/policy_selection'; import { PolicyConfiguration } from './components/policy_configuration'; import { Review } from './components/review'; import { @@ -23,6 +23,7 @@ import { bootstrap } from '../../api'; import { STRUCTURE_INDEX_TEMPLATE, STRUCTURE_POLICY_CONFIGURATION, + STRUCTURE_REVIEW, } from '../../store/constants'; // import { DiffView } from './components/review/diff_view'; // import diff from './diff.json'; @@ -99,27 +100,29 @@ export class Wizard extends Component { done={() => this.onSelectedStepChanged(2)} /> ); + // case 2: + // return ( + // this.onSelectedStepChanged(3)} + // back={() => this.onSelectedStepChanged(1)} + // /> + // ); case 2: - return ( - this.onSelectedStepChanged(3)} - back={() => this.onSelectedStepChanged(1)} - /> - ); - case 3: return ( this.onSelectedStepChanged(4)} - back={() => this.onSelectedStepChanged(2)} + done={() => this.onSelectedStepChanged(3)} + back={() => this.onSelectedStepChanged(1)} /> ); - case 4: + case 3: return ( this.onSelectedStepChanged(3)} + errors={errors[STRUCTURE_REVIEW]} + back={() => this.onSelectedStepChanged(2)} /> ); } @@ -133,27 +136,27 @@ export class Wizard extends Component { isComplete: this.state.selectedStep > 1, onClick: () => this.onSelectedStepChanged(1), }, + // { + // title: 'Select or create policy', + // isSelected: this.state.selectedStep === 2, + // isComplete: this.state.selectedStep > 2, + // disabled: this.state.selectedStep < 2, + // onClick: () => this.onSelectedStepChanged(2), + // }, { - title: 'Select or create policy', + title: 'Configure policy', isSelected: this.state.selectedStep === 2, isComplete: this.state.selectedStep > 2, disabled: this.state.selectedStep < 2, onClick: () => this.onSelectedStepChanged(2), }, { - title: 'Configure policy', + title: 'Review and save', isSelected: this.state.selectedStep === 3, isComplete: this.state.selectedStep > 3, disabled: this.state.selectedStep < 3, onClick: () => this.onSelectedStepChanged(3), }, - { - title: 'Review and save', - isSelected: this.state.selectedStep === 4, - isComplete: this.state.selectedStep > 4, - disabled: this.state.selectedStep < 4, - onClick: () => this.onSelectedStepChanged(4), - }, ]; // const templateDiff = diff; @@ -171,7 +174,7 @@ export class Wizard extends Component { {/* */} {this.renderContent()} diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js index 7b6e7076d87c5..04c59709bd426 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/constants.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -29,6 +29,8 @@ export const PHASE_ROLLOVER_AFTER_UNITS = 'selectedAfterUnits'; export const PHASE_FORCE_MERGE_SEGMENTS = 'selectedForceMergeSegments'; export const PHASE_FORCE_MERGE_ENABLED = 'forceMergeEnabled'; +export const PHASE_SHRINK_ENABLED = 'shrinkEnabled'; + export const PHASE_NODE_ATTRS = 'selectedNodeAttrs'; export const PHASE_PRIMARY_SHARD_COUNT = 'selectedPrimaryShardCount'; export const PHASE_REPLICA_COUNT = 'selectedReplicaCount'; @@ -52,6 +54,8 @@ export const STRUCTURE_PRIMARY_NODES = 'primary_nodes'; export const STRUCTURE_REPLICAS = 'replicas'; export const STRUCTURE_POLICY_CONFIGURATION = 'policyConfiguration'; + +export const STRUCTURE_REVIEW = 'review'; export const STRUCTURE_POLICY_NAME = 'policyName'; export const STRUCTURE_INDEX_NAME = 'indexName'; export const STRUCTURE_ALIAS_NAME = 'aliasName'; @@ -59,7 +63,9 @@ export const STRUCTURE_ALIAS_NAME = 'aliasName'; export const ERROR_STRUCTURE = { [STRUCTURE_INDEX_TEMPLATE]: { [STRUCTURE_TEMPLATE_SELECTION]: { - [STRUCTURE_TEMPLATE_NAME]: [] + [STRUCTURE_TEMPLATE_NAME]: [], + [STRUCTURE_INDEX_NAME]: [], + [STRUCTURE_ALIAS_NAME]: [] }, [STRUCTURE_CONFIGURATION]: { [STRUCTURE_NODE_ATTRS]: [], @@ -68,9 +74,6 @@ export const ERROR_STRUCTURE = { } }, [STRUCTURE_POLICY_CONFIGURATION]: { - [STRUCTURE_POLICY_NAME]: [], - [STRUCTURE_INDEX_NAME]: [], - [STRUCTURE_ALIAS_NAME]: [], [PHASE_HOT]: { [PHASE_ROLLOVER_ALIAS]: [], [PHASE_ROLLOVER_MAX_AGE]: [], @@ -100,5 +103,8 @@ export const ERROR_STRUCTURE = { [PHASE_ROLLOVER_AFTER]: [], [PHASE_ROLLOVER_AFTER_UNITS]: [], }, + }, + [STRUCTURE_REVIEW]: { + [STRUCTURE_POLICY_NAME]: [], } }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 8b68c8a5d1ceb..7a44e98db113b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -36,7 +36,8 @@ import { PHASE_ROLLOVER_MAX_SIZE_STORED, PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, PHASE_ROLLOVER_ALIAS, - PHASE_ROLLOVER_MAX_DOC_SIZE + PHASE_ROLLOVER_MAX_DOC_SIZE, + PHASE_SHRINK_ENABLED } from '../constants'; export const defaultWarmPhase = { @@ -48,6 +49,7 @@ export const defaultWarmPhase = { [PHASE_ROLLOVER_AFTER]: '', [PHASE_ROLLOVER_AFTER_UNITS]: 's', [PHASE_NODE_ATTRS]: '', + [PHASE_SHRINK_ENABLED]: true, [PHASE_PRIMARY_SHARD_COUNT]: '', [PHASE_REPLICA_COUNT]: '' }; @@ -95,6 +97,7 @@ const defaultPolicy = { const defaultState = { isLoading: false, originalPolicyName: undefined, + selectedPolicySet: false, selectedPolicy: defaultPolicy, policies: [] }; @@ -113,12 +116,14 @@ export const policies = handleActions( return { ...state, selectedPolicy: defaultPolicy, + selectedPolicySet: true, }; } return { ...state, originalPolicyName: selectedPolicy.name, + selectedPolicySet: true, selectedPolicy: { ...defaultPolicy, ...policyFromES(selectedPolicy) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index 38947e3f475f1..c99fb716e4761 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -112,7 +112,23 @@ export const getAffectedIndexPatterns = state => { return indexPatterns; }; -// const hasJSONChanged = (json1, json2) => JSON.stringify(json1) !== JSON.stringify(json2); +export const getSelectedIndexTemplateIndices = state => { + const selectedIndexTemplate = getSelectedIndexTemplate(state); + if (selectedIndexTemplate) { + return selectedIndexTemplate.indices; + } + return undefined; +}; + +export const getExistingAllocationRules = state => { + const selectedIndexTemplate = getSelectedIndexTemplate(state); + if (selectedIndexTemplate) { + return selectedIndexTemplate.allocation_rules; + } + return undefined; +}; + +const hasJSONChanged = (json1, json2) => JSON.stringify(json1) !== JSON.stringify(json2); export const getTemplateDiff = state => { const originalFullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; const newFullIndexTemplate = merge(cloneDeep(originalFullIndexTemplate), { @@ -137,7 +153,7 @@ export const getTemplateDiff = state => { return { originalFullIndexTemplate, newFullIndexTemplate, - hasChanged: true//hasJSONChanged(originalFullIndexTemplate, newFullIndexTemplate), + hasChanged: hasJSONChanged(originalFullIndexTemplate, newFullIndexTemplate), }; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index 235ebb4dd2062..a34e94af5c6ab 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -4,9 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import { PHASE_HOT, PHASE_WARM, @@ -18,7 +15,6 @@ import { PHASE_ROLLOVER_MAX_SIZE_STORED, STRUCTURE_INDEX_TEMPLATE, STRUCTURE_CONFIGURATION, - STRUCTURE_NODE_ATTRS, STRUCTURE_PRIMARY_NODES, STRUCTURE_REPLICAS, STRUCTURE_TEMPLATE_SELECTION, @@ -28,7 +24,10 @@ import { STRUCTURE_INDEX_NAME, STRUCTURE_ALIAS_NAME, ERROR_STRUCTURE, - PHASE_ATTRIBUTES_THAT_ARE_NUMBERS + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_SHRINK_ENABLED, + STRUCTURE_REVIEW } from '../constants'; import { getPhase, @@ -37,7 +36,6 @@ import { getSelectedPolicyName, getSelectedIndexTemplateName, isNumber, - getSelectedNodeAttrs, getSelectedPrimaryShardCount, getSelectedReplicaCount, getSaveAsNewPolicy, @@ -69,12 +67,19 @@ export const validatePhase = (type, phase) => { } for (const numberedAttribute of PHASE_ATTRIBUTES_THAT_ARE_NUMBERS) { - if (phase.hasOwnProperty(numberedAttribute) && !phase[numberedAttribute] === '') { + if (phase.hasOwnProperty(numberedAttribute) && phase[numberedAttribute] !== '') { + // If shrink is disabled, there is no need to validate this + if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && !phase[PHASE_SHRINK_ENABLED]) { + continue; + } if (!isNumber(phase[numberedAttribute])) { - errors[numberedAttribute].push('A number is required.'); + errors[numberedAttribute] = ['A number is required.']; } else if (phase[numberedAttribute] < 0) { - errors[numberedAttribute].push('Only positive numbers allowed.'); + errors[numberedAttribute] = ['Only positive numbers allowed.']; + } + else if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && phase[numberedAttribute] < 1) { + errors[numberedAttribute] = ['Only positive numbers above 0 are allowed.']; } } } @@ -89,13 +94,15 @@ export const validateLifecycle = state => { if (!getSelectedIndexTemplateName(state)) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][ STRUCTURE_TEMPLATE_NAME - ].push('An index template is required.'); + ].push('An index template is required'); } - if (!getSelectedNodeAttrs(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ - STRUCTURE_NODE_ATTRS - ].push('A node must be selected.'); + if (getBootstrapEnabled(state) && !getIndexName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_INDEX_NAME].push('An index name is required.'); + } + + if (getBootstrapEnabled(state) && !getAliasName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('An alias name is required.'); } if (!isNumber(getSelectedPrimaryShardCount(state))) { @@ -103,10 +110,10 @@ export const validateLifecycle = state => { STRUCTURE_PRIMARY_NODES ].push('A value is required.'); } - else if (getSelectedPrimaryShardCount(state) < 0) { + else if (getSelectedPrimaryShardCount(state) < 1) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ STRUCTURE_PRIMARY_NODES - ].push('Only positive numbers allowed.'); + ].push('Only positive numbers above 0 are allowed.'); } if (!isNumber(getSelectedReplicaCount(state))) { @@ -121,11 +128,11 @@ export const validateLifecycle = state => { } if (!getSelectedPolicyName(state)) { - errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('A policy name is required.'); + errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('A policy name is required.'); } if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { - errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('The policy name must be different.'); + errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('The policy name must be different.'); } // if (getSaveAsNewPolicy(state)) { @@ -135,14 +142,6 @@ export const validateLifecycle = state => { // } // } - if (getBootstrapEnabled(state) && !getIndexName(state)) { - errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_INDEX_NAME].push('An index name is required.'); - } - - if (getBootstrapEnabled(state) && !getAliasName(state)) { - errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_ALIAS_NAME].push('An alias name is required.'); - } - errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT], ...validatePhase(PHASE_HOT, getPhase(state, PHASE_HOT)) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index f0818f043ca94..94439f27c72a2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -39,6 +39,7 @@ import { getIndexTemplates } from '.'; export const getPolicies = state => state.policies.policies; export const getSelectedPolicy = state => state.policies.selectedPolicy; +export const getIsSelectedPolicySet = state => state.policies.selectedPolicySet; export const getSelectedOriginalPolicyName = state => state.policies.originalPolicyName; export const getSaveAsNewPolicy = state => diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js index 73541518350f7..0cb74ac6d2599 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -38,7 +38,7 @@ async function getAffectedIndices( template.settings && template.settings.index && template.settings.index.lifecycle && - template.settings.index.lifecycle.name === policyName + (policyName && template.settings.index.lifecycle.name === policyName) ) { accum.push(...template.index_patterns); } @@ -64,6 +64,32 @@ export function registerGetAffectedRoute(server) { const isEsError = isEsErrorFactory(server); const licensePreRouting = licensePreRoutingFactory(server); + server.route({ + path: + '/api/index_lifecycle_management/indices/affected/{indexTemplateName}', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await getAffectedIndices( + callWithRequest, + request.params.indexTemplateName, + ); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [licensePreRouting] + } + }); + server.route({ path: '/api/index_lifecycle_management/indices/affected/{indexTemplateName}/{policyName}', diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js index 37d15cf60a477..7bc4922478be0 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js @@ -20,6 +20,7 @@ async function formatTemplates(templates, callWithRequest) { const formattedTemplate = { index_lifecycle_name: settings.index && settings.index.lifecycle ? settings.index.lifecycle.name : undefined, index_patterns, + allocation_rules: settings.index && settings.index.routing ? settings.index.routing : undefined, name: templateName, }; From 4851068c49264d93732277c5172b750ce0695054 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 14 May 2018 11:58:48 -0400 Subject: [PATCH 009/102] Add logic to auto enable a phase when something is set --- .../store/middleware/auto_enable_phase.js | 29 +++++++++++++++++++ .../public/store/middleware/index.js | 7 +++++ .../public/store/reducers/policies.js | 2 +- .../public/store/store.js | 3 +- 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js new file mode 100644 index 0000000000000..738be1fd7a56d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js @@ -0,0 +1,29 @@ +/* + * 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 { + setPhaseData +} from '../actions'; +import { getPhaseData } from '../selectors'; +import { PHASE_ENABLED } from '../constants'; + +const setsPerPhase = {}; + +export const autoEnablePhase = store => next => action => { + const state = store.getState(); + + if (action.type === setPhaseData().type) { + const { phase } = action.payload; + setsPerPhase[phase] = setsPerPhase[phase] || 0; + setsPerPhase[phase]++; + + if (setsPerPhase[phase] === 1 && !getPhaseData(state, phase, PHASE_ENABLED)) { + store.dispatch(setPhaseData(phase, PHASE_ENABLED, true)); + } + } + + return next(action); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js new file mode 100644 index 0000000000000..ccad9cac1daa4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js @@ -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 { autoEnablePhase } from './auto_enable_phase'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 7a44e98db113b..91e6e255e0859 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -83,7 +83,7 @@ export const defaultDeletePhase = { [PHASE_ROLLOVER_AFTER_UNITS]: 's' }; -const defaultPolicy = { +export const defaultPolicy = { name: '', saveAsNew: true, phases: { diff --git a/x-pack/plugins/index_lifecycle_management/public/store/store.js b/x-pack/plugins/index_lifecycle_management/public/store/store.js index 3566efa9fff10..355a834188f3f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/store.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/store.js @@ -8,9 +8,10 @@ import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; import { indexLifecycleManagement } from './reducers/'; +import { autoEnablePhase } from './middleware'; export const indexLifecycleManagementStore = (initialState = {}) => { - const enhancers = [ applyMiddleware(thunk) ]; + const enhancers = [ applyMiddleware(thunk, autoEnablePhase) ]; window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); return createStore( From 5ca7b4f30ebc044e1665996ef74d90a36f65717b Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Wed, 16 May 2018 14:14:46 -0700 Subject: [PATCH 010/102] redesign ilm --- package.json | 2 +- x-pack/package.json | 2 +- .../public/less/main.less | 49 ++ .../public/main.html | 2 +- .../components/configuration/configuration.js | 180 ++++--- .../template_selection/template_selection.js | 29 +- .../index_template/index_template.js | 28 +- .../node_attrs_details/node_attrs_details.js | 75 +-- .../components/cold_phase/cold_phase.js | 279 ++++++----- .../components/delete_phase/delete_phase.js | 179 +++---- .../components/hot_phase/hot_phase.js | 277 +++++----- .../components/warm_phase/warm_phase.js | 473 +++++++++--------- .../policy_configuration.js | 13 +- .../policy_selection/policy_selection.js | 63 ++- .../wizard/components/review/diff_view.js | 42 +- .../wizard/components/review/review.js | 219 ++++---- .../sections/wizard/wizard.container.js | 1 + .../public/sections/wizard/wizard.js | 29 +- .../index_management/public/styles/table.less | 2 +- x-pack/yarn.lock | 468 +---------------- yarn.lock | 355 +++---------- 21 files changed, 1094 insertions(+), 1673 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/less/main.less diff --git a/package.json b/package.json index 528fea567f10b..2eda9cb138496 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "url": "https://github.com/elastic/kibana.git" }, "dependencies": { - "@elastic/eui": "v0.0.44", + "@elastic/eui": "v0.0.47", "@elastic/filesaver": "1.1.2", "@elastic/numeral": "2.3.2", "@elastic/ui-ace": "0.2.3", diff --git a/x-pack/package.json b/x-pack/package.json index 78b191aca3de9..55ed85b5adbb8 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -75,7 +75,7 @@ "yargs": "4.7.1" }, "dependencies": { - "@elastic/eui": "0.0.44", + "@elastic/eui": "0.0.47", "@elastic/node-crypto": "0.1.2", "@elastic/node-phantom-simple": "2.2.4", "@elastic/numeral": "2.3.2", diff --git a/x-pack/plugins/index_lifecycle_management/public/less/main.less b/x-pack/plugins/index_lifecycle_management/public/less/main.less new file mode 100644 index 0000000000000..392512d93d0c3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/less/main.less @@ -0,0 +1,49 @@ +@import (reference) "~ui/styles/variables"; + +#indexLifecycleManagementReactRoot { + background: @globalColorLightestGray; + min-height: 100vh; +} + +.euiPageContent.ilmContent { + max-width: 1000px; + width: 100%; +} + +.ilmHrule { + // Less has a bug with calcs + width: calc(~"100% + 48px") !important; + margin-left: -24px; + margin-right: -24px; +} + +.ilmAlias { + display: inline-block; + background-color: #333; + color: white; + padding: 4px 8px; +} + +.ilmDiff__nav { + padding: 16px; + background: #f5f5f5; +} + +.ilmDiff__code { + +} + +.euiAnimateContentLoad { + animation: euiAnimContentLoad $euiAnimSpeedExtraSlow $euiAnimSlightResistance; +} + +@keyframes euiAnimContentLoad { + 0% { + opacity: 0; + transform: translateY(16px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/main.html b/x-pack/plugins/index_lifecycle_management/public/main.html index 1d1d0036e7fc1..36820f8e159ac 100644 --- a/x-pack/plugins/index_lifecycle_management/public/main.html +++ b/x-pack/plugins/index_lifecycle_management/public/main.html @@ -1,3 +1,3 @@ -
+
diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index 922e593529283..99a5899dd5efe 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -4,21 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, - EuiTitle, EuiSpacer, EuiSelect, EuiFieldNumber, - EuiHorizontalRule, EuiCallOut, EuiButtonEmpty, - EuiIconTip, EuiLink, + EuiDescribedFormGroup, } from '@elastic/eui'; import { STRUCTURE_NODE_ATTRS, @@ -93,97 +91,95 @@ export class Configuration extends Component { return (
- - -

Configure options

-
- - - Where do you want your hot indices to live -   - - - } - errorKey={STRUCTURE_NODE_ATTRS} - isShowingErrors={isShowingErrors} - errors={errors} - helpText={selectedNodeAttrs ? ( - - this.setState({ isShowingNodeDetailsFlyout: true }) - } - > - View node details - - ) : null} + Configuration} + titleSize="s" + description="Indices are thought of as "hot" when they are actively being written to." + fullWidth > - { - await setSelectedNodeAttrs(e.target.value); - validate(); - }} - options={nodeOptions} - /> - - -

- The best way to determine how many shards you need is to benchmark - using realistic data and queries on your hardware.{' '} - - Learn more. - -

-
- - - - - { - await setSelectedPrimaryShardCount(e.target.value); - validate(); - }} - value={selectedPrimaryShardCount} - /> - - - - - { - await setSelectedReplicaCount(e.target.value); - validate(); - }} - value={selectedReplicaCount} - /> - - - - {this.state.isShowingNodeDetailsFlyout ? ( - this.setState({ isShowingNodeDetailsFlyout: false })} + + this.setState({ isShowingNodeDetailsFlyout: true }) + } + > + View a list of nodes attached to this configuration + + ) : null} + > + { + await setSelectedNodeAttrs(e.target.value); + validate(); + }} + options={nodeOptions} + /> + + + The best way to determine how many shards you need is to benchmark + using realistic data and queries on your hardware.{' '} + + Learn more in our docs + . +

+ } /> - ) : null} + + + + + { + await setSelectedPrimaryShardCount(e.target.value); + validate(); + }} + value={selectedPrimaryShardCount} + /> + + + + + { + await setSelectedReplicaCount(e.target.value); + validate(); + }} + value={selectedReplicaCount} + /> + + + + {this.state.isShowingNodeDetailsFlyout ? ( + this.setState({ isShowingNodeDetailsFlyout: false })} + /> + ) : null} - {primaryNodeErrors} + {primaryNodeErrors} +
); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index a158b406762d5..3d5777711a555 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -7,7 +7,15 @@ import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { EuiSelect, EuiFormRow, EuiSwitch, EuiFieldText } from '@elastic/eui'; +import { + EuiSelect, + EuiFormRow, + EuiSwitch, + EuiFieldText, + EuiDescribedFormGroup, + EuiLink, +} from '@elastic/eui'; + import { ErrableFormRow } from '../../../../form_errors'; import { STRUCTURE_TEMPLATE_NAME, @@ -50,9 +58,22 @@ export class TemplateSelection extends PureComponent { } = this.props; return ( - + Select a template} + fullWidth + titleSize="s" + description={ +

+ An index template defines the settings, mappings, and aliases to apply + when you create an index.{' '} + + Learn more + . +

+ } + > ) : null} -
+ ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js index b80cbaf77d919..87b620531bf44 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { toastNotifications } from 'ui/notify'; @@ -12,12 +12,8 @@ import { TemplateSelection } from './components/template_selection'; import { Configuration } from './components/configuration'; import { - EuiSpacer, - EuiTitle, EuiHorizontalRule, EuiButton, - EuiTextColor, - EuiLink, } from '@elastic/eui'; import { hasErrors } from '../../../../lib/find_errors'; import { @@ -60,25 +56,6 @@ export class IndexTemplate extends Component { return (
- -

Select a template

-
- - - - -
- An index template defines the settings, mappings, and aliases to apply - when you create an index. -
-
- - - Learn more - -
-
- + - Next + Continue
); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js index 6bb03853e8f31..7cb2977e706ab 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js @@ -16,6 +16,7 @@ import { EuiSpacer, EuiButtonEmpty, EuiCallOut, + EuiPortal, } from '@elastic/eui'; export class NodeAttrsDetails extends PureComponent { @@ -36,42 +37,44 @@ export class NodeAttrsDetails extends PureComponent { const { selectedNodeAttrs, allocationRules, details, close } = this.props; return ( - - - -

Nodes that contain the attribute: `{selectedNodeAttrs}`

-
- - {allocationRules ? ( - - - Be aware that this index template has existing allocation rules - which will affect the list of nodes these indices can be allocated to. - - - - ) : null} - -
- - - Close - - -
+ + + + +

Nodes that contain the attribute: `{selectedNodeAttrs}`

+
+ + {allocationRules ? ( + + + Be aware that this index template has existing allocation rules + which will affect the list of nodes these indices can be allocated to. + + + + ) : null} + +
+ + + Close + + +
+
); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 8de43b1f2b4da..13f73ce68b5a7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -4,23 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { PureComponent } from 'react'; +import React, { PureComponent, Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, - EuiIcon, - EuiTitle, EuiSpacer, EuiText, EuiTextColor, - EuiAccordion, EuiFormRow, EuiFieldNumber, EuiSelect, - EuiSwitch, EuiButtonEmpty, + EuiDescribedFormGroup, + EuiBetaBadge, + EuiButton, } from '@elastic/eui'; import { PHASE_ENABLED, @@ -78,153 +77,155 @@ export class ColdPhase extends PureComponent { } = this.props; return ( - - -
- -
-
- - -

Cold phase

-
- + + Cold phase{' '} + {phaseData[PHASE_ENABLED] ? ( + + ) : null} +
+ } + titleSize="s" + description={ + +

+ Your read-only index is queried less frequently. Use this phase + when the index no longer needs to be on the most performant hardware. +

+ {isShowingErrors ? ( + -

- This phase is optional. Your read-only index is queried less frequently. - Use this phase when the index no longer needs to be on the most performant hardware. -

+

This phase contains errors that need to be fixed.

- {isShowingErrors ? ( - - -

This phase contains errors that need to be fixed.

-
-
- ) : null} - - - } - buttonClassName="ilmAccordion__button" - buttonContentClassName="ilmAccordion__buttonContent" - extraAction={ - setPhaseData(PHASE_ENABLED, e.target.checked)} - label="Enable this phase" - /> + ) : null} +
} + fullWidth > -
- -

Configuration

-
- - - - + +
+ + { + await setPhaseData(PHASE_ENABLED, false); + validate(); + }} > - { - setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); - validate(); - }} - /> - - - - - - setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) - } - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } - ]} - /> - - - + Deactive cold phase + +
- + + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} - > - View node details - - ) : null} - > - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - + - - - - { - await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); - validate(); - }} - /> - - - - + - setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) - } + iconType="eye" + onClick={() => showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > - Use number in warm phase + View a list of nodes attached to this configuration - - - -
- + ) : null} + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + validate(); + }} + /> + + + + + + setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) + } + > + Set same as warm phase + + + + + + ) : ( +
+ + { + await setPhaseData(PHASE_ENABLED, true); + validate(); + }} + > + Activate cold phase + +
+ )} + ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index ed28165c9ccc5..7a8a00301023e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -7,22 +7,22 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, - EuiIcon, EuiTitle, EuiSpacer, EuiText, EuiTextColor, - EuiAccordion, EuiFormRow, EuiFieldNumber, EuiSelect, - EuiSwitch, + EuiDescribedFormGroup, + EuiBetaBadge, + EuiButton, } from '@elastic/eui'; import { PHASE_ENABLED, @@ -60,95 +60,100 @@ export class DeletePhase extends PureComponent { } = this.props; return ( - - -
- -
-
- - -

Delete phase

-
- + + Delete phase{' '} + {phaseData[PHASE_ENABLED] ? ( + + ) : null} +
+ } + titleSize="s" + description={ + +

+ When your data is no longer useful. Define how long you want to retain it. +

+ {isShowingErrors ? ( + -

- This phase is optional. Your data is no longer useful. Define how long you want to retain it. -

+

This phase contains errors that need to be fixed.

- {isShowingErrors ? ( - - -

This phase contains errors that need to be fixed.

-
-
- ) : null} -
-
- } - buttonClassName="ilmAccordion__button" - buttonContentClassName="ilmAccordion__buttonContent" - extraAction={ - setPhaseData(PHASE_ENABLED, e.target.checked)} - label="Enable this phase" - /> + ) : null} + } + fullWidth > -
- -

Configuration

-
- - - - + +
+ + { + await setPhaseData(PHASE_ENABLED, false); + validate(); + }} > - { - setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); - validate(); - }} - /> - - - - - - setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) - } - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } - ]} - /> - - - -
- + Deactive cold phase + +
+ + + +

Configuration

+
+ + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + + + ) : ( +
+ + { + await setPhaseData(PHASE_ENABLED, true); + validate(); + }} + > + Activate delete phase + +
+ )} + ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index ca35b917b0e7c..9511cdac48294 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -10,16 +10,16 @@ import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, - EuiIcon, - EuiTitle, EuiSpacer, EuiText, EuiTextColor, - EuiAccordion, EuiFieldNumber, EuiSelect, EuiSwitch, - EuiLink + EuiLink, + EuiFormRow, + EuiDescribedFormGroup, + EuiBetaBadge, } from '@elastic/eui'; import { PHASE_ROLLOVER_ALIAS, @@ -66,52 +66,41 @@ export class HotPhase extends PureComponent { } = this.props; return ( - - -
- -
-
- - -

Hot phase

-
- + + Hot phase{' '} + + + } + titleSize="s" + description={ + +

+ This phase is required. Your index is being queried and in active writing mode. +

+ {isShowingErrors ? ( + -

This phase is required. Your index is being queried and in active writing mode.

+

This phase contains errors that need to be fixed.

- {isShowingErrors ? ( - - -

This phase contains errors that need to be fixed.

-
-
- ) : null} -
- + ) : null} + } - buttonClassName="ilmAccordion__button" - buttonContentClassName="ilmAccordion__buttonContent" + fullWidth > -
- -

Rollover condition

-
- + + Setting this to true will rollover the index when it gets too big or too old. The alias will switch to the new index.{' '} + + Learn more. + +

+ } + > { @@ -120,111 +109,99 @@ export class HotPhase extends PureComponent { }} label="Enable rollover" /> - - -

- - Rollover the index when it gets too big or too old. The alias will switch to the new index. - -   - - Learn more. - -

-
- {phaseData[PHASE_ROLLOVER_ENABLED] ? ( - - - - - - { - await setPhaseData( - PHASE_ROLLOVER_MAX_SIZE_STORED, - e.target.value - ); - validate(); - }} - /> - - - - - { - await setPhaseData( - PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, - e.target.value - ); - validate(); - }} - options={[ - { value: 'gb', text: 'gigabytes' }, - { value: MAX_SIZE_TYPE_DOCUMENT, text: 'documents' } - ]} - /> - - - - - - - - { - await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); - validate(); - }} - /> - - - - - { - await setPhaseData( - PHASE_ROLLOVER_MAX_AGE_UNITS, - e.target.value - ); - validate(); - }} - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } - ]} - /> - - - - - ) : null} -
-
+ + {phaseData[PHASE_ROLLOVER_ENABLED] ? ( + + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED, + e.target.value + ); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'gb', text: 'gigabytes' }, + { value: MAX_SIZE_TYPE_DOCUMENT, text: 'documents' } + ]} + /> + + + + + + + + { + await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_AGE_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' } + ]} + /> + + + + + ) : null} + ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index a2a27fd52c639..c627b96bd2a0a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -10,18 +10,19 @@ import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, - EuiIcon, EuiTitle, EuiSpacer, EuiText, EuiTextColor, - EuiAccordion, EuiFormRow, EuiFieldNumber, EuiSelect, EuiSwitch, EuiButtonEmpty, EuiLink, + EuiDescribedFormGroup, + EuiBetaBadge, + EuiButton, } from '@elastic/eui'; import { PHASE_ENABLED, @@ -110,227 +111,145 @@ export class WarmPhase extends Component { } = this.props; return ( - - -
- -
-
- - -

Warm phase

-
- + + Warm phase{' '} + {phaseData[PHASE_ENABLED] ? ( + + ) : null} + + } + titleSize="s" + description={ + +

+ Your index is frequently queried, but is read-only. + Use this phase to optimize for search.Your index is being queried + and in active writing mode. +

+ {isShowingErrors ? ( + -

- This phase is optional. Your index is frequently queried, - but is read-only. Use this phase to optimize for search. -

+

This phase contains errors that need to be fixed.

- {isShowingErrors ? ( - - -

This phase contains errors that need to be fixed.

-
-
- ) : null} -
- - } - buttonClassName="ilmAccordion__button" - buttonContentClassName="ilmAccordion__buttonContent" - extraAction={ - { - await setPhaseData(PHASE_ENABLED, e.target.checked); - validate(); - }} - label="Enable this phase" - /> + ) : null} + } + fullWidth > -
- -

Configuration

-
- - {hotPhaseRolloverEnabled ? ( - - - { - await this.setState({ applyOnRollover: e.target.checked }); - validate(); - }} - /> - - - ) : null} - {!this.state.applyOnRollover ? ( - - - - { - setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + + + + {phaseData[PHASE_ENABLED] ? ( + + +
+ { + await setPhaseData(PHASE_ENABLED, false); validate(); }} - /> - - - - - + Remove warm phase + +
+
+ {hotPhaseRolloverEnabled ? ( + + { - await setPhaseData( - PHASE_ROLLOVER_AFTER_UNITS, - e.target.value - ); + await this.setState({ applyOnRollover: e.target.checked }); validate(); }} - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' }, - ]} /> -
-
- ) : null} - - + ) : null} + {!this.state.applyOnRollover ? ( + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_AFTER_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + ]} + /> + + + + ) : null} - - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS]) - } - > - View node details - - ) : null - } - > - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - + - - + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS]) + } + > + View node details + + ) : null + } > - { - await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); validate(); }} /> - - - - { - await setPhaseData( - PHASE_REPLICA_COUNT, - hotPhaseReplicaCount - ); - validate(); - }} - > - Use number in hot phase - - - - - - - - -

Shrink

-
- - - Shrink the index into a new index with fewer primary shards.{' '} - - Learn more. - - - - - - { - await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); - validate(); - }} - label="Enable shrink" - /> - - - - {phaseData[PHASE_SHRINK_ENABLED] ? ( - - + { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - e.target.value - ); + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); validate(); }} /> @@ -342,69 +261,151 @@ export class WarmPhase extends Component { flush="left" onClick={async () => { await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - hotPhasePrimaryShardCount + PHASE_REPLICA_COUNT, + hotPhaseReplicaCount ); validate(); }} > - Use number in hot phase + Set same as hot phase - - ) : null} - -

Force merge

-
- - - Reduce the number of segments in your shard by and merging smaller - files and clearing deleted ones.{' '} - - Learn More - - - + +

Shrink

+
+ + + Shrink the index into a new index with fewer primary shards.{' '} + + Learn more. + + + + + + + { + await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); + validate(); + }} + label="Enable shrink" + /> - + - { - await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); - validate(); - }} - /> + {phaseData[PHASE_SHRINK_ENABLED] ? ( + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + e.target.value + ); + validate(); + }} + /> + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + hotPhasePrimaryShardCount + ); + validate(); + }} + > + Set same as hot phase + + + + + + + + ) : null} + + +

Force merge

+
+ + + Reduce the number of segments in your shard by and merging smaller + files and clearing deleted ones.{' '} + + Learn More + + + - + - {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( - - { - await setPhaseData( - PHASE_FORCE_MERGE_SEGMENTS, - e.target.value - ); + await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); validate(); }} /> - - ) : null} -
-
+ + + + {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( + + { + await setPhaseData( + PHASE_FORCE_MERGE_SEGMENTS, + e.target.value + ); + validate(); + }} + /> + + ) : null} + + ) : ( + +
+ { + await setPhaseData(PHASE_ENABLED, true); + validate(); + }} + > + Activate warm phase + +
+
+ )} + + ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index df1c4199ab507..8cabbf599371e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -10,6 +10,7 @@ import { toastNotifications } from 'ui/notify'; import { EuiTitle, + EuiText, EuiSpacer, EuiHorizontalRule, EuiButton, @@ -123,14 +124,14 @@ export class PolicyConfiguration extends Component {

- {!selectedPolicyName ? 'Edit new policy' : `Edit policy ${selectedPolicyName}`} + {!selectedPolicyName ? 'Create a new policy' : `Edit policy ${selectedPolicyName}`}

- -
Configure the phases of your data and when to transition between them. Only the hot phase is required.
-
- + +

Configure the phases of your data and when to transition between them. Only the hot phase is required.

+
+ - Next + Continue {this.state.isShowingNodeDetailsFlyout ? ( diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index 883f7bdadea61..80e10110baa1b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -8,14 +8,12 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { - EuiFlexGrid, EuiFlexGroup, EuiFlexItem, - EuiIcon, - EuiTitle, - EuiSpacer, - EuiPanel, - EuiText, + EuiButton, + EuiDescribedFormGroup, + EuiFormRow, + EuiSelect, } from '@elastic/eui'; export class PolicySelection extends Component { @@ -38,25 +36,48 @@ export class PolicySelection extends Component { // this.props.done(); } + render() { const { policies, existingPolicyName } = this.props; + let options = []; + options = policies.map(item => ({ value: item.name, text: item.name })); + + console.log(policies); + return ( -
- - - -

Select a policy

-
- -

- An index lifecycle policy is a blueprint for transitioning your data over time. - You can create a new policy or edit an existing policy and save it with a new name. -

-
+ Select or create a policy} + titleSize="s" + description="An index lifecycle policy is a + blueprint for transitioning your data over time. + You can create a new policy or edit an existing + policy and save it with a new name." + fullWidth + > + + + + { + await this.selectPolicy(e.target.value); + }} + /> + + + + + this.selectPolicy(null)} + > + Create new policy + + - + {/* ))} + */} + {/* */} {/* Back */} -
+ ); } } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js index 77809dc140e53..7f743e048fd46 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js @@ -4,17 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { EuiCodeEditor, - EuiDescriptionList, - EuiDescriptionListTitle, - EuiDescriptionListDescription, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, + EuiToolTip, + EuiCode, } from '@elastic/eui'; import ace from 'brace'; import 'brace/mode/json'; @@ -68,30 +67,31 @@ export class DiffView extends PureComponent { addDiffAddonsForAce(); return ( - - - + + +
    {changes.map(({ key, original, updated }) => ( - - - this.scrollToKey(key, updated)}> - {key} - - - - {original ? ( +
  • + - Changing `{JSON.stringify(original)}` to `{JSON.stringify(updated)}`` + Changing the value of {key} from {JSON.stringify(original)} + to {JSON.stringify(updated)} ) : ( - Adding with `{JSON.stringify(updated)}` + Setting a value of {JSON.stringify(updated)} for {key} )} - - + > + this.scrollToKey(key, updated)}> + {key} + + +
  • ))} - +
- + (this.aceEditor = aceEditor)} mode="diff_json" diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index 5c61331258f9f..fff16b7af2bb4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -18,18 +18,19 @@ import 'brace/ext/language_tools'; import { EuiTitle, + EuiCode, + EuiCallOut, EuiSpacer, EuiHorizontalRule, EuiButton, EuiFlexItem, - EuiFlexGrid, - EuiPanel, EuiText, EuiButtonEmpty, EuiFormRow, EuiSwitch, EuiFieldText, EuiLoadingSpinner, + EuiFlexGroup, } from '@elastic/eui'; import { getAffectedIndices } from '../../../../api'; import { DiffView } from './diff_view'; @@ -141,99 +142,54 @@ export class Review extends Component { */} + +

Review your policy changes

+
+ +

Be careful. Your changes will go into effect immediately once you save.

+
- - {originalPolicyName ? ( - - { - await setSaveAsNewPolicy(e.target.checked); - validate(); - }} - label={ - - Save this as a new policy so it does not - effect other templates. - - } - /> - - ) : null} - {saveAsNewPolicy ? ( - + + +

{`${affectedIndexTemplates.length} Affected index ${affectedIndexTemplates.length === 1 ? 'template' : 'templates'}`}

+
    + {affectedIndexTemplates.map(template => ( +
  • {template}
  • + ))} +
+

{`${affectedIndices.length} Affected ${affectedIndices.length === 1 ? 'Index' : 'Indices' }`}

+ { isLoadingAffectedIndices ? ( + + ) : ( +
    + {affectedIndices.map(index =>
  • {index}
  • )} +
+ ) } +
+ + {bootstrapEnabled ? ( + + + - { - await setSelectedPolicyName(e.target.value); - validate(); - }} - /> -
- ) : null} -
- - - - - -

{affectedIndexTemplates.length} Affected Index {affectedIndexTemplates.length === 1 ? 'Template' : 'Templates' }

-
- -
    - {affectedIndexTemplates.map(template => ( -
  • {template}
  • - ))} -
-
-
-
- - - -

{affectedIndices.length} Affected {affectedIndices.length === 1 ? 'Index' : 'Indices' }

-
- - { isLoadingAffectedIndices ? ( - - ) : ( -
    - {affectedIndices.map(index =>
  • {index}
  • )} -
- ) } -
-
-
- {bootstrapEnabled ? ( - - - -

New alias

-
- -

Point to this new alias going forward:

-
    -
  • {aliasName}
  • -
-
-
-
- ) : null} -
+

Since you decided to bootstrap a new index you'll want to point to a new alias going forward

+

{aliasName} is your new alias

+ + + ) : null} + {templateDiff.hasChanged ? (

- We will be changing the index template named `{ - selectedIndexTemplateName - }` + {selectedIndexTemplateName} template changes

@@ -258,18 +214,83 @@ export class Review extends Component {
) : null} - - Back - + + + + {originalPolicyName ? ( + + +

Save changes to {originalPolicyName} policy

+
+ +

+ You are editing an existing policy. This means that any saves you make + will also change any index templates this policy is attached to. You can instead save + these changes and make it a brand new policy that only changes the template you + selected. +

+
+ + + { + await setSaveAsNewPolicy(e.target.checked); + validate(); + }} + label={ + + Save this as a new policy + + } + /> + +
+ ) : null} + {saveAsNewPolicy ? ( + + +

Save your work

+
+ + + { + await setSelectedPolicyName(e.target.value); + validate(); + }} + /> + +
+ ) : null} +
+ + + + + Back + + + + + done(lifecycle)} + > + Looks good, save changes + + +    - done(lifecycle)} - > - Looks good, save changes - ); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js index 57eb484bcc0fd..2b76624165a78 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js @@ -8,6 +8,7 @@ import { connect } from 'react-redux'; +import '../../less/main.less'; import { Wizard as PresentationComponent } from './wizard'; import { saveLifecycle } from '../../store/actions'; import { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index 63d6a7c00a83e..3a7ba64893b23 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -13,8 +13,8 @@ import { PolicyConfiguration } from './components/policy_configuration'; import { Review } from './components/review'; import { EuiPage, - EuiFlexGroup, - EuiFlexItem, + EuiPageBody, + EuiPageContent, EuiTitle, EuiSpacer, EuiStepsHorizontal, @@ -163,20 +163,21 @@ export class Wizard extends Component { return ( - - - + + +

Index lifecycle management

-
-
- - - - {/* */} - {this.renderContent()} + + + + + {/* */} + {this.renderContent()} + +
); } diff --git a/x-pack/plugins/index_management/public/styles/table.less b/x-pack/plugins/index_management/public/styles/table.less index bd2b7a65c443e..2f365744e8da0 100644 --- a/x-pack/plugins/index_management/public/styles/table.less +++ b/x-pack/plugins/index_management/public/styles/table.less @@ -8,7 +8,7 @@ .indexTableHorizontalScrollContainer { overflow-x: auto; max-width: 100%; - height: 100vh; + min-height: 100vh; } .indexTableHorizontalScroll { min-width: 800px; diff --git a/x-pack/yarn.lock b/x-pack/yarn.lock index 1f44d67006d6e..3da71d5ea98f8 100644 --- a/x-pack/yarn.lock +++ b/x-pack/yarn.lock @@ -10,30 +10,25 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@elastic/eui@0.0.44": - version "0.0.44" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.44.tgz#b0b58eb1b10d6f8de017f548bb06c5b5f71e3d61" +"@elastic/eui@0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.47.tgz#5bae27966bb1d68bb3106853610a407509053b44" dependencies: brace "^0.10.0" classnames "^2.2.5" core-js "^2.5.1" - eslint-config-prettier "^2.9.0" - eslint-plugin-prettier "^2.6.0" focus-trap-react "^3.0.4" highlight.js "^9.12.0" html "^1.0.0" - jquery "^3.2.1" keymirror "^0.1.1" lodash "^3.10.1" numeral "^2.0.6" - prettier "^1.11.1" prop-types "^15.6.0" react-ace "^5.5.0" react-color "^2.13.8" react-datepicker v1.4.1 react-input-autosize "^2.2.1" react-virtualized "^9.18.5" - serve "^6.3.1" tabbable "^1.1.0" uuid "^3.1.0" @@ -97,13 +92,6 @@ accept@2.x.x: boom "5.x.x" hoek "4.x.x" -accepts@~1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - acorn-globals@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" @@ -122,10 +110,6 @@ add-event-listener@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/add-event-listener/-/add-event-listener-0.0.1.tgz#a76229ebc64c8aefae204a16273a2f255abea2d0" -address@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" - agentkeepalive@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" @@ -185,12 +169,6 @@ angular-ui-bootstrap@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/angular-ui-bootstrap/-/angular-ui-bootstrap-1.2.5.tgz#b0c1eff0bf3b7a65668984a1b81820a90dc60995" -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - dependencies: - string-width "^2.0.0" - ansi-cyan@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" @@ -276,10 +254,6 @@ aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" -arch@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.0.tgz#3613aa46149064b3c1f0607919bf1d4786e82889" - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -304,15 +278,6 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" -args@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/args/-/args-4.0.0.tgz#5ca24cdba43d4b17111c56616f5f2e9d91933954" - dependencies: - camelcase "5.0.0" - chalk "2.3.2" - leven "2.1.0" - mri "1.1.0" - argv-split@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argv-split/-/argv-split-2.0.1.tgz#be264117790dbd5ccd63ec3f449a1804814ac4c5" @@ -947,12 +912,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" - dependencies: - safe-buffer "5.1.1" - bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -979,7 +938,7 @@ bluebird@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.1.1.tgz#7e2e4318d62ae72a674f6aea6357bb4def1a6e41" -bluebird@3.5.1, bluebird@^3.3.1: +bluebird@^3.3.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -1017,18 +976,6 @@ boom@5.x.x: dependencies: hoek "4.x.x" -boxen@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" - brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -1144,10 +1091,6 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1180,10 +1123,6 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -1196,7 +1135,7 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1232,22 +1171,6 @@ chai@~1.9.2: assertion-error "1.0.0" deep-eql "0.1.3" -chalk@2.3.2, chalk@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1266,6 +1189,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chance@1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.10.tgz#03500b04ad94e778dd2891b09ec73a6ad87b1996" @@ -1312,10 +1243,6 @@ classnames@2.2.5, classnames@^2.1.2, classnames@^2.2.3, classnames@^2.2.4, class version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" @@ -1334,13 +1261,6 @@ clipboard@^1.6.1: select "^1.1.2" tiny-emitter "^2.0.0" -clipboardy@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" - dependencies: - arch "^2.1.0" - execa "^0.8.0" - cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -1460,24 +1380,6 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" -compressible@~2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" - dependencies: - mime-db ">= 1.33.0 < 2" - -compression@^1.6.2: - version "1.7.2" - resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" - dependencies: - accepts "~1.3.4" - bytes "3.0.0" - compressible "~2.0.13" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.1" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1514,10 +1416,6 @@ content-type-parser@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" -content-type@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - content@3.x.x: version "3.0.6" resolved "https://registry.yarnpkg.com/content/-/content-3.0.6.tgz#9c2e301e9ae515ed65a4b877d78aa5659bb1b809" @@ -1789,10 +1687,6 @@ d3@3.5.6: version "3.5.6" resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.6.tgz#9451c651ca733fb9672c81fb7f2655164a73a42d" -dargs@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1828,7 +1722,7 @@ debug@2.2.0: dependencies: ms "0.7.1" -debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8: +debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1937,22 +1831,10 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - deprecated@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -1971,13 +1853,6 @@ detect-newline@2.X, detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" -detect-port@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.2.2.tgz#57a44533632d8bc74ad255676866ca43f96c7469" - dependencies: - address "^1.0.1" - debug "^2.6.0" - dfa@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.1.0.tgz#d30218bd10d030fa421df3ebbc82285463a31781" @@ -2093,10 +1968,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - elasticsearch@13.0.1: version "13.0.1" resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-13.0.1.tgz#fa58204233052c4cd221e8721e48f3906b385b32" @@ -2108,10 +1979,6 @@ elasticsearch@13.0.1: lodash.isempty "^4.4.0" lodash.trimend "^4.5.1" -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -2204,10 +2071,6 @@ es6-promise@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.0.1.tgz#ccc4963e679f0ca9fb187c777b9e583d3c7573c2" -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - escape-string-regexp@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" @@ -2256,19 +2119,6 @@ escodegen@~1.3.2: optionalDependencies: source-map "~0.1.33" -eslint-config-prettier@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz#5ecd65174d486c22dff389fe036febf502d468a3" - dependencies: - get-stdin "^5.0.1" - -eslint-plugin-prettier@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz#33e4e228bdb06142d03c560ce04ec23f6c767dd7" - dependencies: - fast-diff "^1.1.1" - jest-docblock "^21.0.0" - esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -2305,10 +2155,6 @@ esutils@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - exec-sh@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38" @@ -2339,18 +2185,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -2492,10 +2326,6 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" -fast-diff@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" - fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -2554,10 +2384,6 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" -filesize@3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -2735,22 +2561,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - from@^0.1.3: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" -fs-extra@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-mkdirp-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" @@ -2837,10 +2651,6 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" -get-stdin@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" - get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -3240,7 +3050,7 @@ gulplog@^1.0.0: dependencies: glogg "^1.0.0" -handlebars@4.0.11, handlebars@^4.0.3: +handlebars@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: @@ -3479,15 +3289,6 @@ htmlparser2@^3.9.1: inherits "^2.0.1" readable-stream "^2.0.2" -http-errors@1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" - dependencies: - depd "1.1.1" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - http-errors@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.4.0.tgz#6c0242dea6b3df7afda153c71089b31c6e82aabf" @@ -3495,15 +3296,6 @@ http-errors@~1.4.0: inherits "2.0.1" statuses ">= 1.2.1 < 2" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -3570,7 +3362,7 @@ inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -3615,10 +3407,6 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -ip@1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - iron@4.x.x: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" @@ -3838,7 +3626,7 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-stream@1.1.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3876,10 +3664,6 @@ is-windows@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4069,10 +3853,6 @@ jest-diff@^22.4.3: jest-get-type "^22.4.3" pretty-format "^22.4.3" -jest-docblock@^21.0.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" - jest-docblock@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.4.3.tgz#50886f132b42b280c903c592373bb6e93bb68b19" @@ -4309,7 +4089,7 @@ jquery@^3.1.1: version "3.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787" -jquery@^3.2.1, jquery@^3.3.1: +jquery@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" @@ -4387,12 +4167,6 @@ json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - optionalDependencies: - graceful-fs "^4.1.6" - jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -4480,7 +4254,7 @@ left-pad@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" -leven@2.1.0, leven@^2.1.0: +leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" @@ -4862,21 +4636,6 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micro-compress@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micro-compress/-/micro-compress-1.0.0.tgz#53f5a80b4ad0320ca165a559b6e3df145d4f704f" - dependencies: - compression "^1.6.2" - -micro@9.1.4: - version "9.1.4" - resolved "https://registry.yarnpkg.com/micro/-/micro-9.1.4.tgz#dbe655f34bb3390509898ddf3fda12348f5cbaa9" - dependencies: - content-type "1.0.4" - is-stream "1.1.0" - mri "1.1.0" - raw-body "2.3.2" - micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -4917,30 +4676,16 @@ mime-db@1.x.x: version "1.32.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414" -"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -mime-types@2.1.18, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - dependencies: - mime-db "~1.33.0" - mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: mime-db "~1.30.0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -5071,10 +4816,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.13.0, moment@^2.20.1: version "2.20.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" -mri@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" - ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -5145,10 +4886,6 @@ nearley@^2.7.10: railroad-diagrams "^1.0.0" randexp "^0.4.2" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - ngreact@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/ngreact/-/ngreact-0.5.1.tgz#2dcccc1541771796689d13e51bb8d5010af41c57" @@ -5204,10 +4941,6 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" -node-version@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.1.3.tgz#1081c87cce6d2dbbd61d0e51e28c287782678496" - nomnom@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -5389,16 +5122,6 @@ object.values@^1.0.4: function-bind "^1.1.0" has "^1.0.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5415,16 +5138,6 @@ onetime@^1.0.0: version "1.1.0" resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" -openssl-self-signed-certificate@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz#9d3a4776b1a57e9847350392114ad2f915a83dd4" - -opn@5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" - dependencies: - is-wsl "^1.1.0" - optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -5586,7 +5299,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@1.0.2, path-is-inside@^1.0.1: +path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -5621,12 +5334,6 @@ path-to-regexp@^1.0.0, path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -5689,10 +5396,6 @@ pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -5797,10 +5500,6 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.11.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" - pretty-format@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" @@ -5973,28 +5672,6 @@ randomatic@^1.1.3: is-number "^3.0.0" kind-of "^4.0.0" -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -raw-body@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" - dependencies: - bytes "3.0.0" - http-errors "1.6.2" - iconv-lite "0.4.19" - unpipe "1.0.0" - -rc@^1.0.1, rc@^1.1.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - rc@^1.1.7: version "1.2.3" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.3.tgz#51575a900f8dd68381c710b4712c2154c3e2035b" @@ -6390,19 +6067,6 @@ regexpu-core@^2.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -registry-auth-token@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - dependencies: - rc "^1.0.1" - regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" @@ -6685,7 +6349,7 @@ rxjs@5.3.0: dependencies: symbol-observable "^1.0.1" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -6747,55 +6411,10 @@ semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - sequencify@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" -serve@^6.3.1: - version "6.5.6" - resolved "https://registry.yarnpkg.com/serve/-/serve-6.5.6.tgz#579136688f80f6bf4a618ca8e8cba10dfb4d95e3" - dependencies: - args "4.0.0" - basic-auth "2.0.0" - bluebird "3.5.1" - boxen "1.3.0" - chalk "2.4.0" - clipboardy "1.2.3" - dargs "5.1.0" - detect-port "1.2.2" - filesize "3.6.1" - fs-extra "5.0.0" - handlebars "4.0.11" - ip "1.1.5" - micro "9.1.4" - micro-compress "1.0.0" - mime-types "2.1.18" - node-version "1.1.3" - openssl-self-signed-certificate "1.1.6" - opn "5.3.0" - path-is-inside "1.0.2" - path-type "3.0.0" - send "0.16.2" - update-check "1.3.2" - set-blocking@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-1.0.0.tgz#cd5e5d938048df1ac92dfe92e1f16add656f5ec5" @@ -6832,14 +6451,6 @@ setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - shallow-copy@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" @@ -7088,14 +6699,10 @@ static-module@^1.1.0: static-eval "~0.2.0" through2 "~0.4.1" -"statuses@>= 1.2.1 < 2", statuses@~1.4.0: +"statuses@>= 1.2.1 < 2": version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" -"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -7353,12 +6960,6 @@ temp@^0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - dependencies: - execa "^0.7.0" - test-exclude@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" @@ -7662,14 +7263,6 @@ unique-stream@^2.0.2: json-stable-stringify "^1.0.0" through2-filter "^2.0.0" -universalify@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" - -unpipe@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -7677,13 +7270,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -update-check@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.3.2.tgz#460f9e9ab24820367f3edbeb4d4142d9936ff171" - dependencies: - registry-auth-token "3.3.2" - registry-url "3.1.0" - urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -7746,10 +7332,6 @@ value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - venn.js@0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/venn.js/-/venn.js-0.2.9.tgz#33c29075efa484731d59d884752900cc33033656" @@ -7948,12 +7530,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2" -widest-line@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" - dependencies: - string-width "^2.1.1" - window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" diff --git a/yarn.lock b/yarn.lock index cdda60551b8cc..8f573bc2f01fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,30 +77,25 @@ version "0.0.0" uid "" -"@elastic/eui@0.0.44", "@elastic/eui@v0.0.44": - version "0.0.44" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.44.tgz#b0b58eb1b10d6f8de017f548bb06c5b5f71e3d61" +"@elastic/eui@0.0.47", "@elastic/eui@v0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.47.tgz#5bae27966bb1d68bb3106853610a407509053b44" dependencies: brace "^0.10.0" classnames "^2.2.5" core-js "^2.5.1" - eslint-config-prettier "^2.9.0" - eslint-plugin-prettier "^2.6.0" focus-trap-react "^3.0.4" highlight.js "^9.12.0" html "^1.0.0" - jquery "^3.2.1" keymirror "^0.1.1" lodash "^3.10.1" numeral "^2.0.6" - prettier "^1.11.1" prop-types "^15.6.0" react-ace "^5.5.0" react-color "^2.13.8" react-datepicker v1.4.1 react-input-autosize "^2.2.1" react-virtualized "^9.18.5" - serve "^6.3.1" tabbable "^1.1.0" uuid "^3.1.0" @@ -181,14 +176,6 @@ version "9.4.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.7.tgz#57d81cd98719df2c9de118f2d5f3b1120dcd7275" -"@zeit/check-updates@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@zeit/check-updates/-/check-updates-1.1.0.tgz#d0f65026a36f27cd1fd54c647d8294447c1d2d8b" - dependencies: - chalk "2.3.0" - ms "2.1.1" - update-notifier "2.3.0" - JSONStream@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.1.1.tgz#c98bfd88c8f1e1e8694e53c5baa6c8691553e59a" @@ -226,13 +213,6 @@ accepts@1.3.3: mime-types "~2.1.11" negotiator "0.6.1" -accepts@~1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -281,10 +261,6 @@ add-event-listener@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/add-event-listener/-/add-event-listener-0.0.1.tgz#a76229ebc64c8aefae204a16273a2f255abea2d0" -address@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" - adm-zip@0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" @@ -513,10 +489,6 @@ aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" -arch@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.0.tgz#3613aa46149064b3c1f0607919bf1d4786e82889" - are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" @@ -537,16 +509,6 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" -args@3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/args/-/args-3.0.8.tgz#2f425ab639c69d74ff728f3d7c6e93b97b91af7c" - dependencies: - camelcase "4.1.0" - chalk "2.1.0" - mri "1.1.0" - pkginfo "0.4.1" - string-similarity "1.2.0" - arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -1567,12 +1529,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" - dependencies: - safe-buffer "5.1.1" - batch-processor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" @@ -1629,14 +1585,14 @@ bluebird@3.4.6: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" -bluebird@3.5.1, bluebird@^3.3.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - bluebird@^2.10.0, bluebird@^2.9.24: version "2.11.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" +bluebird@^3.3.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + bmp-js@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a" @@ -1723,7 +1679,7 @@ boom@5.2.0, boom@5.x.x: dependencies: hoek "4.x.x" -boxen@1.3.0, boxen@^1.2.1, boxen@^1.2.2: +boxen@^1.2.1, boxen@^1.2.2: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" dependencies: @@ -2071,10 +2027,6 @@ camelcase-keys@^3.0.0: camelcase "^3.0.0" map-obj "^1.0.0" -camelcase@4.1.0, camelcase@^4.0.0, camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -2087,6 +2039,10 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" +camelcase@^4.0.0, camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" @@ -2146,14 +2102,6 @@ chai@3.5.0, "chai@>=1.9.2 <4.0.0": deep-eql "^0.1.3" type-detect "^1.0.0" -chalk@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" - dependencies: - ansi-styles "^3.1.0" - escape-string-regexp "^1.0.5" - supports-color "^4.0.0" - chalk@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" @@ -2162,14 +2110,6 @@ chalk@2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -chalk@2.3.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2180,6 +2120,14 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -2396,13 +2344,6 @@ clipboard@^1.6.1: select "^1.1.2" tiny-emitter "^2.0.0" -clipboardy@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" - dependencies: - arch "^2.1.0" - execa "^0.8.0" - cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -2614,24 +2555,6 @@ component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" -compressible@~2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" - dependencies: - mime-db ">= 1.33.0 < 2" - -compression@^1.6.2: - version "1.7.2" - resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" - dependencies: - accepts "~1.3.4" - bytes "3.0.0" - compressible "~2.0.13" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.1" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2746,7 +2669,7 @@ content-type-parser@^1.0.1, content-type-parser@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" -content-type@1.0.4, content-type@~1.0.1, content-type@~1.0.4: +content-type@~1.0.1, content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -3308,10 +3231,6 @@ d@1: dependencies: es5-ext "^0.10.9" -dargs@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -3341,7 +3260,7 @@ debug@0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" -debug@2, debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -3538,7 +3457,7 @@ depd@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.0.1.tgz#80aec64c9d6d97e65cc2a9caa93c0aa6abf73aaa" -depd@~1.1.0, depd@~1.1.1, depd@~1.1.2: +depd@~1.1.0, depd@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3549,10 +3468,6 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -3567,13 +3482,6 @@ detect-newline@2.X, detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" -detect-port@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.2.2.tgz#57a44533632d8bc74ad255676866ca43f96c7469" - dependencies: - address "^1.0.1" - debug "^2.6.0" - dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -3856,7 +3764,7 @@ encode-uri-query@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/encode-uri-query/-/encode-uri-query-1.0.0.tgz#d632be4aafe8316c6145007ffb2844c5312b194c" -encodeurl@~1.0.1, encodeurl@~1.0.2: +encodeurl@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4375,10 +4283,6 @@ esutils@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - even-better@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/even-better/-/even-better-7.0.2.tgz#d056f429c90ecc20ee9494aca0a751f743504d2e" @@ -4457,18 +4361,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" @@ -4784,10 +4676,6 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" -filesize@3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa" - fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -4982,10 +4870,6 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - from@^0.1.3, from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" @@ -5008,14 +4892,6 @@ fs-extra@4.0.3, fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" @@ -5641,9 +5517,9 @@ h2o2@5.1.1: joi "9.X.X" wreck "9.X.X" -handlebars@4.0.11, handlebars@^4.0.1, handlebars@^4.0.3: - version "4.0.11" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" +handlebars@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -5651,9 +5527,9 @@ handlebars@4.0.11, handlebars@^4.0.1, handlebars@^4.0.3: optionalDependencies: uglify-js "^2.6" -handlebars@4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" +handlebars@^4.0.1, handlebars@^4.0.3: + version "4.0.11" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -6262,10 +6138,6 @@ ip-regex@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" -ip@1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - iron@4.x.x: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" @@ -6612,7 +6484,7 @@ is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-stream@1.1.0, is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -6670,10 +6542,6 @@ is-word-character@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.1.tgz#5a03fa1ea91ace8a6eb0c7cd770eb86d65c8befb" -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -7348,7 +7216,7 @@ jpeg-js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.2.0.tgz#53e448ec9d263e683266467e9442d2c5a2ef5482" -jquery@^3.1.1, jquery@^3.2.1, jquery@^3.3.1: +jquery@^3.1.1, jquery@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" @@ -8433,21 +8301,6 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micro-compress@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micro-compress/-/micro-compress-1.0.0.tgz#53f5a80b4ad0320ca165a559b6e3df145d4f704f" - dependencies: - compression "^1.6.2" - -micro@9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/micro/-/micro-9.1.0.tgz#f2effba306639076e994c007c327dfc36a5185e9" - dependencies: - content-type "1.0.4" - is-stream "1.1.0" - mri "1.1.0" - raw-body "2.3.2" - micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -8491,11 +8344,11 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.x.x, "mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: +mime-db@1.x.x, mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-types@2.1.18, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: @@ -8505,10 +8358,6 @@ mime@1.3.x: version "1.3.6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -8668,10 +8517,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.10.6, moment@^2.13.0, moment@^2.20.1: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" -mri@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" - ms@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" @@ -8688,7 +8533,7 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -ms@2.1.1, ms@^2.0.0: +ms@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" @@ -8891,10 +8736,6 @@ node-status-codes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" -node-version@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.1.0.tgz#f437d7ba407e65e2c4eaef8887b1718ba523d4f0" - nomnom@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -9118,10 +8959,6 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -9138,16 +8975,6 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -openssl-self-signed-certificate@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz#9d3a4776b1a57e9847350392114ad2f915a83dd4" - -opn@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225" - dependencies: - is-wsl "^1.1.0" - optimist@^0.6.1, optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -9399,7 +9226,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1, path-is-absolute@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@1.0.2, path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -9424,12 +9251,6 @@ path-to-regexp@^1.0.0, path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@3.0.0, path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -9444,6 +9265,12 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -9576,10 +9403,6 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" -pkginfo@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" - plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" @@ -9915,10 +9738,6 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75" - prettier@^1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" @@ -10193,7 +10012,7 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@^1.2.0, range-parser@~1.2.0: +range-parser@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -10737,6 +10556,9 @@ regex-cache@^0.4.2: regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" regexpu-core@^1.0.0: version "1.0.0" @@ -11128,7 +10950,7 @@ rxjs@5.4.3: dependencies: symbol-observable "^1.0.1" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -11249,51 +11071,6 @@ semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -serve@^6.3.1: - version "6.5.3" - resolved "https://registry.yarnpkg.com/serve/-/serve-6.5.3.tgz#39ae7b7ff5934a9ca93ba7235344eb34b726cc48" - dependencies: - "@zeit/check-updates" "1.1.0" - args "3.0.8" - basic-auth "2.0.0" - bluebird "3.5.1" - boxen "1.3.0" - chalk "2.3.2" - clipboardy "1.2.3" - dargs "5.1.0" - detect-port "1.2.2" - filesize "3.6.0" - fs-extra "5.0.0" - handlebars "4.0.11" - ip "1.1.5" - micro "9.1.0" - micro-compress "1.0.0" - mime-types "2.1.18" - node-version "1.1.0" - openssl-self-signed-certificate "1.1.6" - opn "5.2.0" - path-is-inside "1.0.2" - path-type "3.0.0" - send "0.16.2" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -11768,7 +11545,7 @@ static-module@^2.2.0: static-eval "^2.0.0" through2 "~2.0.3" -statuses@1, "statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2", statuses@~1.4.0: +statuses@1, "statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2": version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" @@ -11840,12 +11617,6 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" -string-similarity@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-1.2.0.tgz#d75153cb383846318b7a39a8d9292bb4db4e9c30" - dependencies: - lodash "^4.13.1" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -12743,7 +12514,19 @@ upath@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" -update-notifier@2.3.0, update-notifier@^2.2.0: +update-notifier@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" + dependencies: + chalk "^1.0.0" + configstore "^1.0.0" + is-npm "^1.0.0" + latest-version "^1.0.0" + repeating "^1.1.2" + semver-diff "^2.0.0" + string-length "^1.0.0" + +update-notifier@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" dependencies: @@ -12757,18 +12540,6 @@ update-notifier@2.3.0, update-notifier@^2.2.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" -update-notifier@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" - dependencies: - chalk "^1.0.0" - configstore "^1.0.0" - is-npm "^1.0.0" - latest-version "^1.0.0" - repeating "^1.1.2" - semver-diff "^2.0.0" - string-length "^1.0.0" - urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -12880,10 +12651,6 @@ value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - vega-canvas@1: version "1.0.1" resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.0.1.tgz#22cfa510af0cfbd920fc6af8b6111d3de5e63c44" From aa3abdd4e455cf0cfd633de165d0cfd5942d87dc Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 16 May 2018 20:20:07 -0400 Subject: [PATCH 011/102] Adding server api tests --- .../register_license_checker.js | 4 +- .../indices/__tests__/bootstrap_route.test.js | 56 +++++++++++++ .../__tests__/get_affected_route.test.js | 78 +++++++++++++++++++ .../indices/register_get_affected_route.js | 2 +- 4 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js index b05946e60b330..35bc4b7533605 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js @@ -10,9 +10,9 @@ import { PLUGIN } from '../../../common/constants'; export function registerLicenseChecker(server) { const xpackMainPlugin = server.plugins.xpack_main; - const watcherPlugin = server.plugins.watcher; + const ilmPlugin = server.plugins.index_lifecycle_management; - mirrorPluginStatus(xpackMainPlugin, watcherPlugin); + mirrorPluginStatus(xpackMainPlugin, ilmPlugin); xpackMainPlugin.status.once('green', () => { // Register a function that is called whenever the xpack info changes, // to re-compute the license check results for this plugin diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js new file mode 100644 index 0000000000000..5bc48358cd187 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js @@ -0,0 +1,56 @@ +/* + * 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 { registerBootstrapRoute } from '../register_bootstrap_route'; + +jest.mock('../../../../lib/call_with_request_factory', () => { + const mock = jest.fn(); + return { + callWithRequestFactory: () => mock, + }; +}); + +jest.mock('../../../../lib/is_es_error_factory', () => ({ + isEsErrorFactory: jest.fn().mockImplementation(() => jest.fn()), +})); + +jest.mock('../../../../lib/license_pre_routing_factory', () => ({ + licensePreRoutingFactory: jest.fn().mockImplementation(() => jest.fn()), +})); + +let routeHandler; +const mockServer = { + route: options => { + routeHandler = options.handler; + } +}; + +describe('ilmBootstrapRoute', () => { + it('should call indices.create', async () => { + registerBootstrapRoute(mockServer); + + await routeHandler({ payload: { + indexName: 'myIndex', + aliasName: 'myAlias', + } }, jest.fn()); + + const mock = require('../../../../lib/call_with_request_factory').callWithRequestFactory().mock; + + expect(mock.calls.length).toBe(1); + expect(mock.calls[0]).toEqual([ + 'indices.create', + { + index: 'myIndex', + aliases: { + myAlias: {} + }, + settings: { + 'index.lifecycle.rollover_alias': 'myAlias' + } + } + ]); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js new file mode 100644 index 0000000000000..f50ede9e34af7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js @@ -0,0 +1,78 @@ +/* + * 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 { registerGetAffectedRoute } from '../register_get_affected_route'; + +jest.mock('../../../../lib/call_with_request_factory', () => { + const mock = jest.fn().mockImplementation((method, params) => { + // console.log('hi', method, params); + if (params.path === '/_template') { + return { + 'foobar': { + index_patterns: ['foobar*'] + }, + 'barfoo': { + index_patterns: ['barfoo*'], + settings: { + index: { + lifecycle: { + name: 'myPolicy' + } + } + } + } + }; + } + }); + return { + callWithRequestFactory: () => mock, + }; +}); + +jest.mock('../../../../lib/is_es_error_factory', () => ({ + isEsErrorFactory: jest.fn().mockImplementation(() => jest.fn()), +})); + +jest.mock('../../../../lib/license_pre_routing_factory', () => ({ + licensePreRoutingFactory: jest.fn().mockImplementation(() => jest.fn()), +})); + +const routeHandlers = []; +const mockServer = { + route: options => { + routeHandlers.push(options.handler); + } +}; + +describe('ilmGetAffectedRoute', () => { + it('should call indices.create', async () => { + registerGetAffectedRoute(mockServer); + + + for (const routeHandler of routeHandlers) { + await routeHandler({ params: { + indexTemplateName: 'foobar', + policyName: 'myPolicy' + } }, jest.fn()); + } + + const mock = require('../../../../lib/call_with_request_factory').callWithRequestFactory().mock; + + expect(mock.calls.length).toBe(4); + expect(mock.calls[1]).toEqual([ + 'indices.get', + { + index: ['foobar*'] + } + ]); + expect(mock.calls[3]).toEqual([ + 'indices.get', + { + index: ['foobar*', 'barfoo*'] + } + ]); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js index 0cb74ac6d2599..fc7f5f2d2d131 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -27,7 +27,7 @@ async function getAffectedIndices( ) { const templates = await fetchTemplates(callWithRequest); - if (!templates || templates.length === 0) { + if (!templates || Object.keys(templates).length === 0) { return []; } From 70caea4db1a8cedab4b59182f952a08563dbc485 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Thu, 17 May 2018 11:07:16 -0400 Subject: [PATCH 012/102] Removing debug and some tweaks from dave's work --- .../public/api/index.js | 2 -- .../public/lib/diff_ace_addons.js | 22 ++-------------- .../public/lib/diff_tools.js | 4 --- .../components/cold_phase/cold_phase.js | 2 +- .../components/delete_phase/delete_phase.js | 2 +- .../components/hot_phase/hot_phase.js | 2 +- .../components/warm_phase/warm_phase.js | 2 +- .../policy_selection/policy_selection.js | 16 +++++++----- .../wizard/components/review/diff_view.js | 6 ----- .../wizard/components/review/review.js | 2 -- .../public/store/actions/index_template.js | 4 +-- .../public/store/actions/policies.js | 1 + .../public/store/middleware/index.js | 1 + ..._selected_policy_from_selected_template.js | 26 +++++++++++++++++++ .../public/store/reducers/policies.js | 2 +- .../public/store/store.js | 4 +-- .../__tests__/get_affected_route.test.js | 1 - 17 files changed, 48 insertions(+), 51 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index c82f07b7d6556..c771697e6b27a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -11,8 +11,6 @@ export const setHttpClient = (client) => { }; const apiPrefix = chrome.addBasePath('/api/index_lifecycle_management'); -console.log('apiPrefix', apiPrefix); - export async function loadNodes() { const response = await httpClient.get(`${apiPrefix}/nodes/list`); return response.data; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js index 31e64a3c5b1d7..96d3f1cc6849f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js @@ -7,30 +7,22 @@ import ace from 'brace'; import { ADDITION_PREFIX, REMOVAL_PREFIX } from './diff_tools'; function findInObject(key, obj, debug) { - // debug && console.log('findInObject()', key, obj); const objKeys = Object.keys(obj); for (const objKey of objKeys) { if (objKey === key) { - // debug && console.log('findInObject() FOUND', key); return obj[objKey]; } if (typeof obj[objKey] === 'object' && !Array.isArray(obj[objKey])) { const item = findInObject(key, obj[objKey], debug); if (item !== false) { - // debug && console.log('findInObject() FOUND 2', key); return item; } } } - // debug && console.log('findInObject() NOT FOUND', key); return false; } function getDiffClasses(key, val, jsonObject) { - const debug = false;//key === 'name' && val === '"t"'; - - debug && console.log('getDiffClasses()', key, val); - let value = val; if (value.endsWith(',')) { value = value.slice(0, -1); @@ -39,8 +31,8 @@ function getDiffClasses(key, val, jsonObject) { value = value.slice(1, -1); } - const additionValue = findInObject(`${ADDITION_PREFIX}${key}`, jsonObject, debug); - const removalValue = findInObject(`${REMOVAL_PREFIX}${key}`, jsonObject, debug); + const additionValue = findInObject(`${ADDITION_PREFIX}${key}`, jsonObject); + const removalValue = findInObject(`${REMOVAL_PREFIX}${key}`, jsonObject); const isAddition = Array.isArray(additionValue) ? !!additionValue.find(v => v === value) @@ -58,16 +50,6 @@ function getDiffClasses(key, val, jsonObject) { diffClasses = 'variable'; } - debug && console.log(`getDiffClasses() - key='${key}' - value='${value}' - additionValue='${additionValue}' - removalValue='${removalValue}' - isAddition=${isAddition} - isRemoval=${isRemoval} - diffClasses='${diffClasses}' - `); - return diffClasses; } diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js index 30e4dacbb3cfb..ca2b113c83086 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js @@ -95,10 +95,7 @@ export const mergeAndPreserveDuplicateKeys = ( changes = [] ) => { for (const [key, value] of Object.entries(source)) { - // const debug = key === 'fooobar'; - // debug && console.log('mergeAndPreserveDuplicateKeys', key, value, target); if (isDifferent(target, key, value)) { - // debug && console.log('isDifferent'); result[`${REMOVAL_PREFIX}${key}`] = value; result[`${ADDITION_PREFIX}${key}`] = target[key]; changes.push({ @@ -107,7 +104,6 @@ export const mergeAndPreserveDuplicateKeys = ( updated: removePrefixes(target[key]), }); } else if (isObject(value)) { - // debug && console.log('value is object', target[key]); if (target.hasOwnProperty(key)) { const recurseResult = mergeAndPreserveDuplicateKeys(value, target[key]); result[key] = recurseResult.result; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 13f73ce68b5a7..65e9876fd88b8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -82,7 +82,7 @@ export class ColdPhase extends PureComponent {
Cold phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 7a8a00301023e..40974abb33fec 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -65,7 +65,7 @@ export class DeletePhase extends PureComponent {
Delete phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 9511cdac48294..89d893dbc070f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -70,7 +70,7 @@ export class HotPhase extends PureComponent { title={
Hot phase{' '} - +
} titleSize="s" diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index c627b96bd2a0a..56418c4ea34f4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -116,7 +116,7 @@ export class WarmPhase extends Component {
Warm phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index 80e10110baa1b..2f5e71d8d0f42 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -31,7 +31,8 @@ export class PolicySelection extends Component { this.props.fetchPolicies(); } - selectPolicy(policy) { + selectPolicy(policyName) { + const policy = this.props.policies.find(policy => policy.name === policyName); this.props.setSelectedPolicy(policy); // this.props.done(); } @@ -40,10 +41,11 @@ export class PolicySelection extends Component { render() { const { policies, existingPolicyName } = this.props; - let options = []; - options = policies.map(item => ({ value: item.name, text: item.name })); - - console.log(policies); + const options = policies.map(item => ({ value: item.name, text: item.name })); + options.unshift({ + value: '', + text: '-- New Policy --' + }); return ( - + this.selectPolicy(null)} + onClick={() => this.selectPolicy()} > Create new policy diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js index 7f743e048fd46..a89f7e2412c59 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js @@ -47,22 +47,16 @@ export class DiffView extends PureComponent { templateDiff: { originalFullIndexTemplate, newFullIndexTemplate }, } = this.props; - // console.log(JSON.stringify(this.props)); - const { result: mergedJson, changes } = mergeAndPreserveDuplicateKeys( originalFullIndexTemplate, newFullIndexTemplate ); - console.log('mergedJson', mergedJson, changes); - // Strip the ^ and $ characters const mergedJsonAsString = removePrefixes( JSON.stringify(mergedJson, null, 2) ); - // console.log('mergedJsonAsString', mergedJsonAsString); - setCurrentJsonObject(mergedJson); addDiffAddonsForAce(); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index fff16b7af2bb4..98a4ca16dc609 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -133,8 +133,6 @@ export class Review extends Component { const { affectedIndices, isLoadingAffectedIndices, isShowingErrors } = this.state; - // console.log('render', affectedIndices); - return (
{/* diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js index 50eec52976059..a7640e9cfb97f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js @@ -17,7 +17,7 @@ import { setAliasName, setSelectedPrimaryShardCount, setSelectedReplicaCount, - setSelectedNodeAttrs + setSelectedNodeAttrs, } from '.'; import { PHASE_HOT, @@ -43,7 +43,7 @@ export const fetchIndexTemplates = () => async dispatch => { }; export const fetchedIndexTemplate = createAction('FETCHED_INDEX_TEMPLATE'); -export const fetchIndexTemplate = templateName => async dispatch => { +export const fetchIndexTemplate = templateName => async (dispatch) => { let template; try { template = await loadIndexTemplate(templateName); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index 47046331a4eb4..7d420d6e43b20 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -22,6 +22,7 @@ export const fetchPolicies = () => async dispatch => { } dispatch(fetchedPolicies(policies)); + return policies; }; export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js index ccad9cac1daa4..1ec89365a7906 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js @@ -5,3 +5,4 @@ */ export { autoEnablePhase } from './auto_enable_phase'; +export { setSelectedPolicyFromSelectedTemplate } from './set_selected_policy_from_selected_template'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js new file mode 100644 index 0000000000000..f9ab711e9f302 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js @@ -0,0 +1,26 @@ +/* + * 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 { + fetchedIndexTemplate, + fetchPolicies, + setSelectedPolicy +} from '../actions'; + +export const setSelectedPolicyFromSelectedTemplate = store => next => async action => { + if (action.type === fetchedIndexTemplate().type) { + const template = action.payload; + if (template.settings.index && template.settings.index.lifecycle) { + const policies = await fetchPolicies()(store.dispatch); + const selectedPolicy = policies.find(policy => policy.name === template.settings.index.lifecycle.name); + if (selectedPolicy) { + store.dispatch(setSelectedPolicy(selectedPolicy)); + } + } + } + + return next(action); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 91e6e255e0859..982b20381dc50 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -112,7 +112,7 @@ export const policies = handleActions( }; }, [setSelectedPolicy](state, { payload: selectedPolicy }) { - if (selectedPolicy === null) { + if (!selectedPolicy) { return { ...state, selectedPolicy: defaultPolicy, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/store.js b/x-pack/plugins/index_lifecycle_management/public/store/store.js index 355a834188f3f..024f8bcd81634 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/store.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/store.js @@ -8,10 +8,10 @@ import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; import { indexLifecycleManagement } from './reducers/'; -import { autoEnablePhase } from './middleware'; +import { autoEnablePhase, setSelectedPolicyFromSelectedTemplate } from './middleware'; export const indexLifecycleManagementStore = (initialState = {}) => { - const enhancers = [ applyMiddleware(thunk, autoEnablePhase) ]; + const enhancers = [ applyMiddleware(thunk, autoEnablePhase, setSelectedPolicyFromSelectedTemplate) ]; window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); return createStore( diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js index f50ede9e34af7..67c5865ba1a75 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js @@ -8,7 +8,6 @@ import { registerGetAffectedRoute } from '../register_get_affected_route'; jest.mock('../../../../lib/call_with_request_factory', () => { const mock = jest.fn().mockImplementation((method, params) => { - // console.log('hi', method, params); if (params.path === '/_template') { return { 'foobar': { From bb15a2687d804dc15ee99a0c1338d95a219f79ce Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Thu, 17 May 2018 11:40:20 -0400 Subject: [PATCH 013/102] Conditionally show this message --- .../components/review/review.container.js | 2 ++ .../wizard/components/review/review.js | 28 ++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js index 6c4ee3b274347..dbc2fcae7f535 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js @@ -20,6 +20,7 @@ import { getAliasName, getBootstrapEnabled, getIndexName, + getPolicies, } from '../../../../store/selectors'; import { setSelectedPolicyName, @@ -33,6 +34,7 @@ export const Review = connect( state => ({ selectedIndexTemplateName: getSelectedIndexTemplateName(state), affectedIndexTemplates: getAffectedIndexTemplates(state), + policies: getPolicies(state), templateDiff: getTemplateDiff(state), lifecycle: getLifecycle(state), bootstrapEnabled: getBootstrapEnabled(state), diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index 98a4ca16dc609..72e2f9319b61f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -79,7 +79,7 @@ export class Review extends Component { this.props.selectedPolicyName ); this.setState({ affectedIndices, isLoadingAffectedIndices: false }); - }, 500); + }, 1000); } async componentWillMount() { @@ -129,10 +129,14 @@ export class Review extends Component { lifecycle, bootstrapEnabled, aliasName, + policies, } = this.props; const { affectedIndices, isLoadingAffectedIndices, isShowingErrors } = this.state; + const showSaveChangedMessage = (originalPolicyName && !saveAsNewPolicy) + || (saveAsNewPolicy && !!policies.find(policy => policy.name === selectedPolicyName)); + return (
{/* @@ -217,18 +221,22 @@ export class Review extends Component { {originalPolicyName ? ( - -

Save changes to {originalPolicyName} policy

-
- -

- You are editing an existing policy. This means that any saves you make + { showSaveChangedMessage ? ( + + +

Save changes to {selectedPolicyName} policy

+
+ +

+ You are editing an existing policy. This means that any saves you make will also change any index templates this policy is attached to. You can instead save these changes and make it a brand new policy that only changes the template you selected. -

-
- +

+ + + + ) : null } Date: Fri, 18 May 2018 10:12:22 -0400 Subject: [PATCH 014/102] Policy selection cleanup --- .../policy_selection/policy_selection.container.js | 4 ++-- .../components/policy_selection/policy_selection.js | 8 ++++---- .../public/store/actions/index_template.js | 4 ++++ .../public/store/selectors/index_template.js | 8 -------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js index abe56ef4db032..618cd3a128bbc 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js @@ -10,7 +10,7 @@ import { connect } from 'react-redux'; import { PolicySelection as PresentationComponent } from './policy_selection'; import { - getPolicies, getExistingPolicyName, + getPolicies, getSelectedPolicyName, } from '../../../../store/selectors'; import { fetchPolicies, @@ -20,7 +20,7 @@ import { export const PolicySelection = connect( state => ({ policies: getPolicies(state), - existingPolicyName: getExistingPolicyName(state), + selectedPolicyName: getSelectedPolicyName(state), }), { fetchPolicies, diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index 2f5e71d8d0f42..c5d63fbd61fec 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -23,7 +23,7 @@ export class PolicySelection extends Component { // done: PropTypes.func.isRequired, // back: PropTypes.func.isRequired, - existingPolicyName: PropTypes.string.isRequired, + selectedPolicyName: PropTypes.string.isRequired, policies: PropTypes.array.isRequired }; @@ -39,7 +39,7 @@ export class PolicySelection extends Component { render() { - const { policies, existingPolicyName } = this.props; + const { policies, selectedPolicyName } = this.props; const options = policies.map(item => ({ value: item.name, text: item.name })); options.unshift({ @@ -62,7 +62,7 @@ export class PolicySelection extends Component { { await this.selectPolicy(e.target.value); }} @@ -101,7 +101,7 @@ export class PolicySelection extends Component {

{item.name} - {existingPolicyName === item.name ? '*' : ''} + {selectedPolicyName === item.name ? '*' : ''}

diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js index a7640e9cfb97f..c7bcd3518d036 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js @@ -18,6 +18,7 @@ import { setSelectedPrimaryShardCount, setSelectedReplicaCount, setSelectedNodeAttrs, + setSelectedPolicyName, } from '.'; import { PHASE_HOT, @@ -69,6 +70,9 @@ export const fetchIndexTemplate = templateName => async (dispatch) => { ) ); } + if (template.settings.index.lifecycle) { + dispatch(setSelectedPolicyName(template.settings.index.lifecycle.name)); + } } let indexPattern = template.index_patterns[0]; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index c99fb716e4761..9992c7491d368 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -57,14 +57,6 @@ export const getSelectedIndexTemplate = createSelector( export const getFullSelectedIndexTemplate = state => state.indexTemplate.fullSelectedIndexTemplate; -export const getExistingPolicyName = state => { - const template = getFullSelectedIndexTemplate(state); - if (template && template.settings && template.settings.index && template.settings.index.lifecycle) { - return template.settings.index.lifecycle.name; - } - return ''; -}; - export const getAlias = state => { const template = getSelectedIndexTemplate(state); if (template && template.settings) { From 2ad2735081b25269e13043506d0479792f36ec8c Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 18 May 2018 15:50:28 -0400 Subject: [PATCH 015/102] Updates for better UX --- .../components/warm_phase/warm_phase.js | 2 + .../store/middleware/auto_set_node_attrs.js | 34 ++++++++++++++ .../public/store/middleware/index.js | 1 + ..._selected_policy_from_selected_template.js | 18 +++++++- .../public/store/selectors/lifecycle.js | 45 ++++++++++++++++--- .../public/store/store.js | 18 ++++++-- 6 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 56418c4ea34f4..71aa73ea8bcaf 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -110,6 +110,8 @@ export class WarmPhase extends Component { hotPhaseRolloverEnabled, } = this.props; + console.log('warm', phaseData, phaseData[PHASE_NODE_ATTRS]); + return ( next => action => { + const state = store.getState(); + + if (action.type === setSelectedNodeAttrs().type) { + const warmPhaseAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); + if (!warmPhaseAttrs) { + store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, action.payload)); + } + } + else if (action.type === setPhaseData().type) { + const { phase, key, value } = action.payload; + + if (phase === PHASE_WARM && key === PHASE_NODE_ATTRS) { + const coldPhaseAttrs = getPhaseData(state, PHASE_COLD, PHASE_NODE_ATTRS); + if (!coldPhaseAttrs) { + store.dispatch(setPhaseData(PHASE_COLD, PHASE_NODE_ATTRS, value)); + } + } + } + + return next(action); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js index 1ec89365a7906..fcb0a960db4e2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js @@ -6,3 +6,4 @@ export { autoEnablePhase } from './auto_enable_phase'; export { setSelectedPolicyFromSelectedTemplate } from './set_selected_policy_from_selected_template'; +export { autoSetNodeAttrs } from './auto_set_node_attrs'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js index f9ab711e9f302..a87f9c3496d64 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js @@ -7,8 +7,11 @@ import { fetchedIndexTemplate, fetchPolicies, - setSelectedPolicy + setSelectedPolicy, + setPhaseData } from '../actions'; +import { getSelectedNodeAttrs, getPhaseData } from '../selectors'; +import { PHASE_WARM, PHASE_NODE_ATTRS, PHASE_COLD } from '../constants'; export const setSelectedPolicyFromSelectedTemplate = store => next => async action => { if (action.type === fetchedIndexTemplate().type) { @@ -18,6 +21,19 @@ export const setSelectedPolicyFromSelectedTemplate = store => next => async acti const selectedPolicy = policies.find(policy => policy.name === template.settings.index.lifecycle.name); if (selectedPolicy) { store.dispatch(setSelectedPolicy(selectedPolicy)); + + // We also want to update node attrs for future phases if they do not exist + const state = store.getState(); + const hotNodeAttrs = getSelectedNodeAttrs(state); + const warmNodeAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); + const coldNodeAttrs = getPhaseData(state, PHASE_COLD, PHASE_NODE_ATTRS); + + if (hotNodeAttrs && !warmNodeAttrs) { + store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, hotNodeAttrs)); + } + if ((hotNodeAttrs || warmNodeAttrs) && !coldNodeAttrs) { + store.dispatch(setPhaseData(PHASE_COLD, PHASE_NODE_ATTRS, warmNodeAttrs || hotNodeAttrs)); + } } } } diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index a34e94af5c6ab..af92c797b8df4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -27,7 +27,9 @@ import { PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, PHASE_PRIMARY_SHARD_COUNT, PHASE_SHRINK_ENABLED, - STRUCTURE_REVIEW + STRUCTURE_REVIEW, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS } from '../constants'; import { getPhase, @@ -84,6 +86,24 @@ export const validatePhase = (type, phase) => { } } + if (phase[PHASE_SHRINK_ENABLED]) { + if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + errors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; + } + else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { + errors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers above 0 are allowed.']; + } + } + + if (phase[PHASE_FORCE_MERGE_ENABLED]) { + if (!isNumber(phase[PHASE_FORCE_MERGE_SEGMENTS])) { + errors[PHASE_FORCE_MERGE_SEGMENTS] = ['A number is required.']; + } + else if (phase[PHASE_FORCE_MERGE_SEGMENTS] < 1) { + errors[PHASE_FORCE_MERGE_SEGMENTS] = ['Only positive numbers above 0 are allowed.']; + } + } + return errors; }; @@ -142,23 +162,38 @@ export const validateLifecycle = state => { // } // } + const hotPhase = getPhase(state, PHASE_HOT); + const warmPhase = getPhase(state, PHASE_WARM); + const coldPhase = getPhase(state, PHASE_COLD); + const deletePhase = getPhase(state, PHASE_DELETE); + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT], - ...validatePhase(PHASE_HOT, getPhase(state, PHASE_HOT)) + ...validatePhase(PHASE_HOT, hotPhase) }; errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM], - ...validatePhase(PHASE_WARM, getPhase(state, PHASE_WARM)) + ...validatePhase(PHASE_WARM, warmPhase) }; errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD], - ...validatePhase(PHASE_COLD, getPhase(state, PHASE_COLD)) + ...validatePhase(PHASE_COLD, coldPhase) }; errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE], - ...validatePhase(PHASE_DELETE, getPhase(state, PHASE_DELETE)) + ...validatePhase(PHASE_DELETE, deletePhase) }; + if (warmPhase[PHASE_SHRINK_ENABLED]) { + if (isNumber(warmPhase[PHASE_PRIMARY_SHARD_COUNT]) && warmPhase[PHASE_PRIMARY_SHARD_COUNT] > 0) { + if (getSelectedPrimaryShardCount(state) % warmPhase[PHASE_PRIMARY_SHARD_COUNT] !== 0) { + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM][PHASE_PRIMARY_SHARD_COUNT].push( + 'The shard count needs to be a divisor of the hot phase shard count.' + ); + } + } + } + return errors; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/store.js b/x-pack/plugins/index_lifecycle_management/public/store/store.js index 024f8bcd81634..2e3b5c219dad8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/store.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/store.js @@ -4,14 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createStore, applyMiddleware, compose } from 'redux'; +import { + createStore, + applyMiddleware, + compose +} from 'redux'; import thunk from 'redux-thunk'; -import { indexLifecycleManagement } from './reducers/'; -import { autoEnablePhase, setSelectedPolicyFromSelectedTemplate } from './middleware'; +import { + indexLifecycleManagement +} from './reducers/'; +import { + autoEnablePhase, + setSelectedPolicyFromSelectedTemplate, + autoSetNodeAttrs +} from './middleware'; export const indexLifecycleManagementStore = (initialState = {}) => { - const enhancers = [ applyMiddleware(thunk, autoEnablePhase, setSelectedPolicyFromSelectedTemplate) ]; + const enhancers = [applyMiddleware(thunk, autoEnablePhase, setSelectedPolicyFromSelectedTemplate, autoSetNodeAttrs)]; window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); return createStore( From 877d84934d22c0a990d954ec0cd8860a126c3289 Mon Sep 17 00:00:00 2001 From: gchaps Date: Mon, 21 May 2018 11:54:11 -0700 Subject: [PATCH 016/102] [COPYEDIT | ILM] Copyedit of text in index lifecycle management UI --- .../components/configuration/configuration.js | 8 +++--- .../template_selection/template_selection.js | 6 ++--- .../index_template/index_template.js | 2 +- .../components/cold_phase/cold_phase.js | 8 +++--- .../components/delete_phase/delete_phase.js | 4 +-- .../components/hot_phase/hot_phase.js | 11 ++++---- .../components/warm_phase/warm_phase.js | 19 +++++++------- .../policy_configuration.js | 6 ++--- .../wizard/components/review/review.js | 22 ++++++++-------- .../public/sections/wizard/wizard.js | 2 +- .../public/store/selectors/lifecycle.js | 26 +++++++++---------- 11 files changed, 57 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index 99a5899dd5efe..707bf4d9dcad2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -92,9 +92,9 @@ export class Configuration extends Component { return (
Configuration} + title={

Configure hot indices

} titleSize="s" - description="Indices are thought of as "hot" when they are actively being written to." + description="A hot index is actively being written to." fullWidth > - Learn more in our docs - . + Learn more +

} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 3d5777711a555..29a1430a4b17b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -68,7 +68,7 @@ export class TemplateSelection extends PureComponent { when you create an index.{' '} Learn more - . +

} > @@ -100,7 +100,7 @@ export class TemplateSelection extends PureComponent { {bootstrapEnabled ? (

- Your read-only index is queried less frequently. Use this phase - when the index no longer needs to be on the most performant hardware. + Your index is queried less frequently + and no longer needs to be on the most performant hardware.

{isShowingErrors ? ( -

This phase contains errors that need to be fixed.

+

This phase contains errors

) : null} @@ -206,7 +206,7 @@ export class ColdPhase extends PureComponent { setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) } > - Set same as warm phase + Set to same as warm phase diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 40974abb33fec..7c674116c0fa6 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -73,12 +73,12 @@ export class DeletePhase extends PureComponent { description={

- When your data is no longer useful. Define how long you want to retain it. + Use this phase to define how long to retain your data.

{isShowingErrors ? ( -

This phase contains errors that need to be fixed.

+

This phase contains errors

) : null} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 89d893dbc070f..2510e264f9395 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -77,12 +77,13 @@ export class HotPhase extends PureComponent { description={

- This phase is required. Your index is being queried and in active writing mode. + This phase is required. Your index is being queried and actively written to. + You can optimize this phase for write throughput.

{isShowingErrors ? ( -

This phase contains errors that need to be fixed.

+

This phase contains errors

) : null} @@ -94,9 +95,9 @@ export class HotPhase extends PureComponent { hasEmptyLabelSpace helpText={

- Setting this to true will rollover the index when it gets too big or too old. The alias will switch to the new index.{' '} + If true, rollover the index when it gets too big or too old. The alias switches to the new index.{' '} - Learn more. + Learn more

} @@ -116,7 +117,7 @@ export class HotPhase extends PureComponent {

- Your index is frequently queried, but is read-only. - Use this phase to optimize for search.Your index is being queried - and in active writing mode. + Your index becomes read-only when it enters the warm phase. + You can optimize this phase for search.

{isShowingErrors ? ( -

This phase contains errors that need to be fixed.

+

This phase contains errors

) : null} @@ -267,7 +266,7 @@ export class WarmPhase extends Component { validate(); }} > - Set same as hot phase + Set to same as hot phase
@@ -282,7 +281,7 @@ export class WarmPhase extends Component { Shrink the index into a new index with fewer primary shards.{' '} - Learn more. + Learn more @@ -295,7 +294,7 @@ export class WarmPhase extends Component { await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); validate(); }} - label="Enable shrink" + label="Shrink index" /> @@ -334,7 +333,7 @@ export class WarmPhase extends Component { validate(); }} > - Set same as hot phase + Set to same as hot phase @@ -349,10 +348,10 @@ export class WarmPhase extends Component { - Reduce the number of segments in your shard by and merging smaller + Reduce the number of segments in your shard by merging smaller files and clearing deleted ones.{' '} - Learn More + Learn more diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index 8cabbf599371e..eacadcba7fd6a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -72,7 +72,7 @@ export class PolicyConfiguration extends Component { if (await this.validate()) { this.props.done(); } else { - toastNotifications.addDanger('Please fix errors on the page.'); + toastNotifications.addDanger('Please the fix errors on the page'); } }; @@ -124,12 +124,12 @@ export class PolicyConfiguration extends Component {

- {!selectedPolicyName ? 'Create a new policy' : `Edit policy ${selectedPolicyName}`} + {!selectedPolicyName ? 'Create a policy' : `Edit policy ${selectedPolicyName}`}

-

Configure the phases of your data and when to transition between them. Only the hot phase is required.

+

Configure the phases of your data and when to transition between them.

Review your policy changes -

Be careful. Your changes will go into effect immediately once you save.

+

When you save a policy, your changes go into effect immediately.

-

{`${affectedIndexTemplates.length} Affected index ${affectedIndexTemplates.length === 1 ? 'template' : 'templates'}`}

+

{`${affectedIndexTemplates.length} Index ${affectedIndexTemplates.length === 1 ? 'template' : 'templates'}`}

    {affectedIndexTemplates.map(template => (
  • {template}
  • ))}
-

{`${affectedIndices.length} Affected ${affectedIndices.length === 1 ? 'Index' : 'Indices' }`}

+

{`${affectedIndices.length} ${affectedIndices.length === 1 ? 'Index' : 'Indices' }`}

{ isLoadingAffectedIndices ? ( ) : ( @@ -177,10 +177,10 @@ export class Review extends Component { -

Since you decided to bootstrap a new index you'll want to point to a new alias going forward

+

You decided to bootstrap a new index. Point to this new alias going forward.

{aliasName} is your new alias

@@ -224,13 +224,13 @@ export class Review extends Component { { showSaveChangedMessage ? ( -

Save changes to {selectedPolicyName} policy

+

Save changes to {selectedPolicyName} policy?

- You are editing an existing policy. This means that any saves you make - will also change any index templates this policy is attached to. You can instead save - these changes and make it a brand new policy that only changes the template you + You are editing an existing policy. Any changes you make + will also change index templates that this policy is attached to. Alternately, you can save + these changes in a new policy and only change the template you selected.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index 3a7ba64893b23..757326c6ed1d2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -144,7 +144,7 @@ export class Wizard extends Component { // onClick: () => this.onSelectedStepChanged(2), // }, { - title: 'Configure policy', + title: 'Configure a policy', isSelected: this.state.selectedStep === 2, isComplete: this.state.selectedStep > 2, disabled: this.state.selectedStep < 2, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index a34e94af5c6ab..0b3b97f7d83b9 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -58,10 +58,10 @@ export const validatePhase = (type, phase) => { !isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED]) ) { errors[PHASE_ROLLOVER_MAX_AGE] = [ - 'A rollover requires a max age, max docs, or max size.' + 'A maximum age is required' ]; errors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ - 'A rollover requires a max age, max docs, or max size.' + 'A maximum index size is required' ]; } } @@ -73,13 +73,13 @@ export const validatePhase = (type, phase) => { continue; } if (!isNumber(phase[numberedAttribute])) { - errors[numberedAttribute] = ['A number is required.']; + errors[numberedAttribute] = ['A number is required']; } else if (phase[numberedAttribute] < 0) { - errors[numberedAttribute] = ['Only positive numbers allowed.']; + errors[numberedAttribute] = ['Only positive numbers are allowed']; } else if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && phase[numberedAttribute] < 1) { - errors[numberedAttribute] = ['Only positive numbers above 0 are allowed.']; + errors[numberedAttribute] = ['Only positive numbers are allowed']; } } } @@ -98,41 +98,41 @@ export const validateLifecycle = state => { } if (getBootstrapEnabled(state) && !getIndexName(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_INDEX_NAME].push('An index name is required.'); + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_INDEX_NAME].push('An index name is required'); } if (getBootstrapEnabled(state) && !getAliasName(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('An alias name is required.'); + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('An alias name is required'); } if (!isNumber(getSelectedPrimaryShardCount(state))) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ STRUCTURE_PRIMARY_NODES - ].push('A value is required.'); + ].push('A value is required'); } else if (getSelectedPrimaryShardCount(state) < 1) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ STRUCTURE_PRIMARY_NODES - ].push('Only positive numbers above 0 are allowed.'); + ].push('Only positive numbers are allowed'); } if (!isNumber(getSelectedReplicaCount(state))) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ STRUCTURE_REPLICAS - ].push('A value is required.'); + ].push('A value is required'); } else if (getSelectedReplicaCount(state) < 0) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ STRUCTURE_REPLICAS - ].push('Only positive numbers allowed.'); + ].push('Only positive numbers are allowed'); } if (!getSelectedPolicyName(state)) { - errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('A policy name is required.'); + errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('A policy name is required'); } if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { - errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('The policy name must be different.'); + errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('The policy name must be different'); } // if (getSaveAsNewPolicy(state)) { From 374c6c4f9d413a136d084896df10be908138682a Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Tue, 29 May 2018 10:34:14 -0400 Subject: [PATCH 017/102] Use better default text --- .../index_lifecycle_management/public/store/selectors/nodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js index ca0d06f9f2dde..4a65cf969bdb5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js @@ -21,7 +21,7 @@ export const getNodeOptions = createSelector( options.sort((a, b) => a.value.localeCompare(b.value)); options.unshift({ - text: '', + text: '-- Do not reallocate my indices --', value: undefined, }); From f81799059b774e60b0d030f0815aeacc3fe19695 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 6 Jun 2018 09:41:34 -0400 Subject: [PATCH 018/102] Remove debug --- .../policy_configuration/components/warm_phase/warm_phase.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 55edac604eafd..5af74cf85e57f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -110,8 +110,6 @@ export class WarmPhase extends Component { hotPhaseRolloverEnabled, } = this.props; - console.log('warm', phaseData, phaseData[PHASE_NODE_ATTRS]); - return ( Date: Mon, 11 Jun 2018 13:15:36 -0400 Subject: [PATCH 019/102] Adding readme and comments --- .../index_lifecycle_management/README.md | 70 +++++++++++++++++++ .../public/lib/diff_ace_addons.js | 23 ++++++ .../public/lib/diff_tools.js | 48 +++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 x-pack/plugins/index_lifecycle_management/README.md diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md new file mode 100644 index 0000000000000..304a2f970a6d9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -0,0 +1,70 @@ +# Index lifecyle management + +## What is it +-- TODO -- + +## UI + +The UI currently consists of a single wizard, broken into three steps. + +### Step 1 +The first step involves choosing the index template in which the created/selected policy will be applied. +Then, it lets the user tweak configuration options on this template including shard and replica count as well as allocation rules. + +### Step 2 +The second step lets the user choose which policy they want to apply to the selected index template. They can choose a new one or select an existing one. Either way, after selection, they will see configuration options for the policy itself. This includes configuration for the hot, warm, cold, and delete phase. + +### Step 3 +The third and last step lets the user name their policy and also see the affected indices and index templates. These indices and index templates are what will be affected once the user saves the work done in the wizard (This includes changes to the index template itself which will change indices created from the template and also changes to a policy that is attached to another index template). The user can also see a visual diff of what will change in the index template. Then, the user clicks the Save button and blamo! + +## UI Architecture + +The UI is built on React and Redux. + +### Redux + +The redux store consists of a few top level attributes: +``` +indexTemplate +nodes +policies +general +``` + +The logic behind the store is separate into four main concerns: +1) reducers/ +2) actions/ +3) selectors/ +4) middleware/ + +The reducers and actions are pretty standard redux, so no need to discuss much there. + +### Selectors + +The selectors showcase how we access any stateful data. All access comes through selectors so if there are any changes required to the state tree, we only need to update the reducers and selectors. + +#### Middleware + +The middleware folder contains specific pieces of state logic we need to handle side effects of certain state changing. + +One example is the `auto_enable_phase.js` middleware. By default, the warm, cold and delete phases are disabled. However, the user can expand the section in the UI and edit configuration without needing to enable/disable the phase. Ideally, once the user edits any configuration piece within a phase, we _assume_ they want that phase enabled so this middleware will detect a change in a phase and enable if it is not enabled already. + +#### Generic phase data + +Each of our four phases have some similar and some unique configuration options. Instead of making each individual phase a specific action for that phase, the code is written more generically to capture any data change within a phase to a single action. Therefore, each phase component's configuration inputs will look similar, like: `setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value)`. The top level container for each phase will handle automatically prefixing the `setPhaseData` prop with the right phase: ` setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value),` + +### Diff View + +The third step of the wizard features a visual diff UI component which is custom to this feature. It is based off Ace/Brace and the custom code used to power is it lives in `lib/diff_ace_addons.js` and `lib/diff_tools.js`. The UI parts are in `sections/wizard/components/review/diff_view.js`. See those individual files for more detailed comments/explanations. + +### Validation + +Every step in the wizard features validation and will show error states after the user attempts to move to the next step assuming there are errors on the current page. + +This works by constantly revalidating the entire wizard state after each state change. This is technically optional as the method to trigger validation is manually called in each UI component that triggers a state change. + +It's important to note that the validation logic does not apply to a single step in the wizard, but will always validate the entire state tree. This helps prevent scenarios where a change in a step might invalidate a change in another step and we lose that validation state. + +Once a step change is initiated (like clicking Next Step), the current step is marked as able to see errors and will reject the change if there are errors. It will show a toast to the user that there are errors and make each error visible on the relevant UI control. + +As a way to consolidate showing these errors, there is a custom UI component called `ErrableFormRow` that wraps a `EuiFormRow` and it's child with the appropriate error states when appropriate. diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js index 96d3f1cc6849f..82c6e6b4d1e39 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js @@ -22,6 +22,16 @@ function findInObject(key, obj, debug) { return false; } +/** + * Utilty method that will determine if the current key/value pair + * is an addition or removal and return the appropriate ace classes + * for styling. This is called after finding a valid key/value match + * in our custom JSON diff mode for ace. + * + * @param {string} key + * @param {string} val + * @param {object} jsonObject + */ function getDiffClasses(key, val, jsonObject) { let value = val; if (value.endsWith(',')) { @@ -57,6 +67,14 @@ let currentJsonObject; const getCurrentJsonObject = () => currentJsonObject; export const setCurrentJsonObject = jsonObject => currentJsonObject = jsonObject; +/** + * This function will update the ace editor to support a `DiffJsonMode` that will + * show a merged object (merged through `diff_tools:mergeAndPreserveDuplicateKeys`) + * and highlight additions and removals. The goal of this from a UI perspective is + * to help the user see a visual result of merging two javascript objects. + * + * Read this as a starter: https://github.com/ajaxorg/ace/wiki/Creating-or-Extending-an-Edit-Mode + */ export const addDiffAddonsForAce = () => { const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules') .JsonHighlightRules; @@ -72,6 +90,11 @@ export const addDiffAddonsForAce = () => { token: (key, val) => { return getDiffClasses(key, val, getCurrentJsonObject()); }, + // This is designed to match a key:value pair represented in JSON + // like: + // "foo": "bar" + // Be aware when tweaking this that there are idiosyncracies with + // how these work internally in ace. regex: '(?:"([\\w-+]+)"\\s*:\\s*([^\\n\\[]+)$)', }, { diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js index ca2b113c83086..c06835261c759 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js @@ -6,6 +6,13 @@ export const ADDITION_PREFIX = '$$$'; export const REMOVAL_PREFIX = '^^^'; + +/** + * Utility method that will properly escape the prefixes to use in a valid + * RegExp + * + * @param {string} prefix + */ const escapePrefix = prefix => prefix .split('') @@ -19,6 +26,15 @@ const removePrefixRegex = new RegExp( export const isBoolean = value => JSON.parse(value) === true || JSON.parse(value) === false; const isObject = value => typeof value === 'object' && !Array.isArray(value); + +/** + * Utility method that will determine if the key/value pair provided is different + * than the value found using the key in the provided obj. + * + * @param {object} obj + * @param {string} key + * @param {object} value + */ const isDifferent = (obj, key, value) => { // If the object does not contain the key, then ignore since it's not a removal or addition if (!obj.hasOwnProperty(key)) { @@ -39,6 +55,14 @@ const isDifferent = (obj, key, value) => { // We should be dealing with primitives so do a basic comparison return obj[key] !== value; }; + +/** + * This utility method is called when an object exists in the target object + * but not in the source and we want to mark each part of the object as an + * addition + * + * @param {*} obj + */ const getAdditions = obj => { const result = {}; for (const [key, value] of Object.entries(obj)) { @@ -51,6 +75,12 @@ const getAdditions = obj => { return result; }; +/** + * This method is designed to remove all prefixes from the object previously added + * by `mergeAndPreserveDuplicateKeys` + * + * @param {object} obj + */ export const removePrefixes = obj => { if (typeof obj === 'string') { return obj.replace(removePrefixRegex, ''); @@ -70,6 +100,13 @@ export const removePrefixes = obj => { ); }; +/** + * This function is designed to recursively remove any prefixes added through the + * `mergeAndPreserveDuplicateKeys` process. + * + * @param {string} key + * @param {object} value + */ const normalizeChange = (key, value) => { if (typeof value === 'string') { return { @@ -88,6 +125,17 @@ const normalizeChange = (key, value) => { }, {}); }; +/** + * This function is designed to merge two objects together, but instead of + * overriding key collisions, it will create two keys for each collision - the key + * from the source object will start with the `REMOVAL_PREFIX` and the key from the + * target object will start with the `ADDITION_PREFIX`. The resulting object from + * this function call will contain the merged object and potentially some + * `REMOVAL_PREFIX` and `ADDITION_PREFIX` keys. + * + * @param {object} source + * @param {object} target + */ export const mergeAndPreserveDuplicateKeys = ( source, target, From 475540a65331425ceac688a8c18fe5ca7af1981c Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 11 Jun 2018 15:49:34 -0400 Subject: [PATCH 020/102] Update readme --- x-pack/plugins/index_lifecycle_management/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md index 304a2f970a6d9..14297875d177c 100644 --- a/x-pack/plugins/index_lifecycle_management/README.md +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -51,7 +51,9 @@ One example is the `auto_enable_phase.js` middleware. By default, the warm, cold #### Generic phase data -Each of our four phases have some similar and some unique configuration options. Instead of making each individual phase a specific action for that phase, the code is written more generically to capture any data change within a phase to a single action. Therefore, each phase component's configuration inputs will look similar, like: `setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value)`. The top level container for each phase will handle automatically prefixing the `setPhaseData` prop with the right phase: ` setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value),` +Each of our four phases have some similar and some unique configuration options. Instead of making each individual phase a specific action for that phase, the code is written more generically to capture any data change within a phase to a single action. Therefore, each phase component's configuration inputs will look similar, like: `setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value)`. The top level container for each phase will handle automatically prefixing the `setPhaseData` prop with the right phase: ` setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value),`. + +To complement this generic logic, there is a list of constants that are used to ensure the right pieces of data are changed. These are contained within `store/constants.js` ### Diff View From 36e001dbe168ad0331d892a069a9c47318cf7b71 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 11 Jun 2018 15:58:23 -0400 Subject: [PATCH 021/102] Do not need this anymore --- x-pack/plugins/index_lifecycle_management/TODO.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/TODO.md diff --git a/x-pack/plugins/index_lifecycle_management/TODO.md b/x-pack/plugins/index_lifecycle_management/TODO.md deleted file mode 100644 index 3d39db9451560..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ -- PUT to override does not work, is there a PATCH or something? -- Rename PRIMARY_NODES to PRIMARY_SHARDS per configuration.js label change -- Policy type in the UI anywhere? -- Diff editor to EUI -- Index management changes to see status -- CRUD UIs for managing policies and index templates \ No newline at end of file From 934f4fb1b1e57d0c4f4b0f9c326249f20f9b0c6b Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 11 Jun 2018 16:00:43 -0400 Subject: [PATCH 022/102] Remove debug or commented out code --- .../policy_configuration.js | 104 ------------------ .../policy_selection/policy_selection.js | 44 -------- .../wizard/components/review/review.js | 24 ---- .../public/sections/wizard/diff.json | 33 ------ .../public/sections/wizard/wizard.js | 22 ---- 5 files changed, 227 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index eacadcba7fd6a..a2ed77bd87f3b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -97,27 +97,6 @@ export class PolicyConfiguration extends Component { ); } - // const singleTemplate = ( - // - // This policy is only attached to the selected template{' '} - // {selectedIndexTemplateName}. - // - // ); - - // const multiTemplate = ( - // - // This policy is attached to{' '} - // {affectedIndexTemplates.length - 1} other template(s){' '} - // besides {selectedIndexTemplateName}. - // - // ); - - // const warningMessage = - // affectedIndexTemplates.length === 1 && - // affectedIndexTemplates[0] === selectedIndexTemplateName - // ? singleTemplate - // : multiTemplate; - return (
@@ -159,89 +138,6 @@ export class PolicyConfiguration extends Component { /> - {/* - - - - {originalPolicyName ? ( - - { - await setSaveAsNewPolicy(e.target.checked); - validate(); - }} - label={ - - Save this as a new policy so it does not - effect other templates. - - } - /> - - ) : null} - {saveAsNewPolicy ? ( - - { - await setSelectedPolicyName(e.target.value); - validate(); - }} - /> - - ) : null} - - - setBootstrapEnabled(e.target.checked)} - label={Create an index and alias for this template} - /> - - {bootstrapEnabled ? ( - - - { - await setIndexName(e.target.value); - validate(); - }} - /> - - - { - await setAliasName(e.target.value); - validate(); - }} - /> - - - ) : null} - */} - - {/* */} - policy.name === policyName); this.props.setSelectedPolicy(policy); - // this.props.done(); } - render() { const { policies, selectedPolicyName } = this.props; @@ -79,46 +75,6 @@ export class PolicySelection extends Component { - {/* - - - this.selectPolicy(null)} - > - - -

New policy

-
-
-
- {policies.map(item => ( - - this.selectPolicy(item)} - > - -

- {item.name} - {selectedPolicyName === item.name ? '*' : ''} -

-
-
-
- ))} -
- */} - - {/* */} - - {/* - Back - */} ); } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index 87d538ac08b80..4119c6ef1a0ee 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -8,7 +8,6 @@ import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; import { toastNotifications } from 'ui/notify'; -// import DiffEditor from 'react-ace/lib/diff'; import './review.less'; import 'brace/theme/github'; @@ -111,8 +110,6 @@ export class Review extends Component { const { done, back, - - /* Start might move */ setSelectedPolicyName, setSaveAsNewPolicy, validate, @@ -121,8 +118,6 @@ export class Review extends Component { selectedPolicyName, saveAsNewPolicy, originalPolicyName, - /* End might move */ - selectedIndexTemplateName, affectedIndexTemplates, templateDiff, @@ -139,10 +134,6 @@ export class Review extends Component { return (
- {/* -

Changes that will occur

-
- */}

Review your policy changes

@@ -198,21 +189,6 @@ export class Review extends Component { - {/* */} - ) : null} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json deleted file mode 100644 index 9ce2e9b307baa..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/diff.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "originalFullIndexTemplate": { - "settings": { - "index": { - "number_of_shards": "1", - "auto_expand_replicas": "0-1", - "lifecycle": { - "name": "my_policy" - } - } - } - }, - "newFullIndexTemplate": { - "settings": { - "index": { - "number_of_shards": "1", - "auto_expand_replicas": "0-1", - "number_of_replicas": "1", - "lifecycle": { - "name": "my_policy4" - }, - "routing": { - "allocation": { - "include": { - "sattr_name": "node" - } - } - } - } - } - }, - "hasChanged": true -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index 757326c6ed1d2..ab92bc815bd2f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -8,7 +8,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { toastNotifications } from 'ui/notify'; import { IndexTemplate } from './components/index_template'; -// import { PolicySelection } from './components/policy_selection'; import { PolicyConfiguration } from './components/policy_configuration'; import { Review } from './components/review'; import { @@ -25,8 +24,6 @@ import { STRUCTURE_POLICY_CONFIGURATION, STRUCTURE_REVIEW, } from '../../store/constants'; -// import { DiffView } from './components/review/diff_view'; -// import diff from './diff.json'; export class Wizard extends Component { static propTypes = { @@ -100,13 +97,6 @@ export class Wizard extends Component { done={() => this.onSelectedStepChanged(2)} /> ); - // case 2: - // return ( - // this.onSelectedStepChanged(3)} - // back={() => this.onSelectedStepChanged(1)} - // /> - // ); case 2: return ( 1, onClick: () => this.onSelectedStepChanged(1), }, - // { - // title: 'Select or create policy', - // isSelected: this.state.selectedStep === 2, - // isComplete: this.state.selectedStep > 2, - // disabled: this.state.selectedStep < 2, - // onClick: () => this.onSelectedStepChanged(2), - // }, { title: 'Configure a policy', isSelected: this.state.selectedStep === 2, @@ -159,8 +142,6 @@ export class Wizard extends Component { }, ]; - // const templateDiff = diff; - return ( @@ -172,9 +153,6 @@ export class Wizard extends Component { - {/* */} {this.renderContent()} From 910f3ebf273597b246c23ab667ebc46c1c6db862 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Tue, 12 Jun 2018 09:34:45 -0400 Subject: [PATCH 023/102] Remove these - they are in the tests PR --- .../indices/__tests__/bootstrap_route.test.js | 56 -------------- .../__tests__/get_affected_route.test.js | 77 ------------------- 2 files changed, 133 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js delete mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js deleted file mode 100644 index 5bc48358cd187..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/bootstrap_route.test.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { registerBootstrapRoute } from '../register_bootstrap_route'; - -jest.mock('../../../../lib/call_with_request_factory', () => { - const mock = jest.fn(); - return { - callWithRequestFactory: () => mock, - }; -}); - -jest.mock('../../../../lib/is_es_error_factory', () => ({ - isEsErrorFactory: jest.fn().mockImplementation(() => jest.fn()), -})); - -jest.mock('../../../../lib/license_pre_routing_factory', () => ({ - licensePreRoutingFactory: jest.fn().mockImplementation(() => jest.fn()), -})); - -let routeHandler; -const mockServer = { - route: options => { - routeHandler = options.handler; - } -}; - -describe('ilmBootstrapRoute', () => { - it('should call indices.create', async () => { - registerBootstrapRoute(mockServer); - - await routeHandler({ payload: { - indexName: 'myIndex', - aliasName: 'myAlias', - } }, jest.fn()); - - const mock = require('../../../../lib/call_with_request_factory').callWithRequestFactory().mock; - - expect(mock.calls.length).toBe(1); - expect(mock.calls[0]).toEqual([ - 'indices.create', - { - index: 'myIndex', - aliases: { - myAlias: {} - }, - settings: { - 'index.lifecycle.rollover_alias': 'myAlias' - } - } - ]); - }); -}); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js deleted file mode 100644 index 67c5865ba1a75..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/__tests__/get_affected_route.test.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { registerGetAffectedRoute } from '../register_get_affected_route'; - -jest.mock('../../../../lib/call_with_request_factory', () => { - const mock = jest.fn().mockImplementation((method, params) => { - if (params.path === '/_template') { - return { - 'foobar': { - index_patterns: ['foobar*'] - }, - 'barfoo': { - index_patterns: ['barfoo*'], - settings: { - index: { - lifecycle: { - name: 'myPolicy' - } - } - } - } - }; - } - }); - return { - callWithRequestFactory: () => mock, - }; -}); - -jest.mock('../../../../lib/is_es_error_factory', () => ({ - isEsErrorFactory: jest.fn().mockImplementation(() => jest.fn()), -})); - -jest.mock('../../../../lib/license_pre_routing_factory', () => ({ - licensePreRoutingFactory: jest.fn().mockImplementation(() => jest.fn()), -})); - -const routeHandlers = []; -const mockServer = { - route: options => { - routeHandlers.push(options.handler); - } -}; - -describe('ilmGetAffectedRoute', () => { - it('should call indices.create', async () => { - registerGetAffectedRoute(mockServer); - - - for (const routeHandler of routeHandlers) { - await routeHandler({ params: { - indexTemplateName: 'foobar', - policyName: 'myPolicy' - } }, jest.fn()); - } - - const mock = require('../../../../lib/call_with_request_factory').callWithRequestFactory().mock; - - expect(mock.calls.length).toBe(4); - expect(mock.calls[1]).toEqual([ - 'indices.get', - { - index: ['foobar*'] - } - ]); - expect(mock.calls[3]).toEqual([ - 'indices.get', - { - index: ['foobar*', 'barfoo*'] - } - ]); - }); -}); From 99aef825a25573b417495baef590b8602041fb9f Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 15 Jun 2018 14:08:48 -0400 Subject: [PATCH 024/102] Toggle system indices --- .../template_selection/template_selection.js | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 29a1430a4b17b..9614e64414ed5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, PureComponent } from 'react'; +import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; import { @@ -14,6 +14,7 @@ import { EuiFieldText, EuiDescribedFormGroup, EuiLink, + EuiSpacer, } from '@elastic/eui'; import { ErrableFormRow } from '../../../../form_errors'; @@ -23,7 +24,7 @@ import { STRUCTURE_ALIAS_NAME, } from '../../../../../../store/constants'; -export class TemplateSelection extends PureComponent { +export class TemplateSelection extends Component { static propTypes = { fetchIndexTemplates: PropTypes.func.isRequired, setSelectedIndexTemplate: PropTypes.func.isRequired, @@ -35,10 +36,21 @@ export class TemplateSelection extends PureComponent { isShowingErrors: PropTypes.bool.isRequired, }; + constructor(props) { + super(props); + this.state = { + isIncludingSystemIndices: false, + }; + } + componentWillMount() { this.props.fetchIndexTemplates(); } + onChangeIncludingSystemIndices = e => { + this.setState({ isIncludingSystemIndices: e.target.checked }); + }; + render() { const { setSelectedIndexTemplate, @@ -48,7 +60,6 @@ export class TemplateSelection extends PureComponent { setAliasName, bootstrapEnabled, - templateOptions, selectedIndexTemplateIndices, indexName, aliasName, @@ -57,6 +68,15 @@ export class TemplateSelection extends PureComponent { isShowingErrors, } = this.props; + const { isIncludingSystemIndices } = this.state; + + const templateOptions = this.props.templateOptions.filter(option => { + if (option.value && option.value.startsWith('.') && !isIncludingSystemIndices) { + return false; + } + return true; + }); + return ( Select a template} @@ -72,6 +92,12 @@ export class TemplateSelection extends PureComponent {

} > + + Date: Mon, 18 Jun 2018 15:59:00 -0400 Subject: [PATCH 025/102] Aliases are not defined here anymore --- .../public/store/selectors/policies.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 94439f27c72a2..805a0818f0b61 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -31,7 +31,6 @@ import { PHASE_PRIMARY_SHARD_COUNT, PHASE_REPLICA_COUNT, PHASE_ENABLED, - PHASE_ROLLOVER_ALIAS, PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, MAX_SIZE_TYPE_DOCUMENT } from '../constants'; @@ -182,9 +181,7 @@ export const phaseToES = (state, phase) => { esPhase.actions = {}; if (phase[PHASE_ROLLOVER_ENABLED]) { - esPhase.actions.rollover = { - alias: phase[PHASE_ROLLOVER_ALIAS], - }; + esPhase.actions.rollover = {}; if (isNumber(phase[PHASE_ROLLOVER_MAX_AGE])) { esPhase.actions.rollover.max_age = `${phase[PHASE_ROLLOVER_MAX_AGE]}${ From db39544bb848f7815d842453693143eedbae5fc1 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 18 Jun 2018 16:42:48 -0400 Subject: [PATCH 026/102] Handle rollover better in warm phase and remove from cold,delete --- .../components/cold_phase/cold_phase.js | 2 -- .../components/delete_phase/delete_phase.js | 2 -- .../components/warm_phase/warm_phase.js | 17 ++++---------- .../public/store/selectors/policies.js | 23 +++++++++++-------- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 7a0da69affcb6..dc55d36b4d067 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -23,7 +23,6 @@ import { } from '@elastic/eui'; import { PHASE_ENABLED, - PHASE_ROLLOVER_ENABLED, PHASE_ROLLOVER_ALIAS, PHASE_ROLLOVER_AFTER, PHASE_ROLLOVER_AFTER_UNITS, @@ -42,7 +41,6 @@ export class ColdPhase extends PureComponent { errors: PropTypes.object.isRequired, phaseData: PropTypes.shape({ [PHASE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_ENABLED]: PropTypes.bool.isRequired, [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ PropTypes.number, diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 7c674116c0fa6..8d9e179dc99fa 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -26,7 +26,6 @@ import { } from '@elastic/eui'; import { PHASE_ENABLED, - PHASE_ROLLOVER_ENABLED, PHASE_ROLLOVER_AFTER, PHASE_ROLLOVER_AFTER_UNITS, } from '../../../../../../store/constants'; @@ -41,7 +40,6 @@ export class DeletePhase extends PureComponent { errors: PropTypes.object.isRequired, phaseData: PropTypes.shape({ [PHASE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_ENABLED]: PropTypes.bool.isRequired, [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ PropTypes.number, PropTypes.string diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 5af74cf85e57f..a31f58ec30352 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, Component } from 'react'; +import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; import { @@ -39,7 +39,7 @@ import { } from '../../../../../../store/constants'; import { ErrableFormRow } from '../../../../form_errors'; -export class WarmPhase extends Component { +export class WarmPhase extends PureComponent { static propTypes = { setPhaseData: PropTypes.func.isRequired, validate: PropTypes.func.isRequired, @@ -84,13 +84,6 @@ export class WarmPhase extends Component { nodeOptions: PropTypes.array.isRequired, }; - constructor(props) { - super(props); - this.state = { - applyOnRollover: false, - }; - } - componentWillMount() { this.props.fetchNodes(); } @@ -160,15 +153,15 @@ export class WarmPhase extends Component { { - await this.setState({ applyOnRollover: e.target.checked }); + await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); validate(); }} /> ) : null} - {!this.state.applyOnRollover ? ( + {!phaseData[PHASE_ROLLOVER_ENABLED] ? ( { export const isNumber = value => typeof value === 'number'; -export const phaseFromES = (phase, defaultPolicy) => { +export const phaseFromES = (phase, phaseName, defaultPolicy) => { const policy = { ...defaultPolicy }; if (!phase) { @@ -91,13 +91,20 @@ export const phaseFromES = (phase, defaultPolicy) => { } policy[PHASE_ENABLED] = true; + policy[PHASE_ROLLOVER_ENABLED] = false; if (phase.after) { const { size: after, units: afterUnits } = splitSizeAndUnits( phase.after ); - policy[PHASE_ROLLOVER_AFTER] = after; - policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + // If the after is set to 0s, it effectively means we are moving + // to the warm phase after rollover from the hot phase + if (phaseName === PHASE_WARM && after === 0) { + policy[PHASE_ROLLOVER_ENABLED] = true; + } else { + policy[PHASE_ROLLOVER_AFTER] = after; + policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + } } if (phase.actions) { @@ -124,8 +131,6 @@ export const phaseFromES = (phase, defaultPolicy) => { policy[PHASE_ROLLOVER_MAX_SIZE_STORED] = rollover.max_docs; policy[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] = MAX_SIZE_TYPE_DOCUMENT; } - } else { - policy[PHASE_ROLLOVER_ENABLED] = false; } if (actions.allocate) { @@ -159,10 +164,10 @@ export const policyFromES = ({ name, type, phases }) => { name, type, phases: { - [PHASE_HOT]: phaseFromES(phases[PHASE_HOT], defaultHotPhase), - [PHASE_WARM]: phaseFromES(phases[PHASE_WARM], defaultWarmPhase), - [PHASE_COLD]: phaseFromES(phases[PHASE_COLD], defaultColdPhase), - [PHASE_DELETE]: phaseFromES(phases[PHASE_DELETE], defaultDeletePhase) + [PHASE_HOT]: phaseFromES(phases[PHASE_HOT], PHASE_HOT, defaultHotPhase), + [PHASE_WARM]: phaseFromES(phases[PHASE_WARM], PHASE_WARM, defaultWarmPhase), + [PHASE_COLD]: phaseFromES(phases[PHASE_COLD], PHASE_COLD, defaultColdPhase), + [PHASE_DELETE]: phaseFromES(phases[PHASE_DELETE], PHASE_DELETE, defaultDeletePhase) } }; }; From ec2dc4e49efc23a24208516ef458865f175cef01 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 1 Aug 2018 11:02:50 -0400 Subject: [PATCH 027/102] adding learn more link component and switching over to using that --- .../public/components/learn_more_link.js | 30 +++++++++++++++++++ .../components/configuration/configuration.js | 8 ++--- .../template_selection/template_selection.js | 8 ++--- .../components/hot_phase/hot_phase.js | 8 ++--- .../components/warm_phase/warm_phase.js | 13 ++++---- 5 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js diff --git a/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js b/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js new file mode 100644 index 0000000000000..17f6e5e2d1661 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js @@ -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 React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; + +const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`; + + +export class LearnMoreLink extends React.PureComponent { + render() { + const { href, docPath } = this.props; + let url; + if (docPath) { + url = `${esBase}${docPath}`; + } else { + url = href; + } + return ( + + Learn more + + ); + + } +} \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index 707bf4d9dcad2..b107cfae9043b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -15,9 +15,9 @@ import { EuiFieldNumber, EuiCallOut, EuiButtonEmpty, - EuiLink, EuiDescribedFormGroup, } from '@elastic/eui'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; import { STRUCTURE_NODE_ATTRS, STRUCTURE_PRIMARY_NODES, @@ -130,9 +130,9 @@ export class Configuration extends Component {

The best way to determine how many shards you need is to benchmark using realistic data and queries on your hardware.{' '} - - Learn more - +

} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 9614e64414ed5..ccfc199a83540 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -13,10 +13,10 @@ import { EuiSwitch, EuiFieldText, EuiDescribedFormGroup, - EuiLink, EuiSpacer, } from '@elastic/eui'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; import { ErrableFormRow } from '../../../../form_errors'; import { STRUCTURE_TEMPLATE_NAME, @@ -86,9 +86,9 @@ export class TemplateSelection extends Component {

An index template defines the settings, mappings, and aliases to apply when you create an index.{' '} - - Learn more - +

} > diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 2510e264f9395..04ba438f9b671 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -16,11 +16,11 @@ import { EuiFieldNumber, EuiSelect, EuiSwitch, - EuiLink, EuiFormRow, EuiDescribedFormGroup, EuiBetaBadge, } from '@elastic/eui'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; import { PHASE_ROLLOVER_ALIAS, PHASE_ROLLOVER_MAX_AGE, @@ -96,9 +96,9 @@ export class HotPhase extends PureComponent { helpText={

If true, rollover the index when it gets too big or too old. The alias switches to the new index.{' '} - - Learn more - +

} > diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index a31f58ec30352..bd46a44907b83 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -6,7 +6,6 @@ import React, { Fragment, PureComponent } from 'react'; import PropTypes from 'prop-types'; - import { EuiFlexGroup, EuiFlexItem, @@ -19,7 +18,6 @@ import { EuiSelect, EuiSwitch, EuiButtonEmpty, - EuiLink, EuiDescribedFormGroup, EuiBetaBadge, EuiButton, @@ -38,6 +36,7 @@ import { PHASE_SHRINK_ENABLED, } from '../../../../../../store/constants'; import { ErrableFormRow } from '../../../../form_errors'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; export class WarmPhase extends PureComponent { static propTypes = { @@ -273,9 +272,9 @@ export class WarmPhase extends PureComponent { Shrink the index into a new index with fewer primary shards.{' '} - - Learn more - + @@ -343,9 +342,7 @@ export class WarmPhase extends PureComponent { Reduce the number of segments in your shard by merging smaller files and clearing deleted ones.{' '} - - Learn more - +
From 77f65e490b3c25674699ba622634847f80f01e69 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 1 Aug 2018 13:46:01 -0400 Subject: [PATCH 028/102] fixing UI issue when no policies exist --- .../server/routes/api/policies/register_fetch_route.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js index 6ec6745e80b21..0471dbc717bcd 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -10,6 +10,9 @@ import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; function formatHits(hits) { + if (hits.status === 404) { + return []; + } return Object.keys(hits).reduce((accum, lifecycleName) => { const hit = hits[lifecycleName]; accum.push({ @@ -24,7 +27,7 @@ async function fetchPolicies(callWithRequest) { const params = { method: 'GET', path: '/_xpack/index_lifecycle', - // we allow 404 incase the user shutdown security in-between the check and now + // we allow 404 since they may have no policies ignore: [ 404 ] }; From bc52ab19923df2b7a0796b7270ac046c705df647 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 3 Aug 2018 08:14:19 -0400 Subject: [PATCH 029/102] various fixes --- .../public/api/index.js | 3 + .../components/configuration/configuration.js | 4 +- .../template_selection/template_selection.js | 2 +- .../components/cold_phase/cold_phase.js | 4 +- .../components/delete_phase/delete_phase.js | 1 + .../components/hot_phase/hot_phase.js | 2 + .../components/warm_phase/warm_phase.js | 15 +++-- .../policy_selection/policy_selection.js | 55 ++++++++++--------- .../public/store/constants.js | 1 + .../public/store/reducers/nodes.js | 13 ++++- .../public/store/reducers/policies.js | 8 +-- .../public/store/selectors/index_template.js | 14 +++-- .../public/store/selectors/nodes.js | 2 +- .../indices/register_get_affected_route.js | 17 +++--- .../api/lifecycle/register_create_route.js | 2 +- .../api/policies/register_fetch_route.js | 2 +- .../api/templates/register_fetch_route.js | 1 + 17 files changed, 88 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index c771697e6b27a..a67e3c22ae48b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -27,6 +27,9 @@ export async function loadIndexTemplates() { } export async function loadIndexTemplate(templateName) { + if (!templateName) { + return {}; + } const response = await httpClient.get(`${apiPrefix}/template/${templateName}`); return response.data; } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index b107cfae9043b..76ee426662587 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -115,7 +115,7 @@ export class Configuration extends Component { ) : null} > { await setSelectedNodeAttrs(e.target.value); validate(); @@ -151,6 +151,7 @@ export class Configuration extends Component { validate(); }} value={selectedPrimaryShardCount} + min={1} /> @@ -167,6 +168,7 @@ export class Configuration extends Component { validate(); }} value={selectedReplicaCount} + min={0} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index ccfc199a83540..eaf13bb9a4afa 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -105,7 +105,7 @@ export class TemplateSelection extends Component { errors={errors} > { await setSelectedIndexTemplate(e.target.value); validate(); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index dc55d36b4d067..a983f4f101335 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -133,6 +133,7 @@ export class ColdPhase extends PureComponent { setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); validate(); }} + min={1} /> @@ -170,7 +171,7 @@ export class ColdPhase extends PureComponent { ) : null} > { await setPhaseData(PHASE_NODE_ATTRS, e.target.value); @@ -193,6 +194,7 @@ export class ColdPhase extends PureComponent { await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); validate(); }} + min={0} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 8d9e179dc99fa..ae1926a714837 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -119,6 +119,7 @@ export class DeletePhase extends PureComponent { setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); validate(); }} + min={1} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 04ba438f9b671..1ee81eff53a03 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -131,6 +131,7 @@ export class HotPhase extends PureComponent { ); validate(); }} + min={1} /> @@ -173,6 +174,7 @@ export class HotPhase extends PureComponent { await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); validate(); }} + min={1} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index bd46a44907b83..265d3999bc020 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -24,7 +24,7 @@ import { } from '@elastic/eui'; import { PHASE_ENABLED, - PHASE_ROLLOVER_ENABLED, + WARM_PHASE_ON_ROLLOVER, PHASE_ROLLOVER_ALIAS, PHASE_FORCE_MERGE_ENABLED, PHASE_FORCE_MERGE_SEGMENTS, @@ -48,7 +48,7 @@ export class WarmPhase extends PureComponent { errors: PropTypes.object.isRequired, phaseData: PropTypes.shape({ [PHASE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_ENABLED]: PropTypes.bool.isRequired, + [WARM_PHASE_ON_ROLLOVER]: PropTypes.bool.isRequired, [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([ @@ -152,15 +152,15 @@ export class WarmPhase extends PureComponent { { - await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); + await setPhaseData(WARM_PHASE_ON_ROLLOVER, e.target.checked); validate(); }} /> ) : null} - {!phaseData[PHASE_ROLLOVER_ENABLED] ? ( + {!phaseData[WARM_PHASE_ON_ROLLOVER] ? ( @@ -220,7 +221,7 @@ export class WarmPhase extends PureComponent { } > { await setPhaseData(PHASE_NODE_ATTRS, e.target.value); @@ -310,6 +311,7 @@ export class WarmPhase extends PureComponent { ); validate(); }} + min={1} /> @@ -375,6 +377,7 @@ export class WarmPhase extends PureComponent { ); validate(); }} + min={1} /> ) : null} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index 97ab62ddb4c82..3939ccf8424f7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -22,7 +22,7 @@ export class PolicySelection extends Component { setSelectedPolicy: PropTypes.func.isRequired, selectedPolicyName: PropTypes.string.isRequired, - policies: PropTypes.array.isRequired + policies: PropTypes.array.isRequired, }; componentWillMount() { @@ -36,42 +36,43 @@ export class PolicySelection extends Component { render() { const { policies, selectedPolicyName } = this.props; - - const options = policies.map(item => ({ value: item.name, text: item.name })); - options.unshift({ - value: '', - text: '-- New Policy --' - }); + let existingPoliciesSelect; + const policiesExist = policies.length > 0; + if (policiesExist) { + const options = policies.map(item => ({ value: item.name, text: item.name })); + options.unshift({ + value: '', + text: '-- New Policy --', + }); + existingPoliciesSelect = ( + + + { + await this.selectPolicy(e.target.value); + }} + /> + + + ); + } return ( Select or create a policy} + title={

{policiesExist ? 'Select or c' : 'C'}reate a policy

} titleSize="s" - description="An index lifecycle policy is a + description={`An index lifecycle policy is a blueprint for transitioning your data over time. - You can create a new policy or edit an existing - policy and save it with a new name." + You can create a new policy${policiesExist ? ' or edit an existing policy and save it with a new name.' : '.'}`} fullWidth > - - - { - await this.selectPolicy(e.target.value); - }} - /> - - + {existingPoliciesSelect} - this.selectPolicy()} - > - Create new policy - + this.selectPolicy()}>Create new policy diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js index 04c59709bd426..3373946740d01 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/constants.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -17,6 +17,7 @@ export const PHASE_ENABLED = 'phaseEnabled'; export const MAX_SIZE_TYPE_DOCUMENT = 'd'; export const PHASE_ROLLOVER_ENABLED = 'rolloverEnabled'; +export const WARM_PHASE_ON_ROLLOVER = 'warmPhaseOnRollover'; export const PHASE_ROLLOVER_ALIAS = 'selectedAlias'; export const PHASE_ROLLOVER_MAX_AGE = 'selectedMaxAge'; export const PHASE_ROLLOVER_MAX_AGE_UNITS = 'selectedMaxAgeUnits'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js index a5a372ed17a8a..5e8e01eab26df 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js @@ -58,11 +58,18 @@ export const nodes = handleActions( }; }, [setSelectedReplicaCount](state, { payload }) { - let selectedReplicaCount = parseInt(payload); - if (isNaN(selectedReplicaCount)) { - selectedReplicaCount = ''; + let selectedReplicaCount; + if (payload != null) { + selectedReplicaCount = parseInt(payload); + if (isNaN(selectedReplicaCount)) { + selectedReplicaCount = ''; + } + } else { + // default value for Elasticsearch + selectedReplicaCount = 1; } + return { ...state, selectedReplicaCount diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 982b20381dc50..0c147b7d14222 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -37,12 +37,12 @@ import { PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, PHASE_ROLLOVER_ALIAS, PHASE_ROLLOVER_MAX_DOC_SIZE, - PHASE_SHRINK_ENABLED + PHASE_SHRINK_ENABLED, + WARM_PHASE_ON_ROLLOVER } from '../constants'; export const defaultWarmPhase = { [PHASE_ENABLED]: false, - [PHASE_ROLLOVER_ENABLED]: false, [PHASE_ROLLOVER_ALIAS]: '', [PHASE_FORCE_MERGE_SEGMENTS]: '', [PHASE_FORCE_MERGE_ENABLED]: false, @@ -51,7 +51,8 @@ export const defaultWarmPhase = { [PHASE_NODE_ATTRS]: '', [PHASE_SHRINK_ENABLED]: true, [PHASE_PRIMARY_SHARD_COUNT]: '', - [PHASE_REPLICA_COUNT]: '' + [PHASE_REPLICA_COUNT]: '', + [WARM_PHASE_ON_ROLLOVER]: false }; export const defaultHotPhase = { @@ -67,7 +68,6 @@ export const defaultHotPhase = { export const defaultColdPhase = { [PHASE_ENABLED]: false, - [PHASE_ROLLOVER_ENABLED]: false, [PHASE_ROLLOVER_ALIAS]: '', [PHASE_ROLLOVER_AFTER]: '', [PHASE_ROLLOVER_AFTER_UNITS]: 's', diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index 9992c7491d368..06786a385b774 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -58,9 +58,13 @@ export const getSelectedIndexTemplate = createSelector( export const getFullSelectedIndexTemplate = state => state.indexTemplate.fullSelectedIndexTemplate; export const getAlias = state => { - const template = getSelectedIndexTemplate(state); - if (template && template.settings) { - return template.settings.indexlifecycle.rollover_alias; + const indexTemplate = getSelectedIndexTemplate(state); + if (!indexTemplate) { + return undefined; + } + const { settings = {} } = indexTemplate; + if (settings.indexlifecycle) { + return settings.indexlifecycle.rollover_alias; } return undefined; }; @@ -126,8 +130,8 @@ export const getTemplateDiff = state => { const newFullIndexTemplate = merge(cloneDeep(originalFullIndexTemplate), { settings: { index: { - number_of_shards: '' + getSelectedPrimaryShardCount(state), - number_of_replicas: '' + getSelectedReplicaCount(state), + number_of_shards: getSelectedPrimaryShardCount(state), + number_of_replicas: getSelectedReplicaCount(state), lifecycle: { name: getSelectedPolicyName(state) }, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js index 4a65cf969bdb5..b92f96422e6b6 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js @@ -32,7 +32,7 @@ export const getNodeOptions = createSelector( export const getSelectedPrimaryShardCount = state => state.nodes.selectedPrimaryShardCount; export const getSelectedReplicaCount = state => - state.nodes.selectedReplicaCount; + state.nodes.selectedReplicaCount !== undefined ? state.nodes.selectedReplicaCount : 1; export const getSelectedNodeAttrs = state => state.nodes.selectedNodeAttrs; export const getNodesFromSelectedNodeAttrs = state => { const nodes = getNodes(state)[getSelectedNodeAttrs(state)]; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js index fc7f5f2d2d131..e26d04600f15e 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -13,7 +13,7 @@ async function fetchTemplates(callWithRequest) { const params = { method: 'GET', path: '/_template', - // we allow 404 incase the user shutdown security in-between the check and now + // we allow 404 in case there are no templates ignore: [404] }; @@ -27,7 +27,7 @@ async function getAffectedIndices( ) { const templates = await fetchTemplates(callWithRequest); - if (!templates || Object.keys(templates).length === 0) { + if (!templates || Object.keys(templates).length === 0 | templates.status === 404) { return []; } @@ -48,12 +48,15 @@ async function getAffectedIndices( if (!indexPatterns || indexPatterns.length === 0) { return []; } + const indexParams = { + method: 'GET', + path: `/${indexPatterns.join(',')}`, + // we allow 404 in case there are no indices + ignore: [404] + }; + const indices = await callWithRequest('transport.request', indexParams); - const indices = await callWithRequest('indices.get', { - index: indexPatterns - }); - - if (!indices) { + if (!indices || indices.status === 404) { return []; } diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js index 84e2637ab5a4c..cc7822e28da68 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -21,7 +21,7 @@ async function createLifecycle(callWithRequest, lifecycle) { }; const params = { method: 'PUT', - path: `/_xpack/index_lifecycle/${lifecycle.name}`, + path: `/_ilm/${lifecycle.name}`, ignore: [ 404 ], body, }; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js index 0471dbc717bcd..75e88426f65f8 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -26,7 +26,7 @@ function formatHits(hits) { async function fetchPolicies(callWithRequest) { const params = { method: 'GET', - path: '/_xpack/index_lifecycle', + path: '/_ilm', // we allow 404 since they may have no policies ignore: [ 404 ] }; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js index 7bc4922478be0..a8aaf38868e7a 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js @@ -21,6 +21,7 @@ async function formatTemplates(templates, callWithRequest) { index_lifecycle_name: settings.index && settings.index.lifecycle ? settings.index.lifecycle.name : undefined, index_patterns, allocation_rules: settings.index && settings.index.routing ? settings.index.routing : undefined, + settings, name: templateName, }; From e19215334ea2c8cbed97c2da3dd875e20fba1c04 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 9 Aug 2018 12:30:43 -0400 Subject: [PATCH 030/102] some cleanup --- .../index_lifecycle_management/public/app.js | 7 +- .../public/sections/landing/index.js | 10 --- .../sections/landing/landing.container.js | 24 ------- .../public/sections/landing/landing.js | 43 ------------ .../sections/wizard/wizard.container.js | 6 +- .../public/sections/wizard/wizard.js | 68 +++++++++++-------- 6 files changed, 45 insertions(+), 113 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js diff --git a/x-pack/plugins/index_lifecycle_management/public/app.js b/x-pack/plugins/index_lifecycle_management/public/app.js index 2f5c19a16b684..78561b785f28c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/app.js +++ b/x-pack/plugins/index_lifecycle_management/public/app.js @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import React from 'react'; -import { Landing } from './sections/landing'; +import { Wizard } from './sections/wizard'; export const App = () => ( - + ); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js deleted file mode 100644 index cc858ed0f20e5..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/landing/index.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -export { Landing } from './landing.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js deleted file mode 100644 index 93e507b00ab7b..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.container.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { Landing as PresentationComponent } from './landing'; -import { fetchIndexTemplates } from '../../store/actions'; -import { - getIndexTemplates, -} from '../../store/selectors'; - -export const Landing = connect( - state => ({ - indexTemplates: getIndexTemplates(state), - }), - { - fetchIndexTemplates - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js b/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js deleted file mode 100644 index c9b2366e97e05..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/landing/landing.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { Wizard } from '../wizard'; - -export class Landing extends PureComponent { - static propTypes = { - fetchIndexTemplates: PropTypes.func.isRequired, - - indexTemplates: PropTypes.array, - } - - componentWillMount() { - this.props.fetchIndexTemplates(); - } - - render() { - const { indexTemplates } = this.props; - - if (indexTemplates === null) { - // Loading... - return null; - } - - if (indexTemplates.length === 0) { - return ( -

No index templates found.

- ); - } - - return ( - - ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js index 2b76624165a78..84c94f2c82387 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js @@ -10,8 +10,10 @@ import { connect } from 'react-redux'; import '../../less/main.less'; import { Wizard as PresentationComponent } from './wizard'; -import { saveLifecycle } from '../../store/actions'; +import { saveLifecycle, fetchIndexTemplates } from '../../store/actions'; + import { + getIndexTemplates, getIndexTemplatePatch, getBootstrapEnabled, getIndexName, @@ -26,8 +28,10 @@ export const Wizard = connect( indexName: getIndexName(state), aliasName: getAliasName(state), validateLifecycle: () => validateLifecycle(state), + indexTemplates: getIndexTemplates(state), }), { + fetchIndexTemplates, saveLifecycle } )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index ab92bc815bd2f..ed579e0e4aca0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -25,6 +25,9 @@ import { STRUCTURE_REVIEW, } from '../../store/constants'; +const STEP_INDEX_TEMPLATE = 1; +const STEP_POLICY_CONFIGURATION = 2; +const STEP_REVIEW = 3; export class Wizard extends Component { static propTypes = { saveLifecycle: PropTypes.func.isRequired, @@ -34,17 +37,21 @@ export class Wizard extends Component { bootstrapEnabled: PropTypes.bool.isRequired, indexName: PropTypes.string.isRequired, aliasName: PropTypes.string.isRequired, + fetchIndexTemplates: PropTypes.func.isRequired, + indexTemplates: PropTypes.array, }; constructor(props) { super(props); this.state = { - selectedStep: 1, + selectedStep: STEP_INDEX_TEMPLATE, errors: this.getErrors(), }; } - + componentDidMount() { + this.props.fetchIndexTemplates(); + } onSelectedStepChanged = selectedStep => { this.setState({ selectedStep, @@ -89,57 +96,58 @@ export class Wizard extends Component { const { selectedStep, errors } = this.state; switch (selectedStep) { - case 1: + case STEP_INDEX_TEMPLATE: return ( this.onSelectedStepChanged(2)} + done={() => this.onSelectedStepChanged(selectedStep + 1)} /> ); - case 2: + case STEP_POLICY_CONFIGURATION: return ( this.onSelectedStepChanged(3)} - back={() => this.onSelectedStepChanged(1)} + done={() => this.onSelectedStepChanged(selectedStep + 1)} + back={() => this.onSelectedStepChanged(selectedStep - 1)} /> ); - case 3: + case STEP_REVIEW: return ( this.onSelectedStepChanged(2)} + back={() => this.onSelectedStepChanged(selectedStep - 1)} /> ); } } - + createStep(title, stepIndex) { + return { + title, + isSelected: this.state.selectedStep === stepIndex, + isComplete: this.state.selectedStep > stepIndex, + onClick: () => this.onSelectedStepChanged(stepIndex), + }; + } render() { + const { indexTemplates } = this.props; + if (indexTemplates === null) { + // Loading... + return null; + } + + if (indexTemplates.length === 0) { + return ( +

No index templates found.

+ ); + } const steps = [ - { - title: 'Select a template', - isSelected: this.state.selectedStep === 1, - isComplete: this.state.selectedStep > 1, - onClick: () => this.onSelectedStepChanged(1), - }, - { - title: 'Configure a policy', - isSelected: this.state.selectedStep === 2, - isComplete: this.state.selectedStep > 2, - disabled: this.state.selectedStep < 2, - onClick: () => this.onSelectedStepChanged(2), - }, - { - title: 'Review and save', - isSelected: this.state.selectedStep === 3, - isComplete: this.state.selectedStep > 3, - disabled: this.state.selectedStep < 3, - onClick: () => this.onSelectedStepChanged(3), - }, + this.createStep('Select a template', STEP_INDEX_TEMPLATE), + this.createStep('Configure a policy', STEP_POLICY_CONFIGURATION), + this.createStep('Review and save', STEP_REVIEW), ]; return ( From eca34f9eb59add0769883bfb4bf2cb06df2fccd3 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 13 Aug 2018 08:58:04 -0400 Subject: [PATCH 031/102] moving number_of_replicas due to API change --- .../public/store/selectors/policies.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index cf2f859fffbca..d01304d29e5dd 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -211,6 +211,9 @@ export const phaseToES = (state, phase) => { _name: phase[PHASE_NODE_ATTRS] } }; + if (isNumber(phase[PHASE_REPLICA_COUNT])) { + esPhase.actions.allocate.number_of_replicas = phase[PHASE_REPLICA_COUNT]; + } } if (phase[PHASE_FORCE_MERGE_ENABLED]) { @@ -224,12 +227,5 @@ export const phaseToES = (state, phase) => { number_of_shards: phase[PHASE_PRIMARY_SHARD_COUNT] }; } - - if (isNumber(phase[PHASE_REPLICA_COUNT])) { - esPhase.actions.replicas = { - number_of_replicas: phase[PHASE_REPLICA_COUNT] - }; - } - return esPhase; }; From bc52b195ee052be584b221d93a8a1257ea67d8b3 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 15 Aug 2018 09:39:34 -0400 Subject: [PATCH 032/102] modifying some messaging --- .../components/configuration/configuration.js | 47 ++++++++----------- .../public/store/selectors/nodes.js | 5 -- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index 76ee426662587..ac4d153b08f15 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -35,20 +35,13 @@ export class Configuration extends Component { setSelectedReplicaCount: PropTypes.func.isRequired, validate: PropTypes.func.isRequired, - selectedPrimaryShardCount: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, + selectedPrimaryShardCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, selectedNodeAttrs: PropTypes.string.isRequired, nodeOptions: PropTypes.array.isRequired, - selectedReplicaCount: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, + selectedReplicaCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isShowingErrors: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, - isPrimaryShardCountHigherThanSelectedNodeAttrsCount: - PropTypes.bool.isRequired, + isPrimaryShardCountHigherThanSelectedNodeAttrsCount: PropTypes.bool.isRequired, }; constructor(props) { @@ -98,21 +91,21 @@ export class Configuration extends Component { fullWidth > - this.setState({ isShowingNodeDetailsFlyout: true }) - } - > - View a list of nodes attached to this configuration - - ) : null} + helpText={ + selectedNodeAttrs ? ( + this.setState({ isShowingNodeDetailsFlyout: true })} + > + View a list of nodes attached to this configuration + + ) : null + } > - The best way to determine how many shards you need is to benchmark - using realistic data and queries on your hardware.{' '} - + The best way to determine how many shards you need is to benchmark using realistic + data and queries on your hardware.{' '} +

} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js index b92f96422e6b6..887dddc2d3c00 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js @@ -20,11 +20,6 @@ export const getNodeOptions = createSelector( })); options.sort((a, b) => a.value.localeCompare(b.value)); - options.unshift({ - text: '-- Do not reallocate my indices --', - value: undefined, - }); - return options; } ); From 472213bbdae9d90c9d05716e25e84cf62ffe87fe Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 20 Aug 2018 10:11:28 -0400 Subject: [PATCH 033/102] fixing typo --- .../components/delete_phase/delete_phase.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index ae1926a714837..52226029b1111 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -96,7 +96,7 @@ export class DeletePhase extends PureComponent { validate(); }} > - Deactive cold phase + Deactive delete phase
From e3ba85fa6f5dd3dc251d03a7457cc8dabe9697aa Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 20 Aug 2018 11:59:48 -0400 Subject: [PATCH 034/102] fixing some diff issues and not adding sattr_name if none chosen --- .../public/store/selectors/index_template.js | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index 06786a385b774..29524aad9863d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -127,24 +127,28 @@ export const getExistingAllocationRules = state => { const hasJSONChanged = (json1, json2) => JSON.stringify(json1) !== JSON.stringify(json2); export const getTemplateDiff = state => { const originalFullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; - const newFullIndexTemplate = merge(cloneDeep(originalFullIndexTemplate), { + const attributeName = getSelectedNodeAttrs(state); + const baseNewFullIndexTemplate = { settings: { index: { - number_of_shards: getSelectedPrimaryShardCount(state), - number_of_replicas: getSelectedReplicaCount(state), + number_of_shards: getSelectedPrimaryShardCount(state) + '', + number_of_replicas: getSelectedReplicaCount(state) + '', lifecycle: { name: getSelectedPolicyName(state) }, - routing: { - allocation: { - include: { - sattr_name: getSelectedNodeAttrs(state), - } - } - } } } - }); + }; + if (attributeName) { + baseNewFullIndexTemplate.routing = { + allocation: { + include: { + sattr_name: attributeName, + } + } + }; + } + const newFullIndexTemplate = merge(cloneDeep(originalFullIndexTemplate), baseNewFullIndexTemplate); return { originalFullIndexTemplate, From e97639e3a87d2e979d57a5bf5e3c18a6a3a64363 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 22 Aug 2018 11:18:01 -0400 Subject: [PATCH 035/102] making write alias required for template step and making necessary API changes --- .../template_selection/template_selection.js | 30 +++++++++---------- .../public/store/selectors/index_template.js | 7 ++++- .../public/store/selectors/lifecycle.js | 4 +-- .../api/indices/register_bootstrap_route.js | 8 +---- .../api/lifecycle/register_create_route.js | 4 +++ 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index eaf13bb9a4afa..4f8acc5f7b6e5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -113,6 +113,20 @@ export class TemplateSelection extends Component { options={templateOptions} /> + + { + await setAliasName(e.target.value); + validate(); + }} + /> + {selectedIndexTemplateName && selectedIndexTemplateIndices.length === 0 ? ( @@ -120,7 +134,7 @@ export class TemplateSelection extends Component { style={{ maxWidth: '100%' }} checked={bootstrapEnabled} onChange={e => setBootstrapEnabled(e.target.checked)} - label={Create an index and alias for this template} + label={Create an index for this template} /> {bootstrapEnabled ? ( @@ -139,20 +153,6 @@ export class TemplateSelection extends Component { }} /> - - { - await setAliasName(e.target.value); - validate(); - }} - /> - ) : null} diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index 29524aad9863d..d470b6474660b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -17,6 +17,7 @@ import { getSelectedReplicaCount, getSelectedNodeAttrs } from '.'; +import { getAliasName } from './general'; export const getIndexTemplates = state => state.indexTemplate.indexTemplates; export const getIndexTemplateOptions = createSelector( @@ -49,6 +50,9 @@ export const getSelectedIndexTemplate = createSelector( state => getIndexTemplates(state) ], (selectedIndexTemplateName, allTemplates) => { + if (!allTemplates) { + return null; + } return allTemplates.find( template => template.name === selectedIndexTemplateName ); @@ -174,6 +178,7 @@ export const getIndexTemplatePatch = state => { primaryShardCount: getSelectedPrimaryShardCount(state), replicaCount: getSelectedReplicaCount(state), lifecycleName: getSelectedPolicyName(state), - nodeAttrs: getSelectedNodeAttrs(state) + nodeAttrs: getSelectedNodeAttrs(state), + rolloverAlias: getAliasName(state) }; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index 49a074fc4b90a..fcbdb66909fc9 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -121,8 +121,8 @@ export const validateLifecycle = state => { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_INDEX_NAME].push('An index name is required'); } - if (getBootstrapEnabled(state) && !getAliasName(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('An alias name is required'); + if (!getAliasName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('A write alias name is required'); } if (!isNumber(getSelectedPrimaryShardCount(state))) { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js index ad7128903b99c..22902c925ccfa 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js @@ -11,13 +11,7 @@ import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_facto async function bootstrap(callWithRequest, payload) { await callWithRequest('indices.create', { - index: payload.indexName, - aliases: { - [payload.aliasName]: {} - }, - settings: { - 'index.lifecycle.rollover_alias': payload.aliasName, - } + index: payload.indexName }); } diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js index cc7822e28da68..16ef4cd1451f0 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -38,12 +38,16 @@ async function updateIndexTemplate(callWithRequest, indexTemplatePatch) { // Fetch existing template const template = await getIndexTemplate(callWithRequest, indexTemplatePatch.indexTemplate); merge(template, { + aliases: { + [indexTemplatePatch.rolloverAlias]: {} + }, settings: { index: { number_of_shards: indexTemplatePatch.primaryShardCount, number_of_replicas: indexTemplatePatch.replicaCount, lifecycle: { name: indexTemplatePatch.lifecycleName, + rollover_alias: indexTemplatePatch.rolloverAlias }, routing: { allocation: { From 61a3f953f002fea0d201bf9ec89481ce2c2a3ea2 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 22 Aug 2018 14:12:04 -0400 Subject: [PATCH 036/102] removing alias definition from template patching as it needs to be per index --- .../routes/api/indices/register_bootstrap_route.js | 9 ++++++++- .../server/routes/api/lifecycle/register_create_route.js | 3 --- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js index 22902c925ccfa..dcd053e096c52 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js @@ -11,7 +11,14 @@ import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_facto async function bootstrap(callWithRequest, payload) { await callWithRequest('indices.create', { - index: payload.indexName + index: payload.indexName, + body: { + aliases: { + [payload.aliasName]: { + is_write_alias: true + } + }, + } }); } diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js index 16ef4cd1451f0..14cf049454a34 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -38,9 +38,6 @@ async function updateIndexTemplate(callWithRequest, indexTemplatePatch) { // Fetch existing template const template = await getIndexTemplate(callWithRequest, indexTemplatePatch.indexTemplate); merge(template, { - aliases: { - [indexTemplatePatch.rolloverAlias]: {} - }, settings: { index: { number_of_shards: indexTemplatePatch.primaryShardCount, From c507a351bdaff41761a4c94d99994ff7506a1f39 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 27 Aug 2018 08:33:28 -0400 Subject: [PATCH 037/102] some copy edits for clarity --- .../components/template_selection/template_selection.js | 4 ++-- .../components/cold_phase/cold_phase.js | 6 +++--- .../policy_configuration/components/hot_phase/hot_phase.js | 2 +- .../components/warm_phase/warm_phase.js | 2 +- .../public/sections/wizard/components/review/review.js | 6 +++--- .../public/sections/wizard/wizard.js | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 4f8acc5f7b6e5..832a420087dc7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -99,7 +99,7 @@ export class TemplateSelection extends Component { /> setBootstrapEnabled(e.target.checked)} - label={Create an index for this template} + label={Create an index with this index template} /> {bootstrapEnabled ? ( diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index a983f4f101335..737da8502a753 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -88,8 +88,8 @@ export class ColdPhase extends PureComponent { description={

- Your index is queried less frequently - and no longer needs to be on the most performant hardware. + A cold index is queried less frequently + and thus no longer needs to be on the most performant hardware.

{isShowingErrors ? ( @@ -156,7 +156,7 @@ export class ColdPhase extends PureComponent {

- This phase is required. Your index is being queried and actively written to. + This phase is required. A hot index is being queried and actively written to. You can optimize this phase for write throughput.

{isShowingErrors ? ( diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 265d3999bc020..f864c8a883747 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -203,7 +203,7 @@ export class WarmPhase extends PureComponent {

{`${affectedIndexTemplates.length} Index ${affectedIndexTemplates.length === 1 ? 'template' : 'templates'}`}

@@ -182,7 +182,7 @@ export class Review extends Component {

- {selectedIndexTemplateName} template changes + {selectedIndexTemplateName} index template changes

@@ -206,7 +206,7 @@ export class Review extends Component {

You are editing an existing policy. Any changes you make will also change index templates that this policy is attached to. Alternately, you can save - these changes in a new policy and only change the template you + these changes in a new policy and only change the index template you selected.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index ed579e0e4aca0..9e85d6677389c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -145,8 +145,8 @@ export class Wizard extends Component { ); } const steps = [ - this.createStep('Select a template', STEP_INDEX_TEMPLATE), - this.createStep('Configure a policy', STEP_POLICY_CONFIGURATION), + this.createStep('Select an index template', STEP_INDEX_TEMPLATE), + this.createStep('Configure a lifecycle policy', STEP_POLICY_CONFIGURATION), this.createStep('Review and save', STEP_REVIEW), ]; From f74e6027b7687eee2b690d376850446f6e3c0a14 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 27 Aug 2018 08:54:46 -0400 Subject: [PATCH 038/102] fixing issue with editing existing policy when rollover starts the warm phase --- .../public/store/selectors/policies.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index d01304d29e5dd..d70604c63609f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -97,14 +97,8 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { const { size: after, units: afterUnits } = splitSizeAndUnits( phase.after ); - // If the after is set to 0s, it effectively means we are moving - // to the warm phase after rollover from the hot phase - if (phaseName === PHASE_WARM && after === 0) { - policy[PHASE_ROLLOVER_ENABLED] = true; - } else { - policy[PHASE_ROLLOVER_AFTER] = after; - policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; - } + policy[PHASE_ROLLOVER_AFTER] = after; + policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; } if (phase.actions) { From 1f3da343acef29cb64a3bb407181c8d2e4140889 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 27 Aug 2018 09:16:02 -0400 Subject: [PATCH 039/102] addressing PR feedback on server side code --- .../api/indices/register_get_affected_route.js | 15 ++++++++------- .../routes/api/nodes/register_details_route.js | 4 ++-- .../routes/api/nodes/register_list_route.js | 4 ++-- .../routes/api/templates/register_fetch_route.js | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js index e26d04600f15e..8f60bffb94b5a 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -27,25 +27,26 @@ async function getAffectedIndices( ) { const templates = await fetchTemplates(callWithRequest); - if (!templates || Object.keys(templates).length === 0 | templates.status === 404) { + if (!templates || Object.keys(templates).length === 0 || templates.status === 404) { return []; } const indexPatterns = Object.entries(templates).reduce((accum, [templateName, template]) => { - if (templateName === indexTemplateName) { - accum.push(...template.index_patterns); - } else if ( + const isMatchingTemplate = templateName === indexTemplateName; + const isMatchingPolicy = ( + policyName && template.settings && template.settings.index && template.settings.index.lifecycle && - (policyName && template.settings.index.lifecycle.name === policyName) - ) { + template.settings.index.lifecycle.name === policyName + ); + if (isMatchingTemplate || isMatchingPolicy) { accum.push(...template.index_patterns); } return accum; }, []); - if (!indexPatterns || indexPatterns.length === 0) { + if (indexPatterns.length === 0) { return []; } const indexParams = { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js index 9ab30dd0f2f7e..1a0faff7ed5dd 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js @@ -12,7 +12,7 @@ import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; -function formatStats(stats, nodeAttrs) { +function findMatchingNodes(stats, nodeAttrs) { return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { const attributes = stats.attributes || {}; for (const [key, value] of Object.entries(attributes)) { @@ -48,7 +48,7 @@ export function registerDetailsRoute(server) { try { const stats = await fetchNodeStats(callWithRequest); - const response = formatStats(stats, request.params.nodeAttrs); + const response = findMatchingNodes (stats, request.params.nodeAttrs); reply(response); } catch (err) { if (isEsError(err)) { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js index 7b82619665412..8e92e6570edcf 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js @@ -13,7 +13,7 @@ import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; import { NODE_ATTRS_KEYS_TO_IGNORE } from './constants'; -function formatStats(stats) { +function convertStatsIntoList(stats) { return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { const attributes = stats.attributes || {}; for (const [key, value] of Object.entries(attributes)) { @@ -47,7 +47,7 @@ export function registerListRoute(server) { try { const stats = await fetchNodeStats(callWithRequest); - const response = formatStats(stats); + const response = convertStatsIntoList(stats); reply(response); } catch (err) { if (isEsError(err)) { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js index a8aaf38868e7a..1a4c5529b8da8 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js @@ -66,7 +66,7 @@ export function registerFetchRoute(server) { try { const hits = await fetchTemplates(callWithRequest); - const templates = formatTemplates(hits, callWithRequest); + const templates = await formatTemplates(hits, callWithRequest); reply(templates); } catch (err) { if (isEsError(err)) { From effd90983c19e4fc0057cf30b9909b4832f6b18a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 27 Aug 2018 10:05:57 -0400 Subject: [PATCH 040/102] addressing PR feedback --- .../public/main.html | 2 +- .../policy_configuration.js | 3 - .../public/store/actions/nodes.js | 3 +- .../public/store/actions/policies.js | 4 +- .../public/store/constants.js | 5 +- .../public/store/defaults/cold_phase.js | 22 +++++++ .../public/store/defaults/delete_phase.js | 20 ++++++ .../public/store/defaults/hot_phase.js | 26 ++++++++ .../public/store/defaults/index.js | 10 +++ .../public/store/defaults/warm_phase.js | 32 +++++++++ .../store/middleware/auto_enable_phase.js | 8 +-- .../store/middleware/auto_set_node_attrs.js | 17 +++-- .../public/store/reducers/policies.js | 65 ++----------------- .../public/store/selectors/index_template.js | 13 +--- .../public/store/selectors/policies.js | 6 +- 15 files changed, 142 insertions(+), 94 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js diff --git a/x-pack/plugins/index_lifecycle_management/public/main.html b/x-pack/plugins/index_lifecycle_management/public/main.html index 36820f8e159ac..ca86144cbe934 100644 --- a/x-pack/plugins/index_lifecycle_management/public/main.html +++ b/x-pack/plugins/index_lifecycle_management/public/main.html @@ -1,3 +1,3 @@ - +
diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index a2ed77bd87f3b..f6beddab49c78 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -40,9 +40,6 @@ export class PolicyConfiguration extends Component { done: PropTypes.func.isRequired, back: PropTypes.func.isRequired, validate: PropTypes.func.isRequired, - - affectedIndexTemplates: PropTypes.array.isRequired, - selectedIndexTemplateName: PropTypes.string.isRequired, selectedPolicyName: PropTypes.string.isRequired, saveAsNewPolicy: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js index e357b6d6587f3..b806d1df3d380 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js @@ -7,8 +7,9 @@ import { createAction } from 'redux-actions'; import { toastNotifications } from 'ui/notify'; import { loadNodes, loadNodeDetails } from '../../api'; +import { SET_SELECTED_NODE_ATTRS } from '../constants'; -export const setSelectedNodeAttrs = createAction('SET_SELECTED_NODE_ATTRS'); +export const setSelectedNodeAttrs = createAction(SET_SELECTED_NODE_ATTRS); export const setSelectedPrimaryShardCount = createAction( 'SET_SELECTED_PRIMARY_SHARED_COUNT' ); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index 7d420d6e43b20..fccd46b449260 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -10,7 +10,7 @@ import { createAction } from 'redux-actions'; import { toastNotifications } from 'ui/notify'; import { loadPolicies } from '../../api'; - +import { SET_PHASE_DATA } from '../constants'; export const fetchedPolicies = createAction('FETCHED_POLICIES'); export const fetchPolicies = () => async dispatch => { let policies; @@ -29,4 +29,4 @@ export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); -export const setPhaseData = createAction('SET_PHASE_DATA', (phase, key, value) => ({ phase, key, value })); +export const setPhaseData = createAction(SET_PHASE_DATA, (phase, key, value) => ({ phase, key, value })); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js index 3373946740d01..b8e918ef5e4b2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/constants.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ - - - +export const SET_PHASE_DATA = 'SET_PHASE_DATA'; +export const SET_SELECTED_NODE_ATTRS = 'SET_SELECTED_NODE_ATTRS'; export const PHASE_HOT = 'hot'; export const PHASE_WARM = 'warm'; export const PHASE_COLD = 'cold'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js new file mode 100644 index 0000000000000..b5b54c02239ba --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js @@ -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 { + PHASE_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_NODE_ATTRS, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ALIAS, +} from '../constants'; + +export const defaultColdPhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 's', + [PHASE_NODE_ATTRS]: '', + [PHASE_REPLICA_COUNT]: '' +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js new file mode 100644 index 0000000000000..38653399035f6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ALIAS, +} from '../constants'; + +export const defaultDeletePhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 's' +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js new file mode 100644 index 0000000000000..2053af0c3fc58 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js @@ -0,0 +1,26 @@ +/* + * 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 { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MAX_DOC_SIZE, +} from '../constants'; + +export const defaultHotPhase = { + [PHASE_ENABLED]: true, + [PHASE_ROLLOVER_ENABLED]: true, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_MAX_AGE]: '', + [PHASE_ROLLOVER_MAX_AGE_UNITS]: 's', + [PHASE_ROLLOVER_MAX_SIZE_STORED]: '', + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: 'gb', + [PHASE_ROLLOVER_MAX_DOC_SIZE]: '', +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js new file mode 100644 index 0000000000000..a92f98fa8e022 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js @@ -0,0 +1,10 @@ +/* + * 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 { defaultDeletePhase } from './delete_phase'; +export { defaultColdPhase } from './cold_phase'; +export { defaultHotPhase } from './hot_phase'; +export { defaultWarmPhase } from './warm_phase'; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js new file mode 100644 index 0000000000000..26db268641e37 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js @@ -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 { + PHASE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_NODE_ATTRS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ALIAS, + PHASE_SHRINK_ENABLED, + WARM_PHASE_ON_ROLLOVER +} from '../constants'; + +export const defaultWarmPhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_FORCE_MERGE_SEGMENTS]: '', + [PHASE_FORCE_MERGE_ENABLED]: false, + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 's', + [PHASE_NODE_ATTRS]: '', + [PHASE_SHRINK_ENABLED]: true, + [PHASE_PRIMARY_SHARD_COUNT]: '', + [PHASE_REPLICA_COUNT]: '', + [WARM_PHASE_ON_ROLLOVER]: false +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js index 738be1fd7a56d..d7d390f608ccf 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js @@ -4,18 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - setPhaseData -} from '../actions'; +import { setPhaseData } from '../actions'; import { getPhaseData } from '../selectors'; -import { PHASE_ENABLED } from '../constants'; +import { SET_PHASE_DATA, PHASE_ENABLED } from '../constants'; const setsPerPhase = {}; export const autoEnablePhase = store => next => action => { const state = store.getState(); - if (action.type === setPhaseData().type) { + if (action.type === SET_PHASE_DATA) { const { phase } = action.payload; setsPerPhase[phase] = setsPerPhase[phase] || 0; setsPerPhase[phase]++; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js index 5f0a43c31abdc..5d59ccd8c4fe7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js @@ -4,22 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - setPhaseData, setSelectedNodeAttrs -} from '../actions'; +import { setPhaseData } from '../actions'; import { getPhaseData } from '../selectors'; -import { PHASE_WARM, PHASE_NODE_ATTRS, PHASE_COLD } from '../constants'; +import { + SET_SELECTED_NODE_ATTRS, + SET_PHASE_DATA, + PHASE_WARM, + PHASE_NODE_ATTRS, + PHASE_COLD, +} from '../constants'; export const autoSetNodeAttrs = store => next => action => { const state = store.getState(); - if (action.type === setSelectedNodeAttrs().type) { + if (action.type === SET_SELECTED_NODE_ATTRS) { const warmPhaseAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); if (!warmPhaseAttrs) { store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, action.payload)); } - } - else if (action.type === setPhaseData().type) { + } else if (action.type === SET_PHASE_DATA) { const { phase, key, value } = action.payload; if (phase === PHASE_WARM && key === PHASE_NODE_ATTRS) { diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 0c147b7d14222..6afe1b4fc53ae 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -22,67 +22,14 @@ import { PHASE_COLD, PHASE_DELETE, PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, - PHASE_ENABLED, - PHASE_ROLLOVER_ENABLED, - PHASE_FORCE_MERGE_SEGMENTS, - PHASE_FORCE_MERGE_ENABLED, - PHASE_ROLLOVER_AFTER, - PHASE_NODE_ATTRS, - PHASE_PRIMARY_SHARD_COUNT, - PHASE_REPLICA_COUNT, - PHASE_ROLLOVER_AFTER_UNITS, - PHASE_ROLLOVER_MAX_AGE, - PHASE_ROLLOVER_MAX_AGE_UNITS, - PHASE_ROLLOVER_MAX_SIZE_STORED, - PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, - PHASE_ROLLOVER_ALIAS, - PHASE_ROLLOVER_MAX_DOC_SIZE, - PHASE_SHRINK_ENABLED, - WARM_PHASE_ON_ROLLOVER } from '../constants'; -export const defaultWarmPhase = { - [PHASE_ENABLED]: false, - [PHASE_ROLLOVER_ALIAS]: '', - [PHASE_FORCE_MERGE_SEGMENTS]: '', - [PHASE_FORCE_MERGE_ENABLED]: false, - [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 's', - [PHASE_NODE_ATTRS]: '', - [PHASE_SHRINK_ENABLED]: true, - [PHASE_PRIMARY_SHARD_COUNT]: '', - [PHASE_REPLICA_COUNT]: '', - [WARM_PHASE_ON_ROLLOVER]: false -}; - -export const defaultHotPhase = { - [PHASE_ENABLED]: true, - [PHASE_ROLLOVER_ENABLED]: true, - [PHASE_ROLLOVER_ALIAS]: '', - [PHASE_ROLLOVER_MAX_AGE]: '', - [PHASE_ROLLOVER_MAX_AGE_UNITS]: 's', - [PHASE_ROLLOVER_MAX_SIZE_STORED]: '', - [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: 'gb', - [PHASE_ROLLOVER_MAX_DOC_SIZE]: '', -}; - -export const defaultColdPhase = { - [PHASE_ENABLED]: false, - [PHASE_ROLLOVER_ALIAS]: '', - [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 's', - [PHASE_NODE_ATTRS]: '', - [PHASE_REPLICA_COUNT]: '' -}; - -export const defaultDeletePhase = { - [PHASE_ENABLED]: false, - [PHASE_ROLLOVER_ENABLED]: false, - [PHASE_ROLLOVER_ALIAS]: '', - [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 's' -}; - +import { + defaultColdPhase, + defaultDeletePhase, + defaultHotPhase, + defaultWarmPhase, +} from '../defaults'; export const defaultPolicy = { name: '', saveAsNew: true, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index d470b6474660b..339d5380613ff 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -8,7 +8,7 @@ import { createSelector } from 'reselect'; -import { merge, cloneDeep } from 'lodash'; +import { get, merge, cloneDeep } from 'lodash'; import { getSaveAsNewPolicy, getSelectedPolicyName, @@ -19,7 +19,7 @@ import { } from '.'; import { getAliasName } from './general'; -export const getIndexTemplates = state => state.indexTemplate.indexTemplates; +export const getIndexTemplates = state => { return state.indexTemplate.indexTemplates || []; }; export const getIndexTemplateOptions = createSelector( [state => getIndexTemplates(state)], templates => { @@ -63,14 +63,7 @@ export const getFullSelectedIndexTemplate = state => state.indexTemplate.fullSel export const getAlias = state => { const indexTemplate = getSelectedIndexTemplate(state); - if (!indexTemplate) { - return undefined; - } - const { settings = {} } = indexTemplate; - if (settings.indexlifecycle) { - return settings.indexlifecycle.rollover_alias; - } - return undefined; + return get(indexTemplate, 'settings.indexlifecycle.rollover_alias'); }; // TODO: add createSelector diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index d70604c63609f..276928867ea2b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -8,11 +8,11 @@ import { + defaultColdPhase, + defaultDeletePhase, defaultHotPhase, defaultWarmPhase, - defaultColdPhase, - defaultDeletePhase -} from '../reducers/policies'; +} from '../defaults'; import { PHASE_HOT, PHASE_WARM, From 8d644d078b0def13dbe56d34addae335ddb21146 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 27 Aug 2018 15:13:59 -0400 Subject: [PATCH 041/102] removing additional spaces from findMatchingNodes call --- .../server/routes/api/nodes/register_details_route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js index 1a0faff7ed5dd..6cd882caf73a0 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js @@ -48,7 +48,7 @@ export function registerDetailsRoute(server) { try { const stats = await fetchNodeStats(callWithRequest); - const response = findMatchingNodes (stats, request.params.nodeAttrs); + const response = findMatchingNodes(stats, request.params.nodeAttrs); reply(response); } catch (err) { if (isEsError(err)) { From 32a72435fb78b6be485885c72aa7fa53c5098c1c Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 27 Aug 2018 15:16:13 -0400 Subject: [PATCH 042/102] changing template to index template in one more place --- .../components/template_selection/template_selection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js index 832a420087dc7..a86be2cde71e1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -79,7 +79,7 @@ export class TemplateSelection extends Component { return ( Select a template} + title={

Select an index template

} fullWidth titleSize="s" description={ From 8592496692c5ed64fc5b51658cd4904c25e7192f Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 30 Aug 2018 10:37:16 -0400 Subject: [PATCH 043/102] fixing issue with error message showing when bootstrap is successful --- x-pack/plugins/index_lifecycle_management/public/api/index.js | 2 +- .../public/sections/wizard/wizard.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index a67e3c22ae48b..778a33e389549 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -46,7 +46,7 @@ export async function saveLifecycle(lifecycle, indexTemplatePatch) { export async function bootstrap(indexName, aliasName) { const response = await httpClient.post(`${apiPrefix}/indices/bootstrap`, { indexName, aliasName }); - return response.data; + return response.status === 200; } export async function getAffectedIndices(indexTemplateName, policyName) { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index 9e85d6677389c..a0dff6e332e30 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -79,8 +79,8 @@ export class Wizard extends Component { return; } - const response = await bootstrap(indexName, aliasName); - if (response && response.acknowledged) { + const bootstrapSuccess = await bootstrap(indexName, aliasName); + if (bootstrapSuccess) { toastNotifications.addSuccess( 'Successfully bootstrapped an index and alias' ); From 6201e608c8d114004a4e898ca6512d00e7ad320d Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 30 Aug 2018 10:58:57 -0400 Subject: [PATCH 044/102] fixing node options for warm and cold phase --- .../index_template/components/configuration/configuration.js | 2 +- .../components/template_selection/template_selection.js | 2 +- .../index_lifecycle_management/public/store/selectors/nodes.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js index ac4d153b08f15..f1f1e28f3e6ec 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -113,7 +113,7 @@ export class Configuration extends Component { await setSelectedNodeAttrs(e.target.value); validate(); }} - options={[{ text: 'Default allocation (don\'t use attributes)', value: '' }, ...nodeOptions]} + options={nodeOptions} /> a.value.localeCompare(b.value)); - return options; + return [{ text: 'Default allocation (don\'t use attributes)', value: '' }, ...options]; } ); From 1282054d2f03e49ee7e72b0ae1a65de8936bf815 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 30 Aug 2018 13:55:07 -0400 Subject: [PATCH 045/102] adding seconds to duration fields to match what ES supports --- .../policy_configuration/components/cold_phase/cold_phase.js | 3 ++- .../components/delete_phase/delete_phase.js | 3 ++- .../policy_configuration/components/hot_phase/hot_phase.js | 3 ++- .../policy_configuration/components/warm_phase/warm_phase.js | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 737da8502a753..9380ce4aa050e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -146,7 +146,8 @@ export class ColdPhase extends PureComponent { } options={[ { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 52226029b1111..ec2bdb72c9007 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -132,7 +132,8 @@ export class DeletePhase extends PureComponent { } options={[ { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 659dadb0d092c..876be9e46fbad 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -196,7 +196,8 @@ export class HotPhase extends PureComponent { }} options={[ { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' } + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index f864c8a883747..ef62533ae8b0d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -193,6 +193,7 @@ export class WarmPhase extends PureComponent { options={[ { value: 'd', text: 'days' }, { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, ]} /> From de40de48dc5f5ad39e3b1f2efe178e49999f0cd5 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 08:46:30 -0400 Subject: [PATCH 046/102] changing icon for enabled steps so it does not look like an error indicator --- .../policy_configuration/components/cold_phase/cold_phase.js | 4 ++-- .../components/delete_phase/delete_phase.js | 4 ++-- .../policy_configuration/components/hot_phase/hot_phase.js | 4 ++-- .../policy_configuration/components/warm_phase/warm_phase.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 9380ce4aa050e..e9caa9574d6bb 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -18,7 +18,7 @@ import { EuiSelect, EuiButtonEmpty, EuiDescribedFormGroup, - EuiBetaBadge, + EuiIcon, EuiButton, } from '@elastic/eui'; import { @@ -80,7 +80,7 @@ export class ColdPhase extends PureComponent {
Cold phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index ec2bdb72c9007..7a75b2345ec0c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -21,7 +21,7 @@ import { EuiFieldNumber, EuiSelect, EuiDescribedFormGroup, - EuiBetaBadge, + EuiIcon, EuiButton, } from '@elastic/eui'; import { @@ -63,7 +63,7 @@ export class DeletePhase extends PureComponent {
Delete phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 876be9e46fbad..ce6ac41846122 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -18,7 +18,7 @@ import { EuiSwitch, EuiFormRow, EuiDescribedFormGroup, - EuiBetaBadge, + EuiIcon, } from '@elastic/eui'; import { LearnMoreLink } from '../../../../../../components/learn_more_link'; import { @@ -70,7 +70,7 @@ export class HotPhase extends PureComponent { title={
Hot phase{' '} - +
} titleSize="s" diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index ef62533ae8b0d..7daa981e264ee 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -19,7 +19,7 @@ import { EuiSwitch, EuiButtonEmpty, EuiDescribedFormGroup, - EuiBetaBadge, + EuiIcon, EuiButton, } from '@elastic/eui'; import { @@ -108,7 +108,7 @@ export class WarmPhase extends PureComponent {
Warm phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} From 121c1965a5feea76951e027fa429a001bf6fb8fa Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 08:59:38 -0400 Subject: [PATCH 047/102] adjusting icon color for enabled lifecycle steps --- .../policy_configuration/components/cold_phase/cold_phase.js | 2 +- .../components/delete_phase/delete_phase.js | 2 +- .../policy_configuration/components/hot_phase/hot_phase.js | 2 +- .../policy_configuration/components/warm_phase/warm_phase.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index e9caa9574d6bb..09ce094c8caf8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -80,7 +80,7 @@ export class ColdPhase extends PureComponent {
Cold phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index 7a75b2345ec0c..aef3aed0240ad 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -63,7 +63,7 @@ export class DeletePhase extends PureComponent {
Delete phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index ce6ac41846122..27c692ea0d914 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -70,7 +70,7 @@ export class HotPhase extends PureComponent { title={
Hot phase{' '} - +
} titleSize="s" diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 7daa981e264ee..14ba9aa5ca494 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -108,7 +108,7 @@ export class WarmPhase extends PureComponent {
Warm phase{' '} {phaseData[PHASE_ENABLED] ? ( - + ) : null}
} From 280d44c78ef60e89078a6c2885f4501421c9dd3a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 09:13:52 -0400 Subject: [PATCH 048/102] fixing issue with editing an existing policy with warm phase on rollover enabled --- .../public/store/selectors/policies.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 276928867ea2b..76ff6759ebf7a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -32,7 +32,8 @@ import { PHASE_REPLICA_COUNT, PHASE_ENABLED, PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, - MAX_SIZE_TYPE_DOCUMENT + MAX_SIZE_TYPE_DOCUMENT, + WARM_PHASE_ON_ROLLOVER } from '../constants'; import { getIndexTemplates } from '.'; @@ -94,11 +95,15 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { policy[PHASE_ROLLOVER_ENABLED] = false; if (phase.after) { - const { size: after, units: afterUnits } = splitSizeAndUnits( - phase.after - ); - policy[PHASE_ROLLOVER_AFTER] = after; - policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + if (phaseName === PHASE_WARM && phase.after === '0s') { + policy[WARM_PHASE_ON_ROLLOVER] = true; + } else { + const { size: after, units: afterUnits } = splitSizeAndUnits( + phase.after + ); + policy[PHASE_ROLLOVER_AFTER] = after; + policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + } } if (phase.actions) { From ea06e41c17935b5780c5ac035aa68ae564a5bce8 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 09:21:17 -0400 Subject: [PATCH 049/102] fixing issue with default unit for age dropdowns --- .../public/store/defaults/cold_phase.js | 2 +- .../public/store/defaults/delete_phase.js | 2 +- .../public/store/defaults/hot_phase.js | 2 +- .../public/store/defaults/warm_phase.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js index b5b54c02239ba..66750a824cca8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js @@ -16,7 +16,7 @@ export const defaultColdPhase = { [PHASE_ENABLED]: false, [PHASE_ROLLOVER_ALIAS]: '', [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 's', + [PHASE_ROLLOVER_AFTER_UNITS]: 'd', [PHASE_NODE_ATTRS]: '', [PHASE_REPLICA_COUNT]: '' }; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js index 38653399035f6..236e20c0c36d4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js @@ -16,5 +16,5 @@ export const defaultDeletePhase = { [PHASE_ROLLOVER_ENABLED]: false, [PHASE_ROLLOVER_ALIAS]: '', [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 's' + [PHASE_ROLLOVER_AFTER_UNITS]: 'd' }; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js index 2053af0c3fc58..d0e45a963505b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js @@ -19,7 +19,7 @@ export const defaultHotPhase = { [PHASE_ROLLOVER_ENABLED]: true, [PHASE_ROLLOVER_ALIAS]: '', [PHASE_ROLLOVER_MAX_AGE]: '', - [PHASE_ROLLOVER_MAX_AGE_UNITS]: 's', + [PHASE_ROLLOVER_MAX_AGE_UNITS]: 'd', [PHASE_ROLLOVER_MAX_SIZE_STORED]: '', [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: 'gb', [PHASE_ROLLOVER_MAX_DOC_SIZE]: '', diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js index 26db268641e37..84ffa9c742043 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js @@ -23,7 +23,7 @@ export const defaultWarmPhase = { [PHASE_FORCE_MERGE_SEGMENTS]: '', [PHASE_FORCE_MERGE_ENABLED]: false, [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 's', + [PHASE_ROLLOVER_AFTER_UNITS]: 'd', [PHASE_NODE_ATTRS]: '', [PHASE_SHRINK_ENABLED]: true, [PHASE_PRIMARY_SHARD_COUNT]: '', From 7dcefbce271b19dcdd945f328ddb7b85c54cdd5a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 10:10:14 -0400 Subject: [PATCH 050/102] fixing issues with shrink action serialization and deserialization --- .../public/store/selectors/policies.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 76ff6759ebf7a..d607e2c1a1745 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -33,7 +33,8 @@ import { PHASE_ENABLED, PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, MAX_SIZE_TYPE_DOCUMENT, - WARM_PHASE_ON_ROLLOVER + WARM_PHASE_ON_ROLLOVER, + PHASE_SHRINK_ENABLED } from '../constants'; import { getIndexTemplates } from '.'; @@ -105,7 +106,9 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; } } - + if (phaseName === PHASE_WARM) { + phase[PHASE_SHRINK_ENABLED] = phase.actions && phase.actions.shrink; + } if (phase.actions) { const actions = phase.actions; @@ -221,7 +224,7 @@ export const phaseToES = (state, phase) => { }; } - if (isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + if (phase[PHASE_SHRINK_ENABLED] && isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { esPhase.actions.shrink = { number_of_shards: phase[PHASE_PRIMARY_SHARD_COUNT] }; From 7fb7509b489d3f5941040060bed99a305be86795 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 12:10:13 -0400 Subject: [PATCH 051/102] fixing issue with deserialization of ES policy for shrink --- .../public/store/selectors/policies.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index d607e2c1a1745..fcb4b402cb15c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -107,7 +107,7 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { } } if (phaseName === PHASE_WARM) { - phase[PHASE_SHRINK_ENABLED] = phase.actions && phase.actions.shrink; + policy[PHASE_SHRINK_ENABLED] = !!(phase.actions && phase.actions.shrink); } if (phase.actions) { const actions = phase.actions; @@ -157,7 +157,6 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { policy[PHASE_REPLICA_COUNT] = replicas.number_of_replicas; } } - return policy; }; From 797675d7cd4435531ba8882702f837e72735fd72 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 31 Aug 2018 12:23:38 -0400 Subject: [PATCH 052/102] removing shrink option from UI when primary shard count is not greater than 1 for hot phase --- .../warm_phase/warm_phase.container.js | 4 +- .../components/warm_phase/warm_phase.js | 57 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js index b224e9b232769..95176ef3498ca 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js @@ -21,8 +21,8 @@ import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../../.. export const WarmPhase = connect( state => ({ phaseData: getPhase(state, PHASE_WARM), - hotPhaseReplicaCount: getSelectedReplicaCount(state), - hotPhasePrimaryShardCount: getSelectedPrimaryShardCount(state), + hotPhaseReplicaCount: Number(getSelectedReplicaCount(state)), + hotPhasePrimaryShardCount: Number(getSelectedPrimaryShardCount(state)), hotPhaseRolloverEnabled: getPhase(state, PHASE_HOT)[PHASE_ROLLOVER_ENABLED], nodeOptions: getNodeOptions(state) }), diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 14ba9aa5ca494..81943d366cec7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -71,14 +71,8 @@ export class WarmPhase extends PureComponent { [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, }).isRequired, - hotPhaseReplicaCount: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, - hotPhasePrimaryShardCount: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, + hotPhaseReplicaCount: PropTypes.number.isRequired, + hotPhasePrimaryShardCount: PropTypes.number.isRequired, nodeOptions: PropTypes.array.isRequired, }; @@ -266,30 +260,33 @@ export class WarmPhase extends PureComponent { - - - -

Shrink

-
- - - Shrink the index into a new index with fewer primary shards.{' '} - - - + {hotPhaseReplicaCount > 1 ? ( + + + +

Shrink

+
+ + + Shrink the index into a new index with fewer primary shards.{' '} + + + - + - { - await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); - validate(); - }} - label="Shrink index" - /> + { + await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); + validate(); + }} + label="Shrink index" + /> +
+ ) : null } From 02dc6cdc76912793a471b744473d694523f57162 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 09:31:42 -0400 Subject: [PATCH 053/102] going straight to create policy when no policies exist --- .../components/policy_selection/policy_selection.js | 8 +++++--- .../public/store/actions/policies.js | 10 +++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index 3939ccf8424f7..f85d992a3b8f3 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -25,10 +25,9 @@ export class PolicySelection extends Component { policies: PropTypes.array.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.fetchPolicies(); } - selectPolicy(policyName) { const policy = this.props.policies.find(policy => policy.name === policyName); this.props.setSelectedPolicy(policy); @@ -38,6 +37,9 @@ export class PolicySelection extends Component { const { policies, selectedPolicyName } = this.props; let existingPoliciesSelect; const policiesExist = policies.length > 0; + if (!policiesExist) { + return null; + } if (policiesExist) { const options = policies.map(item => ({ value: item.name, text: item.name })); options.unshift({ @@ -61,7 +63,7 @@ export class PolicySelection extends Component { return ( {policiesExist ? 'Select or c' : 'C'}reate a policy} + title={

Select or create a policy

} titleSize="s" description={`An index lifecycle policy is a blueprint for transitioning your data over time. diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index fccd46b449260..72132e13f4d93 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -12,6 +12,10 @@ import { toastNotifications } from 'ui/notify'; import { loadPolicies } from '../../api'; import { SET_PHASE_DATA } from '../constants'; export const fetchedPolicies = createAction('FETCHED_POLICIES'); +export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); +export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); +export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); + export const fetchPolicies = () => async dispatch => { let policies; try { @@ -22,11 +26,11 @@ export const fetchPolicies = () => async dispatch => { } dispatch(fetchedPolicies(policies)); + if (policies.length === 0) { + dispatch(setSelectedPolicy()); + } return policies; }; -export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); -export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); -export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); export const setPhaseData = createAction(SET_PHASE_DATA, (phase, key, value) => ({ phase, key, value })); From d2a67632bf98dffb6fe0b688cdf1ede5918d30db Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 13:41:04 -0400 Subject: [PATCH 054/102] improving lifecycle policy selection --- .../policy_configuration.container.js | 12 ++++--- .../policy_configuration.js | 27 ++++++++++++---- .../policy_selection.container.js | 3 +- .../policy_selection/policy_selection.js | 32 +++++++++---------- .../public/store/actions/policies.js | 1 + .../public/store/reducers/policies.js | 4 +++ 6 files changed, 52 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js index 29da1a719d9b5..2db2af570ed44 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js @@ -15,14 +15,16 @@ import { getAliasName, getSaveAsNewPolicy, getSelectedOriginalPolicyName, - getIsSelectedPolicySet + getIsSelectedPolicySet, + getPolicies } from '../../../../store/selectors'; import { setBootstrapEnabled, setIndexName, setAliasName, setSelectedPolicyName, - setSaveAsNewPolicy + setSaveAsNewPolicy, + unsetSelectedPolicy } from '../../../../store/actions'; export const PolicyConfiguration = connect( @@ -35,13 +37,15 @@ export const PolicyConfiguration = connect( indexName: getIndexName(state), aliasName: getAliasName(state), saveAsNewPolicy: getSaveAsNewPolicy(state), - originalPolicyName: getSelectedOriginalPolicyName(state) + originalPolicyName: getSelectedOriginalPolicyName(state), + hasExistingPolicies: getPolicies(state).length > 0 }), { setBootstrapEnabled, setIndexName, setAliasName, setSelectedPolicyName, - setSaveAsNewPolicy + setSaveAsNewPolicy, + unsetSelectedPolicy } )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index f6beddab49c78..91420828e7734 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -15,6 +15,8 @@ import { EuiHorizontalRule, EuiButton, EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { HotPhase } from './components/hot_phase'; import { WarmPhase } from './components/warm_phase'; @@ -63,7 +65,9 @@ export class PolicyConfiguration extends Component { const noErrors = !hasErrors(this.props.errors); return noErrors; }; - + componentDidMount() { + window.scrollTo(0, 0); + } submit = async () => { this.setState({ isShowingErrors: true }); if (await this.validate()) { @@ -84,6 +88,8 @@ export class PolicyConfiguration extends Component { selectedPolicyName, isSelectedPolicySet, errors, + unsetSelectedPolicy, + hasExistingPolicies } = this.props; const { isShowingErrors } = this.state; @@ -98,11 +104,20 @@ export class PolicyConfiguration extends Component {
- -

- {!selectedPolicyName ? 'Create a policy' : `Edit policy ${selectedPolicyName}`} -

-
+ + + +

+ {!selectedPolicyName ? 'Create a lifecycle policy' : `Edit lifecycle policy ${selectedPolicyName}`} +

+
+
+ {hasExistingPolicies ? ( + + Change lifecycle policy + + ) : null} +

Configure the phases of your data and when to transition between them.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js index 618cd3a128bbc..59ce80fc1f0a4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js @@ -10,7 +10,7 @@ import { connect } from 'react-redux'; import { PolicySelection as PresentationComponent } from './policy_selection'; import { - getPolicies, getSelectedPolicyName, + getPolicies, getIsSelectedPolicySet, getSelectedPolicyName, } from '../../../../store/selectors'; import { fetchPolicies, @@ -19,6 +19,7 @@ import { export const PolicySelection = connect( state => ({ + isSelectedPolicySet: getIsSelectedPolicySet(state), policies: getPolicies(state), selectedPolicyName: getSelectedPolicyName(state), }), diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index f85d992a3b8f3..c26768b33495f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -34,18 +34,15 @@ export class PolicySelection extends Component { } render() { - const { policies, selectedPolicyName } = this.props; - let existingPoliciesSelect; + const { isSelectedPolicySet, policies, selectedPolicyName } = this.props; const policiesExist = policies.length > 0; - if (!policiesExist) { + if (isSelectedPolicySet) { return null; } + let existingPoliciesSelect; if (policiesExist) { const options = policies.map(item => ({ value: item.name, text: item.name })); - options.unshift({ - value: '', - text: '-- New Policy --', - }); + options.unshift({ value: '', text: 'Select an existing policy' }); existingPoliciesSelect = ( @@ -63,21 +60,24 @@ export class PolicySelection extends Component { return ( Select or create a policy} + title={

Select or create a lifecycle policy

} titleSize="s" description={`An index lifecycle policy is a blueprint for transitioning your data over time. You can create a new policy${policiesExist ? ' or edit an existing policy and save it with a new name.' : '.'}`} fullWidth > - - {existingPoliciesSelect} - - - this.selectPolicy()}>Create new policy - - - + + + {existingPoliciesSelect} + +

- OR -

+
+ + this.selectPolicy()}>Create lifecycle policy + +
+
); } diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index 72132e13f4d93..74f79799108c0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -13,6 +13,7 @@ import { loadPolicies } from '../../api'; import { SET_PHASE_DATA } from '../constants'; export const fetchedPolicies = createAction('FETCHED_POLICIES'); export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); +export const unsetSelectedPolicy = createAction('UNSET_SELECTED_POLICY'); export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 6afe1b4fc53ae..b304951b18ef1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -11,6 +11,7 @@ import { handleActions } from 'redux-actions'; import { fetchedPolicies, setSelectedPolicy, + unsetSelectedPolicy, setSelectedPolicyName, setSaveAsNewPolicy, setPhaseData @@ -77,6 +78,9 @@ export const policies = handleActions( } }; }, + [unsetSelectedPolicy]() { + return defaultState; + }, [setSelectedPolicyName](state, { payload: name }) { return { ...state, From a327cb8097f060bde95fae5cb3a1ef9aa0a8a934 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 13:50:23 -0400 Subject: [PATCH 055/102] adding active badge instead of checkmark for active lifecycle policy phases --- .../policy_configuration/components/cold_phase/cold_phase.js | 4 ++-- .../components/delete_phase/delete_phase.js | 4 ++-- .../policy_configuration/components/warm_phase/warm_phase.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 09ce094c8caf8..05f385e57dea8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -18,7 +18,7 @@ import { EuiSelect, EuiButtonEmpty, EuiDescribedFormGroup, - EuiIcon, + EuiBadge, EuiButton, } from '@elastic/eui'; import { @@ -80,7 +80,7 @@ export class ColdPhase extends PureComponent {
Cold phase{' '} {phaseData[PHASE_ENABLED] ? ( - + Active ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index aef3aed0240ad..c714990824fa5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -21,7 +21,7 @@ import { EuiFieldNumber, EuiSelect, EuiDescribedFormGroup, - EuiIcon, + EuiBadge, EuiButton, } from '@elastic/eui'; import { @@ -63,7 +63,7 @@ export class DeletePhase extends PureComponent {
Delete phase{' '} {phaseData[PHASE_ENABLED] ? ( - + Active ) : null}
} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 81943d366cec7..0084bbbee82e3 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -19,7 +19,7 @@ import { EuiSwitch, EuiButtonEmpty, EuiDescribedFormGroup, - EuiIcon, + EuiBadge, EuiButton, } from '@elastic/eui'; import { @@ -102,7 +102,7 @@ export class WarmPhase extends PureComponent {
Warm phase{' '} {phaseData[PHASE_ENABLED] ? ( - + Active ) : null}
} From f508177681c11659b5fd59a878e6a9c61e31c237 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 15:32:24 -0400 Subject: [PATCH 056/102] some cleanup of unneeded properties and only showing save as new when it is appropriate --- .../components/hot_phase/hot_phase.js | 4 +- .../components/warm_phase/warm_phase.js | 2 +- .../policy_configuration.container.js | 4 -- .../policy_configuration.js | 2 - .../components/review/review.container.js | 2 + .../wizard/components/review/review.js | 37 +++++++++++-------- .../public/store/reducers/policies.js | 1 + .../public/store/selectors/policies.js | 2 + 8 files changed, 29 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 27c692ea0d914..7af9cb5a6ce46 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -18,7 +18,7 @@ import { EuiSwitch, EuiFormRow, EuiDescribedFormGroup, - EuiIcon, + EuiBadge, } from '@elastic/eui'; import { LearnMoreLink } from '../../../../../../components/learn_more_link'; import { @@ -70,7 +70,7 @@ export class HotPhase extends PureComponent { title={
Hot phase{' '} - + Active
} titleSize="s" diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 0084bbbee82e3..dbdb67220f577 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -138,7 +138,7 @@ export class WarmPhase extends PureComponent { validate(); }} > - Remove warm phase + Deactivate warm phase
diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js index 2db2af570ed44..233e39776265a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js @@ -13,7 +13,6 @@ import { getBootstrapEnabled, getIndexName, getAliasName, - getSaveAsNewPolicy, getSelectedOriginalPolicyName, getIsSelectedPolicySet, getPolicies @@ -23,7 +22,6 @@ import { setIndexName, setAliasName, setSelectedPolicyName, - setSaveAsNewPolicy, unsetSelectedPolicy } from '../../../../store/actions'; @@ -36,7 +34,6 @@ export const PolicyConfiguration = connect( bootstrapEnabled: getBootstrapEnabled(state), indexName: getIndexName(state), aliasName: getAliasName(state), - saveAsNewPolicy: getSaveAsNewPolicy(state), originalPolicyName: getSelectedOriginalPolicyName(state), hasExistingPolicies: getPolicies(state).length > 0 }), @@ -45,7 +42,6 @@ export const PolicyConfiguration = connect( setIndexName, setAliasName, setSelectedPolicyName, - setSaveAsNewPolicy, unsetSelectedPolicy } )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js index 91420828e7734..df406b14c76d0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -35,7 +35,6 @@ import { PolicySelection } from '../policy_selection/policy_selection.container' export class PolicyConfiguration extends Component { static propTypes = { setSelectedPolicyName: PropTypes.func.isRequired, - setSaveAsNewPolicy: PropTypes.func.isRequired, setIndexName: PropTypes.func.isRequired, setAliasName: PropTypes.func.isRequired, setBootstrapEnabled: PropTypes.func.isRequired, @@ -43,7 +42,6 @@ export class PolicyConfiguration extends Component { back: PropTypes.func.isRequired, validate: PropTypes.func.isRequired, selectedPolicyName: PropTypes.string.isRequired, - saveAsNewPolicy: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, bootstrapEnabled: PropTypes.bool.isRequired, indexName: PropTypes.string.isRequired, diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js index dbc2fcae7f535..044648dbbcabd 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js @@ -21,6 +21,7 @@ import { getBootstrapEnabled, getIndexName, getPolicies, + getIsNewPolicy } from '../../../../store/selectors'; import { setSelectedPolicyName, @@ -42,6 +43,7 @@ export const Review = connect( selectedPolicyName: getSelectedPolicyName(state), saveAsNewPolicy: getSaveAsNewPolicy(state), originalPolicyName: getSelectedOriginalPolicyName(state), + isNewPolicy: getIsNewPolicy(state), /* start might go away */ indexName: getIndexName(state), diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index e7248ab8b3107..8191bd7867c3a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -125,6 +125,7 @@ export class Review extends Component { bootstrapEnabled, aliasName, policies, + isNewPolicy } = this.props; const { affectedIndices, isLoadingAffectedIndices, isShowingErrors } = this.state; @@ -213,24 +214,27 @@ export class Review extends Component { ) : null } - - { - await setSaveAsNewPolicy(e.target.checked); - validate(); - }} - label={ - - Save this as a new policy - - } - /> - + { isNewPolicy ? null : ( + + { + await setSaveAsNewPolicy(e.target.checked); + validate(); + }} + label={ + + Save this as a new policy + + } + /> + + )} + ) : null} - {saveAsNewPolicy ? ( + {saveAsNewPolicy || isNewPolicy ? (

Save your work

@@ -267,6 +271,7 @@ export class Review extends Component { color="secondary" iconType="check" onClick={() => done(lifecycle)} + disabled={!selectedPolicyName} > Looks good, save changes diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index b304951b18ef1..5b2c288858385 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -34,6 +34,7 @@ import { export const defaultPolicy = { name: '', saveAsNew: true, + isNew: true, phases: { [PHASE_HOT]: defaultHotPhase, [PHASE_WARM]: defaultWarmPhase, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index fcb4b402cb15c..12fcededa6f09 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -39,6 +39,8 @@ import { import { getIndexTemplates } from '.'; export const getPolicies = state => state.policies.policies; +export const getIsNewPolicy = state => state.policies.selectedPolicy.isNew; + export const getSelectedPolicy = state => state.policies.selectedPolicy; export const getIsSelectedPolicySet = state => state.policies.selectedPolicySet; export const getSelectedOriginalPolicyName = state => state.policies.originalPolicyName; From f5316bb6b1e62efb282b1a7ac2b873ac45ebda75 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 15:35:07 -0400 Subject: [PATCH 057/102] removing stray fullWidth attribute --- .../wizard/components/policy_selection/policy_selection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js index c26768b33495f..e153d9fd1ce03 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -68,7 +68,7 @@ export class PolicySelection extends Component { fullWidth > - + {existingPoliciesSelect}

- OR -

From 979cd62b3787b6f87cd5f9099768f7ca030e8789 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 5 Sep 2018 09:39:50 -0400 Subject: [PATCH 058/102] adding missing minimum for replica count for warm phase --- .../policy_configuration/components/warm_phase/warm_phase.js | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index dbdb67220f577..d18813b1595b6 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -239,6 +239,7 @@ export class WarmPhase extends PureComponent { await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); validate(); }} + min={0} />
From 873d65b6a66ef4afe0c7159e69c90983a5fda384 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 5 Sep 2018 14:52:07 -0400 Subject: [PATCH 059/102] adding scroll to top for review step --- .../public/sections/wizard/components/review/review.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js index 8191bd7867c3a..20302c08de61f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -65,7 +65,9 @@ export class Review extends Component { this.currentFetchTimeout = null; } - + componentDidMount() { + window.scrollTo(0, 0); + } fetchAffectedIndices = () => { if (this.currentFetchTimeout) { clearTimeout(this.currentFetchTimeout); From 8d658a5279956aebde4c8e4ac1c2197cecaae800 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 6 Sep 2018 08:17:28 -0400 Subject: [PATCH 060/102] fixing issue with start warm phase after rollover introduced by time representaiton change from ES --- .../public/store/selectors/policies.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 12fcededa6f09..3b7733e6c4fd0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -98,7 +98,7 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { policy[PHASE_ROLLOVER_ENABLED] = false; if (phase.after) { - if (phaseName === PHASE_WARM && phase.after === '0s') { + if (phaseName === PHASE_WARM && phase.after === '0ms') { policy[WARM_PHASE_ON_ROLLOVER] = true; } else { const { size: after, units: afterUnits } = splitSizeAndUnits( From dcba51389c2c5db5e50ca54ff15a7ef6eb3d4c94 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 6 Sep 2018 08:29:25 -0400 Subject: [PATCH 061/102] making shrink options not show for primary shard count of 1 as you can't shrink in that situation --- .../components/warm_phase/warm_phase.js | 146 +++++++----------- .../public/store/selectors/lifecycle.js | 20 ++- 2 files changed, 70 insertions(+), 96 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index d18813b1595b6..478ea106c89b7 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -51,23 +51,13 @@ export class WarmPhase extends PureComponent { [WARM_PHASE_ON_ROLLOVER]: PropTypes.bool.isRequired, [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, + [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, - [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, - [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, - [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]).isRequired, + [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, }).isRequired, @@ -110,8 +100,8 @@ export class WarmPhase extends PureComponent { description={

- Your index becomes read-only when it enters the warm phase. - You can optimize this phase for search. + Your index becomes read-only when it enters the warm phase. You can optimize this + phase for search.

{isShowingErrors ? ( @@ -125,8 +115,6 @@ export class WarmPhase extends PureComponent { fullWidth > - - {phaseData[PHASE_ENABLED] ? ( @@ -178,10 +166,7 @@ export class WarmPhase extends PureComponent { { - await setPhaseData( - PHASE_ROLLOVER_AFTER_UNITS, - e.target.value - ); + await setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value); validate(); }} options={[ @@ -206,9 +191,7 @@ export class WarmPhase extends PureComponent { phaseData[PHASE_NODE_ATTRS] ? ( - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS]) - } + onClick={() => showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > View node details @@ -248,10 +231,7 @@ export class WarmPhase extends PureComponent { { - await setPhaseData( - PHASE_REPLICA_COUNT, - hotPhaseReplicaCount - ); + await setPhaseData(PHASE_REPLICA_COUNT, hotPhaseReplicaCount); validate(); }} > @@ -261,7 +241,7 @@ export class WarmPhase extends PureComponent {
- {hotPhaseReplicaCount > 1 ? ( + {hotPhasePrimaryShardCount > 1 ? ( @@ -270,9 +250,7 @@ export class WarmPhase extends PureComponent { Shrink the index into a new index with fewer primary shards.{' '} - + @@ -286,64 +264,59 @@ export class WarmPhase extends PureComponent { }} label="Shrink index" /> - - ) : null } - - + {phaseData[PHASE_SHRINK_ENABLED] ? ( + + + + + { + await setPhaseData(PHASE_PRIMARY_SHARD_COUNT, e.target.value); + validate(); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + hotPhasePrimaryShardCount + ); + validate(); + }} + > + Set to same as hot phase + + + + - {phaseData[PHASE_SHRINK_ENABLED] ? ( - - - - - { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - e.target.value - ); - validate(); - }} - min={1} - /> - - - - - { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - hotPhasePrimaryShardCount - ); - validate(); - }} - > - Set to same as hot phase - - - - - - + + + ) : null} ) : null} + +

Force merge

- Reduce the number of segments in your shard by merging smaller - files and clearing deleted ones.{' '} - + Reduce the number of segments in your shard by merging smaller files and clearing + deleted ones. @@ -370,10 +343,7 @@ export class WarmPhase extends PureComponent { { - await setPhaseData( - PHASE_FORCE_MERGE_SEGMENTS, - e.target.value - ); + await setPhaseData(PHASE_FORCE_MERGE_SEGMENTS, e.target.value); validate(); }} min={1} diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index fcbdb66909fc9..45202099dedf8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -37,6 +37,7 @@ import { phaseToES, getSelectedPolicyName, getSelectedIndexTemplateName, + getFullSelectedIndexTemplate, isNumber, getSelectedPrimaryShardCount, getSelectedReplicaCount, @@ -47,7 +48,7 @@ import { getAliasName, } from '.'; -export const validatePhase = (type, phase) => { +export const validatePhase = (type, phase, state) => { const errors = {}; if (!phase[PHASE_ENABLED]) { @@ -87,11 +88,15 @@ export const validatePhase = (type, phase) => { } if (phase[PHASE_SHRINK_ENABLED]) { - if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { - errors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; - } - else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { - errors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers above 0 are allowed.']; + const selectedTemplate = getFullSelectedIndexTemplate(state); + // shrink options not shown in GUI for primary shard count of 1, so don't validate + if (selectedTemplate && selectedTemplate.settings.number_of_shards > 1) { + if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + errors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; + } + else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { + errors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers above 0 are allowed.']; + } } } @@ -110,7 +115,6 @@ export const validatePhase = (type, phase) => { export const validateLifecycle = state => { // This method of deep copy does not always work but it should be fine here const errors = JSON.parse(JSON.stringify(ERROR_STRUCTURE)); - if (!getSelectedIndexTemplateName(state)) { errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][ STRUCTURE_TEMPLATE_NAME @@ -173,7 +177,7 @@ export const validateLifecycle = state => { }; errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM], - ...validatePhase(PHASE_WARM, warmPhase) + ...validatePhase(PHASE_WARM, warmPhase, state) }; errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD] = { ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD], From 33df733e6d3de7fa0483e0511128a0b27187321b Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 6 Sep 2018 09:53:10 -0400 Subject: [PATCH 062/102] fixing issue with editing existing policy and saveAsNew --- .../public/store/selectors/policies.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 3b7733e6c4fd0..665264d679ade 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -171,7 +171,9 @@ export const policyFromES = ({ name, type, phases }) => { [PHASE_WARM]: phaseFromES(phases[PHASE_WARM], PHASE_WARM, defaultWarmPhase), [PHASE_COLD]: phaseFromES(phases[PHASE_COLD], PHASE_COLD, defaultColdPhase), [PHASE_DELETE]: phaseFromES(phases[PHASE_DELETE], PHASE_DELETE, defaultDeletePhase) - } + }, + isNew: false, + saveAsNew: false }; }; From 90d8b84b4fa5b51b43a905be837507ceb794e98a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 29 Aug 2018 11:03:24 -0400 Subject: [PATCH 063/102] bare bones policy table implementation --- .../public/api/index.js | 4 +- .../index_lifecycle_management/public/app.js | 10 +- .../public/register_routes.js | 9 +- .../no_match/components/no_match/index.js | 7 + .../no_match/components/no_match/no_match.js | 18 ++ .../policy_table/components/no_match/index.js | 7 + .../components/policy_table/index.js | 7 + .../policy_table/policy_table.container.js | 43 +++ .../components/policy_table/policy_table.js | 301 ++++++++++++++++++ .../public/sections/policy_table/index.js | 7 + .../public/services/filter_items.js | 17 + .../public/services/index.js | 8 + .../public/services/sort_table.js | 47 +++ .../public/store/actions/policies.js | 5 +- .../public/store/reducers/policies.js | 48 ++- .../public/store/selectors/policies.js | 43 ++- .../api/policies/register_fetch_route.js | 40 ++- 17 files changed, 598 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/no_match.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/services/filter_items.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/services/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/services/sort_table.js diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index 778a33e389549..d72ee9ae2851b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -34,8 +34,8 @@ export async function loadIndexTemplate(templateName) { return response.data; } -export async function loadPolicies() { - const response = await httpClient.get(`${apiPrefix}/policies`); +export async function loadPolicies(withIndices) { + const response = await httpClient.get(`${apiPrefix}/policies${ withIndices ? '?withIndices=true' : ''}`); return response.data; } diff --git a/x-pack/plugins/index_lifecycle_management/public/app.js b/x-pack/plugins/index_lifecycle_management/public/app.js index 78561b785f28c..abcae68e948c4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/app.js +++ b/x-pack/plugins/index_lifecycle_management/public/app.js @@ -5,8 +5,16 @@ */ import React from 'react'; +import { HashRouter, Switch, Route } from 'react-router-dom'; import { Wizard } from './sections/wizard'; +import { PolicyTable } from './sections/policy_table'; +import { BASE_PATH } from '../common/constants'; export const App = () => ( - + + + + + + ); diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js index c601c63299f31..f97c67511cba1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_routes.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -12,6 +12,7 @@ import { setHttpClient } from './api'; import { App } from './app'; import { BASE_PATH } from '../common/constants/base_path'; import { indexLifecycleManagementStore } from './store'; +import { I18nProvider } from '@kbn/i18n/react'; import routes from 'ui/routes'; @@ -20,9 +21,11 @@ import { manageAngularLifecycle } from './lib/manage_angular_lifecycle'; const renderReact = async (elem) => { render( - - - , + + + + + , elem ); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/index.js new file mode 100644 index 0000000000000..2cdbc4a7094a8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/index.js @@ -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 { NoMatch } from './no_match'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/no_match.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/no_match.js new file mode 100644 index 0000000000000..3f8bc17c2b193 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/components/no_match/no_match.js @@ -0,0 +1,18 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n/react'; + +export const NoMatch = () => ( +
+ +
+); + diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/index.js new file mode 100644 index 0000000000000..63e8cdebd9771 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/no_match/index.js @@ -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 { NoMatch } from './components/no_match'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/index.js new file mode 100644 index 0000000000000..1e10c49443cef --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/index.js @@ -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 { PolicyTable } from './policy_table.container'; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js new file mode 100644 index 0000000000000..b2070a8dbc215 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js @@ -0,0 +1,43 @@ +/* + * 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 { connect } from 'react-redux'; +import { PolicyTable as PresentationComponent } from './policy_table'; +import { fetchPolicies, policyFilterChanged, policyPageChanged, policyPageSizeChanged, policySortChanged } from '../../../../store/actions'; +import { getPageOfPolicies, getPolicyPager, getPolicyFilter } from '../../../../store/selectors'; + + +const mapDispatchToProps = (dispatch) => { + return { + filterChanged: (filter) => { + dispatch(policyFilterChanged({ filter })); + }, + pageChanged: (pageNumber) => { + dispatch(policyPageChanged({ pageNumber })); + }, + pageSizeChanged: (pageSize) => { + dispatch(policyPageSizeChanged({ pageSize })); + }, + sortChanged: (sortField, isSortAscending) => { + dispatch(policySortChanged({ sortField, isSortAscending })); + }, + fetchPolicies: (withIndices) => { + dispatch(fetchPolicies(withIndices)); + } + }; +}; + +export const PolicyTable = connect( + state => ({ + policies: getPageOfPolicies(state), + pager: getPolicyPager(state), + filter: getPolicyFilter(state), + }), + mapDispatchToProps +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js new file mode 100644 index 0000000000000..46caba2917aeb --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -0,0 +1,301 @@ +/* + * 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, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import { NoMatch } from '../no_match'; +import { + EuiLink, + EuiCheckbox, + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiSpacer, + EuiTable, + EuiTableBody, + EuiTableHeader, + EuiTableHeaderCell, + EuiTableHeaderCellCheckbox, + EuiTablePagination, + EuiTableRow, + EuiTableRowCell, + EuiTableRowCellCheckbox, + EuiTitle, + EuiText, + EuiPageBody, + EuiPageContent +} from '@elastic/eui'; + +const HEADERS = { + name: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.nameHeader', { + defaultMessage: 'Name', + }), + coveredIndices: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.coveredIndicesHeader', { + defaultMessage: 'Covered Indices', + }), +}; + +export class PolicyTableUi extends Component { + static getDerivedStateFromProps(props, state) { + // Deselect any policies which no longer exist, e.g. they've been deleted. + const { selectedPoliciesMap } = state; + const policyNames = props.policies.map(policy => policy.name); + const selectedPolicyNames = Object.keys(selectedPoliciesMap); + const missingPolicyNames = selectedPolicyNames.filter(selectedpolicyName => { + return !policyNames.includes(selectedpolicyName); + }); + + if (missingPolicyNames.length) { + const newMap = { ...selectedPoliciesMap }; + missingPolicyNames.forEach(missingPolicyName => delete newMap[missingPolicyName]); + return { selectedPoliciesMap: newMap }; + } + + return null; + } + + constructor(props) { + super(props); + + this.state = { + selectedPoliciesMap: {} + }; + } + componentDidMount() { + this.props.fetchPolicies(true); + } + onSort = column => { + const { sortField, isSortAscending, sortChanged } = this.props; + + const newIsSortAscending = sortField === column ? !isSortAscending : true; + sortChanged(column, newIsSortAscending); + }; + + toggleAll = () => { + const allSelected = this.areAllItemsSelected(); + if (allSelected) { + return this.setState({ selectedPoliciesMap: {} }); + } + const { policies } = this.props; + const selectedPoliciesMap = {}; + policies.forEach(({ name }) => { + selectedPoliciesMap[name] = true; + }); + this.setState({ + selectedPoliciesMap + }); + }; + + toggleItem = name => { + this.setState(({ selectedPoliciesMap }) => { + const newMap = { ...selectedPoliciesMap }; + if (newMap[name]) { + delete newMap[name]; + } else { + newMap[name] = true; + } + return { + selectedPoliciesMap: newMap + }; + }); + }; + + isItemSelected = name => { + return !!this.state.selectedPoliciesMap[name]; + }; + + areAllItemsSelected = () => { + const { policies } = this.props; + const policyOfUnselectedItem = policies.find( + policy => !this.isItemSelected(policy.name) + ); + return policyOfUnselectedItem === -1; + }; + + buildHeader() { + const { sortField, isSortAscending } = this.props; + return Object.entries(HEADERS).map(([fieldName, label]) => { + const isSorted = sortField === fieldName; + return ( + this.onSort(fieldName)} + isSorted={isSorted} + isSortAscending={isSortAscending} + data-test-subj={`policyTableHeaderCell-${fieldName}`} + className={'policyTable__header--' + fieldName} + > + {label} + + ); + }); + } + + buildRowCell(fieldName, value) { + if (fieldName === 'name') { + return ( + { + + }} + > + {value} + + ); + } + return value; + } + + buildRowCells(policy) { + return Object.keys(HEADERS).map(fieldName => { + const { name } = policy; + const value = policy[fieldName]; + return ( + + {this.buildRowCell(fieldName, value)} + + ); + }); + } + + buildRows() { + const { policies = [], detailPanelpolicyName } = this.props; + return policies.map(policy => { + const { name } = policy; + return ( + + + { + this.toggleItem(name); + }} + data-test-subj="policyTableRowCheckbox" + /> + + {this.buildRowCells(policy)} + + ); + }); + } + + renderPager() { + const { pager, pageChanged, pageSizeChanged } = this.props; + return ( + + ); + } + + onItemSelectionChanged = selectedPolicies => { + this.setState({ selectedPolicies }); + }; + + render() { + const { + filterChanged, + filter, + policies, + intl, + } = this.props; + + return ( + + + + + + +

+ +

+
+ + +

+ +

+
+
+
+ + + + { + filterChanged(event.target.value); + }} + data-test-subj="policyTableFilterInput" + placeholder={ + intl.formatMessage({ + id: 'xpack.indexLifecycleMgmt.policyTable.systempoliciesSearchInputPlaceholder', + defaultMessage: 'Search', + }) + } + aria-label="Search policies" + /> + + + + + + {policies.length > 0 ? ( + + + + + + {this.buildHeader()} + + {this.buildRows()} + + ) : ( + + )} + + {policies.length > 0 ? this.renderPager() : null} +
+
+
+ ); + } +} + +export const PolicyTable = injectI18n(PolicyTableUi); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/index.js new file mode 100644 index 0000000000000..c4aa32f1f7dc2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/index.js @@ -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 { PolicyTable } from './components/policy_table'; diff --git a/x-pack/plugins/index_lifecycle_management/public/services/filter_items.js b/x-pack/plugins/index_lifecycle_management/public/services/filter_items.js new file mode 100644 index 0000000000000..6d2e3dae57f46 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/services/filter_items.js @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const filterItems = (fields, filter = '', items = []) => { + const lowerFilter = filter.toLowerCase(); + return items.filter(item => { + const actualFields = fields || Object.keys(item); + const indexOfMatch = actualFields.findIndex(field => { + const normalizedField = String(item[field]).toLowerCase(); + return normalizedField.includes(lowerFilter); + }); + return indexOfMatch !== -1; + }); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/services/index.js b/x-pack/plugins/index_lifecycle_management/public/services/index.js new file mode 100644 index 0000000000000..86f51d8b4a4c1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/services/index.js @@ -0,0 +1,8 @@ +/* + * 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 { filterItems } from './filter_items'; +export { sortTable } from './sort_table'; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js b/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js new file mode 100644 index 0000000000000..cead0087e2538 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js @@ -0,0 +1,47 @@ +/* + * 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'; + +const stringSort = (fieldName) => (item) => item[fieldName]; +const numericSort = (fieldName) => (item) => +item[fieldName]; +const unitMagnitude = { + kb: 1, + mb: 2, + gb: 3, + tb: 4, + pb: 5 +}; +const byteSort = (fieldName) => (item) => { + const rawValue = item[fieldName]; + // raw value can be missing if index is closed + if (!rawValue) { + return 0; + } + const matchResult = rawValue.match(/(.*)([kmgtp]b)/); + if (!matchResult) { + return 0; + } + const [ , number, unit] = matchResult; + return +number * Math.pow(1024, unitMagnitude[unit]); +}; + +const sorters = { + name: stringSort('name'), + status: stringSort('status'), + health: stringSort('health'), + primary: numericSort('primary'), + replica: numericSort('replica'), + documents: numericSort('documents'), + size: byteSort('size'), + primary_size: byteSort('primary_size'), +}; +export const sortTable = (array = [], sortField, isSortAscending) => { + const sorted = sortBy(array, sorters[sortField]); + return isSortAscending + ? sorted + : sorted.reverse(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index 74f79799108c0..77ceddb71d2fc 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -17,10 +17,10 @@ export const unsetSelectedPolicy = createAction('UNSET_SELECTED_POLICY'); export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); -export const fetchPolicies = () => async dispatch => { +export const fetchPolicies = (withIndices) => async dispatch => { let policies; try { - policies = await loadPolicies(); + policies = await loadPolicies(withIndices); } catch (err) { return toastNotifications.addDanger(err.data.message); @@ -33,5 +33,4 @@ export const fetchPolicies = () => async dispatch => { return policies; }; - export const setPhaseData = createAction(SET_PHASE_DATA, (phase, key, value) => ({ phase, key, value })); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index 5b2c288858385..bfeb72a6e50e8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -14,7 +14,11 @@ import { unsetSelectedPolicy, setSelectedPolicyName, setSaveAsNewPolicy, - setPhaseData + setPhaseData, + policyFilterChanged, + policyPageChanged, + policyPageSizeChanged, + policySortChanged, } from '../actions'; import { policyFromES } from '../selectors'; import { @@ -48,7 +52,14 @@ const defaultState = { originalPolicyName: undefined, selectedPolicySet: false, selectedPolicy: defaultPolicy, - policies: [] + policies: [], + sort: { + sortField: 'name', + isSortAscending: true + }, + pageSize: 10, + currentPage: 0, + filter: '' }; export const policies = handleActions( @@ -124,6 +135,39 @@ export const policies = handleActions( } } }; + }, + [policyFilterChanged](state, action) { + const { filter } = action.payload; + return { + ...state, + filter, + currentPage: 0 + }; + }, + [policySortChanged](state, action) { + const { sortField, isSortAscending } = action.payload; + + return { + ...state, + sort: { + sortField, + isSortAscending, + } + }; + }, + [policyPageChanged](state, action) { + const { pageNumber } = action.payload; + return { + ...state, + currentPage: pageNumber, + }; + }, + [policyPageSizeChanged](state, action) { + const { pageSize } = action.payload; + return { + ...state, + pageSize + }; } }, defaultState diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 665264d679ade..1cdd3fc4eb442 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -6,7 +6,8 @@ - +import { createSelector } from 'reselect'; +import { Pager } from '@elastic/eui'; import { defaultColdPhase, defaultDeletePhase, @@ -37,6 +38,8 @@ import { PHASE_SHRINK_ENABLED } from '../constants'; import { getIndexTemplates } from '.'; +import { filterItems, sortTable } from '../../services'; + export const getPolicies = state => state.policies.policies; export const getIsNewPolicy = state => state.policies.selectedPolicy.isNew; @@ -44,7 +47,43 @@ export const getIsNewPolicy = state => state.policies.selectedPolicy.isNew; export const getSelectedPolicy = state => state.policies.selectedPolicy; export const getIsSelectedPolicySet = state => state.policies.selectedPolicySet; export const getSelectedOriginalPolicyName = state => state.policies.originalPolicyName; - +export const getPolicyFilter = (state) => state.policies.filter; +export const getPolicySort = (state) => state.policies.sort; +export const getPolicyCurrentPage = (state) => state.policies.currentPage; +export const getPolicyPageSize = (state) => state.policies.pageSize; + +const getFilteredPolicies = createSelector( + getPolicies, + getPolicyFilter, + (policies, filter) => { + return filterItems(['name'], filter, policies); + } +); +export const getTotalPolicies = createSelector( + getFilteredPolicies, + (filteredPolicies) => { + return filteredPolicies.length; + } +); +export const getPolicyPager = createSelector( + getPolicyCurrentPage, + getPolicyPageSize, + getTotalPolicies, + (currentPage, pageSize, totalPolicies) => { + return new Pager(totalPolicies, pageSize, currentPage); + } +); +export const getPageOfPolicies = createSelector( + getFilteredPolicies, + getPolicySort, + getPolicyPager, + (filteredPolicies, sort, pager) => { + const sortedPolicies = sortTable(filteredPolicies, sort.sortField, sort.isSortAscending); + const { firstItemIndex, lastItemIndex } = pager; + const pagedPolicies = sortedPolicies.slice(firstItemIndex, lastItemIndex + 1); + return pagedPolicies; + } +); export const getSaveAsNewPolicy = state => state.policies.selectedPolicy.saveAsNew; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js index 75e88426f65f8..b5647d306491b 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -9,14 +9,14 @@ import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; -function formatHits(hits) { - if (hits.status === 404) { +function formatPolicies(policiesMap) { + if (policiesMap.status === 404) { return []; } - return Object.keys(hits).reduce((accum, lifecycleName) => { - const hit = hits[lifecycleName]; + return Object.keys(policiesMap).reduce((accum, lifecycleName) => { + const policyEntry = policiesMap[lifecycleName]; accum.push({ - ...hit, + ...policyEntry, name: lifecycleName, }); return accum; @@ -33,6 +33,25 @@ async function fetchPolicies(callWithRequest) { return await callWithRequest('transport.request', params); } +async function addCoveredIndices(policiesMap, callWithRequest) { + if (policiesMap.status === 404) { + return policiesMap; + } + const params = { + method: 'GET', + path: '/*/_ilm/explain', + // we allow 404 since they may have no policies + ignore: [ 404 ] + }; + + const policyExplanation = await callWithRequest('transport.request', params); + Object.entries(policyExplanation.indices).forEach(([indexName, { name: policyName = '' }]) => { + if (policyName) { + policiesMap[policyName] = policiesMap[policyName] || []; + policiesMap[policyName].push(indexName); + } + }); +} export function registerFetchRoute(server) { const isEsError = isEsErrorFactory(server); @@ -43,16 +62,17 @@ export function registerFetchRoute(server) { method: 'GET', handler: async (request, reply) => { const callWithRequest = callWithRequestFactory(server, request); - + const { withIndices } = request.query; try { - const hits = await fetchPolicies(callWithRequest); - const response = formatHits(hits); - reply(response); + const policiesMap = await fetchPolicies(callWithRequest); + if (withIndices) { + await addCoveredIndices(policiesMap, callWithRequest); + } + reply(formatPolicies(policiesMap)); } catch (err) { if (isEsError(err)) { return reply(wrapEsError(err)); } - reply(wrapUnknownError(err)); } }, From 1594b52d161c9a1bfea22c1f2459d2ee7b5a5769 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 29 Aug 2018 14:03:06 -0400 Subject: [PATCH 064/102] implementing delete policy behavior --- .../public/api/index.js | 5 ++ .../components/policy_table/confirm_delete.js | 56 +++++++++++++++++++ .../components/policy_table/policy_table.js | 45 +++++++++++++-- .../api/policies/register_delete_route.js | 47 ++++++++++++++++ .../api/policies/register_fetch_route.js | 8 +-- .../api/policies/register_policies_routes.js | 2 + 6 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index d72ee9ae2851b..31f1cb9226910 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -39,6 +39,11 @@ export async function loadPolicies(withIndices) { return response.data; } +export async function deletePolicies(policyNames) { + const response = await httpClient.delete(`${apiPrefix}/policies/${policyNames.join(',')}`); + return response.data; +} + export async function saveLifecycle(lifecycle, indexTemplatePatch) { const response = await httpClient.post(`${apiPrefix}/lifecycle`, { lifecycle, indexTemplatePatch }); return response.data; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js new file mode 100644 index 0000000000000..39e84e739d158 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js @@ -0,0 +1,56 @@ +/* + * 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, { Component, Fragment } from 'react'; +import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; +import { toastNotifications } from 'ui/notify'; +import { deletePolicies } from '../../../../api'; +export class ConfirmDelete extends Component { + deletePolicies = async () => { + const { policiesToDelete, callback } = this.props; + const policyNames = policiesToDelete.map((policy) => { + return policy.name; + }); + try { + await deletePolicies(policyNames); + toastNotifications.addSuccess(`Deleted policies ${policyNames.join(', ')}`); + } catch (e) { + toastNotifications.addDanger(`Error deleting policies ${policyNames.join(', ')}`); + } + if (callback) { + callback(); + } + }; + render() { + const { policiesToDelete, onCancel } = this.props; + const moreThanOne = policiesToDelete.length > 1; + const title = moreThanOne + ? `Delete ${policiesToDelete.length} policies` + : `Delete policy '${policiesToDelete[0]}'`; + return ( + + +
+ +

+ You are about to delete { moreThanOne ? 'these' : 'this' } polic{moreThanOne ? 'ies' : 'y'}: +

+
    {policiesToDelete.map(({ name, coveredIndices }) =>
  • {name} : {coveredIndices.join(',')}
  • )}
+
+

This operation cannot be undone.

+
+
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index 46caba2917aeb..65f1cd1d1e5f5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { NoMatch } from '../no_match'; import { + EuiButton, EuiLink, EuiCheckbox, EuiFieldSearch, @@ -31,6 +32,7 @@ import { EuiPageContent } from '@elastic/eui'; +import { ConfirmDelete } from './confirm_delete'; const HEADERS = { name: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.nameHeader', { defaultMessage: 'Name', @@ -63,12 +65,27 @@ export class PolicyTableUi extends Component { super(props); this.state = { - selectedPoliciesMap: {} + selectedPoliciesMap: {}, + showDeleteConfirmation: false }; } componentDidMount() { this.props.fetchPolicies(true); } + deleteConfirmation() { + if (!this.state.showDeleteConfirmation) { + return null; + } + return ( + + ); + } + handleDelete = () => { + this.props.fetchPolicies(true); + } onSort = column => { const { sortField, isSortAscending, sortChanged } = this.props; @@ -108,13 +125,17 @@ export class PolicyTableUi extends Component { isItemSelected = name => { return !!this.state.selectedPoliciesMap[name]; }; - + getSelectedPolicies() { + return this.props.policies.filter(({ name }) => { + return this.isItemSelected(name); + }); + } areAllItemsSelected = () => { const { policies } = this.props; - const policyOfUnselectedItem = policies.find( + const unselectedItem = policies.find( policy => !this.isItemSelected(policy.name) ); - return policyOfUnselectedItem === -1; + return !unselectedItem; }; buildHeader() { @@ -222,11 +243,14 @@ export class PolicyTableUi extends Component { policies, intl, } = this.props; + const { selectedPoliciesMap } = this.state; + const numSelected = Object.keys(selectedPoliciesMap).length; return ( + {this.deleteConfirmation()} @@ -250,6 +274,17 @@ export class PolicyTableUi extends Component { + {numSelected > 0 ? ( + + this.setState({ showDeleteConfirmation: true })} + > + Delete {numSelected} polic{numSelected > 1 ? 'ies' : 'y'} + + + ) : null} { + const callWithRequest = callWithRequestFactory(server, request); + const { policyNames } = request.params; + try { + await deletePolicies(policyNames, callWithRequest); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js index b5647d306491b..f28e1d8f3e9b9 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -45,10 +45,10 @@ async function addCoveredIndices(policiesMap, callWithRequest) { }; const policyExplanation = await callWithRequest('transport.request', params); - Object.entries(policyExplanation.indices).forEach(([indexName, { name: policyName = '' }]) => { - if (policyName) { - policiesMap[policyName] = policiesMap[policyName] || []; - policiesMap[policyName].push(indexName); + Object.entries(policyExplanation.indices).forEach(([indexName, { policy }]) => { + if (policy) { + policiesMap[policy].coveredIndices = policiesMap[policy].coveredIndices || []; + policiesMap[policy].coveredIndices.push(indexName); } }); } diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js index 676121eadccb1..40cf2430641b5 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js @@ -5,7 +5,9 @@ */ import { registerFetchRoute } from './register_fetch_route'; +import { registerDeleteRoute } from './register_delete_route'; export function registerPoliciesRoutes(server) { registerFetchRoute(server); + registerDeleteRoute(server); } From e8eadf371b393289411ab65de3d54119013411c1 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 16:13:16 -0400 Subject: [PATCH 065/102] fixing sorting and paging --- .../policy_table/policy_table.container.js | 12 ++++---- .../components/policy_table/policy_table.js | 24 ++++++++++----- .../public/services/sort_table.js | 30 ++----------------- .../public/store/actions/policies.js | 5 ++++ 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js index b2070a8dbc215..fa53d471286ae 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.container.js @@ -10,21 +10,21 @@ import { connect } from 'react-redux'; import { PolicyTable as PresentationComponent } from './policy_table'; import { fetchPolicies, policyFilterChanged, policyPageChanged, policyPageSizeChanged, policySortChanged } from '../../../../store/actions'; -import { getPageOfPolicies, getPolicyPager, getPolicyFilter } from '../../../../store/selectors'; +import { getPageOfPolicies, getPolicyPager, getPolicyFilter, getPolicySort } from '../../../../store/selectors'; const mapDispatchToProps = (dispatch) => { return { - filterChanged: (filter) => { + policyFilterChanged: (filter) => { dispatch(policyFilterChanged({ filter })); }, - pageChanged: (pageNumber) => { + policyPageChanged: (pageNumber) => { dispatch(policyPageChanged({ pageNumber })); }, - pageSizeChanged: (pageSize) => { + policyPageSizeChanged: (pageSize) => { dispatch(policyPageSizeChanged({ pageSize })); }, - sortChanged: (sortField, isSortAscending) => { + policySortChanged: (sortField, isSortAscending) => { dispatch(policySortChanged({ sortField, isSortAscending })); }, fetchPolicies: (withIndices) => { @@ -38,6 +38,8 @@ export const PolicyTable = connect( policies: getPageOfPolicies(state), pager: getPolicyPager(state), filter: getPolicyFilter(state), + sortField: getPolicySort(state).sortField, + isSortAscending: getPolicySort(state).isSortAscending, }), mapDispatchToProps )(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index 65f1cd1d1e5f5..6e0a1ccfe51f3 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -87,10 +87,9 @@ export class PolicyTableUi extends Component { this.props.fetchPolicies(true); } onSort = column => { - const { sortField, isSortAscending, sortChanged } = this.props; - + const { sortField, isSortAscending, policySortChanged } = this.props; const newIsSortAscending = sortField === column ? !isSortAscending : true; - sortChanged(column, newIsSortAscending); + policySortChanged(column, newIsSortAscending); }; toggleAll = () => { @@ -170,6 +169,15 @@ export class PolicyTableUi extends Component { {value} ); + } else if (fieldName === 'coveredIndices') { + if (!value) { + return null; + } + return ( + + {value.length} ({value.join(', ')}) + + ); } return value; } @@ -219,15 +227,15 @@ export class PolicyTableUi extends Component { } renderPager() { - const { pager, pageChanged, pageSizeChanged } = this.props; + const { pager, policyPageChanged, policyPageSizeChanged } = this.props; return ( ); } @@ -238,7 +246,7 @@ export class PolicyTableUi extends Component { render() { const { - filterChanged, + policyFilterChanged, filter, policies, intl, @@ -290,7 +298,7 @@ export class PolicyTableUi extends Component { fullWidth value={filter} onChange={event => { - filterChanged(event.target.value); + policyFilterChanged(event.target.value); }} data-test-subj="policyTableFilterInput" placeholder={ diff --git a/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js b/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js index cead0087e2538..c94bb95a85ce0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/services/sort_table.js @@ -7,37 +7,11 @@ import { sortBy } from 'lodash'; const stringSort = (fieldName) => (item) => item[fieldName]; -const numericSort = (fieldName) => (item) => +item[fieldName]; -const unitMagnitude = { - kb: 1, - mb: 2, - gb: 3, - tb: 4, - pb: 5 -}; -const byteSort = (fieldName) => (item) => { - const rawValue = item[fieldName]; - // raw value can be missing if index is closed - if (!rawValue) { - return 0; - } - const matchResult = rawValue.match(/(.*)([kmgtp]b)/); - if (!matchResult) { - return 0; - } - const [ , number, unit] = matchResult; - return +number * Math.pow(1024, unitMagnitude[unit]); -}; +const arraySort = (fieldName) => (item) => (item[fieldName] || []).length; const sorters = { name: stringSort('name'), - status: stringSort('status'), - health: stringSort('health'), - primary: numericSort('primary'), - replica: numericSort('replica'), - documents: numericSort('documents'), - size: byteSort('size'), - primary_size: byteSort('primary_size'), + coveredIndices: arraySort('coveredIndices'), }; export const sortTable = (array = [], sortField, isSortAscending) => { const sorted = sortBy(array, sorters[sortField]); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index 77ceddb71d2fc..d4fdf2d8fb8ae 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -16,6 +16,11 @@ export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); export const unsetSelectedPolicy = createAction('UNSET_SELECTED_POLICY'); export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); +export const policySortChanged = createAction('POLICY_SORT_CHANGED'); +export const policyPageSizeChanged = createAction('POLICY_PAGE_SIZE_CHANGED'); +export const policyPageChanged = createAction('POLICY_PAGE_CHANGED'); +export const policySortDirectionChanged = createAction('POLICY_SORT_DIRECTION_CHANGED'); +export const policyFilterChanged = createAction('POLICY_FILTER_CHANGED'); export const fetchPolicies = (withIndices) => async dispatch => { let policies; From 25a65338acfd1a4195d127cac13e6aac5ee75a42 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 4 Sep 2018 16:25:31 -0400 Subject: [PATCH 066/102] fixing policy table title --- .../policy_table/components/policy_table/policy_table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index 6e0a1ccfe51f3..0c6ebccae283f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -265,7 +265,7 @@ export class PolicyTableUi extends Component {

From 7ae9ea38869300ce682d663f7633a1ccf3943643 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 5 Sep 2018 08:46:45 -0400 Subject: [PATCH 067/102] rudimentary navigation flow --- .../public/register_management_section.js | 2 +- .../public/register_routes.js | 12 ++++++++++-- .../public/sections/wizard/wizard.js | 15 +++++++++++---- .../public/services/navigation.js | 16 ++++++++++++++++ .../public/store/actions/lifecycle.js | 4 +++- 5 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/services/navigation.js diff --git a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js index 16c4de6cf3236..b1364aa7de612 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js @@ -12,6 +12,6 @@ esSection.register('index_lifecycle_management', { visible: true, display: 'Index Lifecycle Management', order: 1, - url: `#${BASE_PATH}home` + url: `#${BASE_PATH}wizard` }); diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js index f97c67511cba1..b2ebd3711c533 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_routes.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -13,6 +13,7 @@ import { App } from './app'; import { BASE_PATH } from '../common/constants/base_path'; import { indexLifecycleManagementStore } from './store'; import { I18nProvider } from '@kbn/i18n/react'; +import { setUrlService } from './services/navigation'; import routes from 'ui/routes'; @@ -34,9 +35,16 @@ routes.when(`${BASE_PATH}:view?/:id?`, { template: template, controllerAs: 'indexManagement', controller: class IndexManagementController { - constructor($scope, $route, $http) { + constructor($scope, $route, $http, kbnUrl, $rootScope) { setHttpClient($http); - + setUrlService({ + change(url) { + console.log("CHANGE"); + kbnUrl.change(url); + $rootScope.$digest(); + console.log("PICHANGE"); + } + }); $scope.$$postDigest(() => { const elem = document.getElementById('indexLifecycleManagementReactRoot'); renderReact(elem); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js index a0dff6e332e30..8d98a13616b99 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -10,6 +10,7 @@ import { toastNotifications } from 'ui/notify'; import { IndexTemplate } from './components/index_template'; import { PolicyConfiguration } from './components/policy_configuration'; import { Review } from './components/review'; +import { goToPolicyList } from '../../services/navigation'; import { EuiPage, EuiPageBody, @@ -68,15 +69,20 @@ export class Wizard extends Component { }; addLifecycle = async lifecycle => { - await this.props.saveLifecycle(lifecycle, this.props.indexTemplatePatch); - await this.bootstrap(); - // this.onSelectedStepChanged(5); + const lifecycleSuccess = await this.props.saveLifecycle(lifecycle, this.props.indexTemplatePatch); + if (!lifecycleSuccess) { + return; + } + const bootstrapSuccess = await this.bootstrap(); + if (bootstrapSuccess) { + goToPolicyList(); + } }; bootstrap = async () => { const { indexName, aliasName, bootstrapEnabled } = this.props; if (!bootstrapEnabled) { - return; + return true; } const bootstrapSuccess = await bootstrap(indexName, aliasName); @@ -90,6 +96,7 @@ export class Wizard extends Component { } else { toastNotifications.addDanger('Unable to bootstrap an index and alias'); } + return bootstrapSuccess; }; renderContent() { diff --git a/x-pack/plugins/index_lifecycle_management/public/services/navigation.js b/x-pack/plugins/index_lifecycle_management/public/services/navigation.js new file mode 100644 index 0000000000000..b7365e8f22ffc --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/services/navigation.js @@ -0,0 +1,16 @@ +/* + * 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. + */ + +let urlService; +import { BASE_PATH } from '../../common/constants'; +export const setUrlService = (aUrlService) => { + urlService = aUrlService; +}; + + +export const goToPolicyList = () => { + urlService.change(`${BASE_PATH}policies`); +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js index 676894d079093..06287ed7160b9 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js @@ -16,10 +16,12 @@ export const saveLifecycle = (lifecycle, indexTemplatePatch) => async dispatch = saved = await saveLifecycleApi(lifecycle, indexTemplatePatch); } catch (err) { - return toastNotifications.addDanger(err.data.message); + toastNotifications.addDanger(err.data.message); + return false; } toastNotifications.addSuccess(`Successfully created lifecycle policy '${lifecycle.name}'`); dispatch(savedLifecycle(saved)); + return true; }; From 548426285ddcf3a2de67fd7fc4a5267e22bc83e9 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 5 Sep 2018 11:19:59 -0400 Subject: [PATCH 068/102] fixing delete --- .../public/register_routes.js | 2 -- .../components/policy_table/confirm_delete.js | 19 +++++++++++++++---- .../components/policy_table/policy_table.js | 1 + .../api/policies/register_delete_route.js | 1 + 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js index b2ebd3711c533..acffbf8717154 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_routes.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -39,10 +39,8 @@ routes.when(`${BASE_PATH}:view?/:id?`, { setHttpClient($http); setUrlService({ change(url) { - console.log("CHANGE"); kbnUrl.change(url); $rootScope.$digest(); - console.log("PICHANGE"); } }); $scope.$$postDigest(() => { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js index 39e84e739d158..3b20042c7f3a4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js @@ -11,9 +11,10 @@ import { deletePolicies } from '../../../../api'; export class ConfirmDelete extends Component { deletePolicies = async () => { const { policiesToDelete, callback } = this.props; - const policyNames = policiesToDelete.map((policy) => { + const policyNames = policiesToDelete.map(policy => { return policy.name; }); + try { await deletePolicies(policyNames); toastNotifications.addSuccess(`Deleted policies ${policyNames.join(', ')}`); @@ -29,7 +30,7 @@ export class ConfirmDelete extends Component { const moreThanOne = policiesToDelete.length > 1; const title = moreThanOne ? `Delete ${policiesToDelete.length} policies` - : `Delete policy '${policiesToDelete[0]}'`; + : `Delete policy '${policiesToDelete[0].name}'`; return ( {}} >

- You are about to delete { moreThanOne ? 'these' : 'this' } polic{moreThanOne ? 'ies' : 'y'}: + You are about to delete {moreThanOne ? 'these' : 'this'} polic + {moreThanOne ? 'ies' : 'y'}:

-
    {policiesToDelete.map(({ name, coveredIndices }) =>
  • {name} : {coveredIndices.join(',')}
  • )}
+
    + {policiesToDelete.map( + (({ name, coveredIndices }) => ( +
  • + {name} {coveredIndices ? `: ${coveredIndices.join(',')}` : null} +
  • + ): null) + )} +

This operation cannot be undone.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index 0c6ebccae283f..a718d6c020a02 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -85,6 +85,7 @@ export class PolicyTableUi extends Component { } handleDelete = () => { this.props.fetchPolicies(true); + this.setState({ showDeleteConfirmation: false }); } onSort = column => { const { sortField, isSortAscending, policySortChanged } = this.props; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js index 4c1175169063e..bd69b3b93531c 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js @@ -33,6 +33,7 @@ export function registerDeleteRoute(server) { const { policyNames } = request.params; try { await deletePolicies(policyNames, callWithRequest); + reply(); } catch (err) { if (isEsError(err)) { return reply(wrapEsError(err)); From 49c4ebe34987feb75ea4266b89aa0d72be890136 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 6 Sep 2018 18:07:41 -0400 Subject: [PATCH 069/102] Index lifecycle management wizard (#21925) * Index lifecycle management wizard * Adding index lifecycle management files * Updates * Fix errors and add flyout for node details * New diff tool * Scroll to change for review diff * Some feedback on copy * Updating copy, moving components around and fixing bugs with the diff view * Add logic to auto enable a phase when something is set * redesign ilm * Adding server api tests * Removing debug and some tweaks from dave's work * Conditionally show this message * Policy selection cleanup * Updates for better UX * [COPYEDIT | ILM] Copyedit of text in index lifecycle management UI * Use better default text * Remove debug * Adding readme and comments * Update readme * Do not need this anymore * Remove debug or commented out code * Remove these - they are in the tests PR * Toggle system indices * Aliases are not defined here anymore * Handle rollover better in warm phase and remove from cold,delete * adding learn more link component and switching over to using that * fixing UI issue when no policies exist * various fixes * some cleanup * moving number_of_replicas due to API change * modifying some messaging * fixing typo * fixing some diff issues and not adding sattr_name if none chosen * making write alias required for template step and making necessary API changes * removing alias definition from template patching as it needs to be per index * some copy edits for clarity * fixing issue with editing existing policy when rollover starts the warm phase * addressing PR feedback on server side code * addressing PR feedback * removing additional spaces from findMatchingNodes call * changing template to index template in one more place * fixing issue with error message showing when bootstrap is successful * fixing node options for warm and cold phase * adding seconds to duration fields to match what ES supports * changing icon for enabled steps so it does not look like an error indicator * adjusting icon color for enabled lifecycle steps * fixing issue with editing an existing policy with warm phase on rollover enabled * fixing issue with default unit for age dropdowns * fixing issues with shrink action serialization and deserialization * fixing issue with deserialization of ES policy for shrink * removing shrink option from UI when primary shard count is not greater than 1 for hot phase * going straight to create policy when no policies exist * improving lifecycle policy selection * adding active badge instead of checkmark for active lifecycle policy phases * some cleanup of unneeded properties and only showing save as new when it is appropriate * removing stray fullWidth attribute * adding missing minimum for replica count for warm phase * adding scroll to top for review step * fixing issue with start warm phase after rollover introduced by time representaiton change from ES * making shrink options not show for primary shard count of 1 as you can't shrink in that situation * fixing issue with editing existing policy and saveAsNew --- x-pack/index.js | 2 + .../index_lifecycle_management/README.md | 72 ++++ .../common/constants/base_path.js | 7 + .../common/constants/index.js | 8 + .../common/constants/plugin.js | 9 + .../index_lifecycle_management/index.js | 38 ++ .../public/api/index.js | 58 +++ .../index_lifecycle_management/public/app.js | 12 + .../public/components/learn_more_link.js | 30 ++ .../public/index.js | 8 + .../public/less/main.less | 49 +++ .../public/lib/diff_ace_addons.js | 134 +++++++ .../public/lib/diff_tools.js | 192 +++++++++ .../public/lib/find_errors.js | 22 ++ .../public/lib/manage_angular_lifecycle.js | 23 ++ .../public/main.html | 3 + .../public/register_management_section.js | 17 + .../public/register_routes.js | 44 +++ .../configuration/configuration.container.js | 41 ++ .../components/configuration/configuration.js | 179 +++++++++ .../components/configuration/index.js | 7 + .../components/template_selection/index.js | 7 + .../template_selection.container.js | 41 ++ .../template_selection/template_selection.js | 163 ++++++++ .../wizard/components/index_template/index.js | 10 + .../index_template/index_template.js | 83 ++++ .../components/node_attrs_details/index.js | 7 + .../node_attrs_details.container.js | 18 + .../node_attrs_details/node_attrs_details.js | 80 ++++ .../cold_phase/cold_phase.container.js | 34 ++ .../components/cold_phase/cold_phase.js | 232 +++++++++++ .../components/cold_phase/index.js | 7 + .../delete_phase/delete_phase.container.js | 20 + .../components/delete_phase/delete_phase.js | 159 ++++++++ .../components/delete_phase/index.js | 7 + .../hot_phase/hot_phase.container.js | 23 ++ .../components/hot_phase/hot_phase.js | 211 ++++++++++ .../components/hot_phase/index.js | 7 + .../components/warm_phase/index.js | 7 + .../warm_phase/warm_phase.container.js | 33 ++ .../components/warm_phase/warm_phase.js | 372 ++++++++++++++++++ .../components/policy_configuration/index.js | 7 + .../policy_configuration.container.js | 47 +++ .../policy_configuration.js | 177 +++++++++ .../components/policy_selection/index.js | 7 + .../policy_selection.container.js | 30 ++ .../policy_selection/policy_selection.js | 84 ++++ .../wizard/components/review/diff_view.js | 107 +++++ .../wizard/components/review/index.js | 7 + .../components/review/review.container.js | 59 +++ .../wizard/components/review/review.js | 286 ++++++++++++++ .../wizard/components/review/review.less | 7 + .../public/sections/wizard/form_errors.js | 36 ++ .../public/sections/wizard/index.js | 7 + .../sections/wizard/wizard.container.js | 37 ++ .../public/sections/wizard/wizard.js | 170 ++++++++ .../public/store/actions/general.js | 14 + .../public/store/actions/index.js | 14 + .../public/store/actions/index_template.js | 107 +++++ .../public/store/actions/lifecycle.js | 25 ++ .../public/store/actions/nodes.js | 47 +++ .../public/store/actions/policies.js | 37 ++ .../public/store/constants.js | 110 ++++++ .../public/store/defaults/cold_phase.js | 22 ++ .../public/store/defaults/delete_phase.js | 20 + .../public/store/defaults/hot_phase.js | 26 ++ .../public/store/defaults/index.js | 10 + .../public/store/defaults/warm_phase.js | 32 ++ .../public/store/index.js | 7 + .../store/middleware/auto_enable_phase.js | 27 ++ .../store/middleware/auto_set_node_attrs.js | 37 ++ .../public/store/middleware/index.js | 9 + ..._selected_policy_from_selected_template.js | 42 ++ .../public/store/reducers/general.js | 38 ++ .../public/store/reducers/index.js | 21 + .../public/store/reducers/index_template.js | 54 +++ .../public/store/reducers/nodes.js | 80 ++++ .../public/store/reducers/policies.js | 130 ++++++ .../public/store/selectors/general.js | 12 + .../public/store/selectors/index.js | 14 + .../public/store/selectors/index_template.js | 177 +++++++++ .../public/store/selectors/lifecycle.js | 238 +++++++++++ .../public/store/selectors/nodes.js | 42 ++ .../public/store/selectors/policies.js | 236 +++++++++++ .../public/store/store.js | 32 ++ .../call_with_request_factory.js | 18 + .../lib/call_with_request_factory/index.js | 7 + .../check_license/__tests__/check_license.js | 146 +++++++ .../server/lib/check_license/check_license.js | 58 +++ .../server/lib/check_license/index.js | 7 + .../__tests__/wrap_custom_error.js | 21 + .../error_wrappers/__tests__/wrap_es_error.js | 39 ++ .../__tests__/wrap_unknown_error.js | 19 + .../server/lib/error_wrappers/index.js | 9 + .../lib/error_wrappers/wrap_custom_error.js | 18 + .../lib/error_wrappers/wrap_es_error.js | 30 ++ .../lib/error_wrappers/wrap_unknown_error.js | 17 + .../__tests__/is_es_error_factory.js | 48 +++ .../server/lib/is_es_error_factory/index.js | 7 + .../is_es_error_factory.js | 18 + .../__tests__/license_pre_routing_factory.js | 72 ++++ .../lib/license_pre_routing_factory/index.js | 7 + .../license_pre_routing_factory.js | 29 ++ .../lib/register_license_checker/index.js | 7 + .../register_license_checker.js | 21 + .../server/routes/api/indices/index.js | 7 + .../api/indices/register_bootstrap_route.js | 50 +++ .../indices/register_get_affected_route.js | 123 ++++++ .../api/indices/register_indices_routes.js | 13 + .../server/routes/api/lifecycle/index.js | 7 + .../api/lifecycle/register_create_route.js | 96 +++++ .../lifecycle/register_lifecycle_routes.js | 11 + .../server/routes/api/nodes/constants.js | 11 + .../server/routes/api/nodes/index.js | 7 + .../api/nodes/register_details_route.js | 65 +++ .../routes/api/nodes/register_list_route.js | 64 +++ .../routes/api/nodes/register_nodes_routes.js | 13 + .../server/routes/api/policies/index.js | 7 + .../api/policies/register_fetch_route.js | 63 +++ .../api/policies/register_policies_routes.js | 11 + .../server/routes/api/templates/index.js | 7 + .../api/templates/register_fetch_route.js | 83 ++++ .../api/templates/register_get_route.js | 52 +++ .../templates/register_templates_routes.js | 16 + .../index_management/public/styles/table.less | 2 +- yarn.lock | 3 - 126 files changed, 6509 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/README.md create mode 100644 x-pack/plugins/index_lifecycle_management/common/constants/base_path.js create mode 100644 x-pack/plugins/index_lifecycle_management/common/constants/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/common/constants/plugin.js create mode 100644 x-pack/plugins/index_lifecycle_management/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/api/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/app.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/less/main.less create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/main.html create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_management_section.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/general.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/constants.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/store/store.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js diff --git a/x-pack/index.js b/x-pack/index.js index a98af06dde131..e3aecdc51ea89 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -20,6 +20,7 @@ import { apm } from './plugins/apm'; import { licenseManagement } from './plugins/license_management'; import { cloud } from './plugins/cloud'; import { indexManagement } from './plugins/index_management'; +import { indexLifecycleManagement } from './plugins/index_lifecycle_management'; import { consoleExtensions } from './plugins/console_extensions'; import { notifications } from './plugins/notifications'; import { kueryAutocomplete } from './plugins/kuery_autocomplete'; @@ -44,6 +45,7 @@ module.exports = function (kibana) { indexManagement(kibana), consoleExtensions(kibana), notifications(kibana), + indexLifecycleManagement(kibana), kueryAutocomplete(kibana) ]; }; diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md new file mode 100644 index 0000000000000..14297875d177c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -0,0 +1,72 @@ +# Index lifecyle management + +## What is it +-- TODO -- + +## UI + +The UI currently consists of a single wizard, broken into three steps. + +### Step 1 +The first step involves choosing the index template in which the created/selected policy will be applied. +Then, it lets the user tweak configuration options on this template including shard and replica count as well as allocation rules. + +### Step 2 +The second step lets the user choose which policy they want to apply to the selected index template. They can choose a new one or select an existing one. Either way, after selection, they will see configuration options for the policy itself. This includes configuration for the hot, warm, cold, and delete phase. + +### Step 3 +The third and last step lets the user name their policy and also see the affected indices and index templates. These indices and index templates are what will be affected once the user saves the work done in the wizard (This includes changes to the index template itself which will change indices created from the template and also changes to a policy that is attached to another index template). The user can also see a visual diff of what will change in the index template. Then, the user clicks the Save button and blamo! + +## UI Architecture + +The UI is built on React and Redux. + +### Redux + +The redux store consists of a few top level attributes: +``` +indexTemplate +nodes +policies +general +``` + +The logic behind the store is separate into four main concerns: +1) reducers/ +2) actions/ +3) selectors/ +4) middleware/ + +The reducers and actions are pretty standard redux, so no need to discuss much there. + +### Selectors + +The selectors showcase how we access any stateful data. All access comes through selectors so if there are any changes required to the state tree, we only need to update the reducers and selectors. + +#### Middleware + +The middleware folder contains specific pieces of state logic we need to handle side effects of certain state changing. + +One example is the `auto_enable_phase.js` middleware. By default, the warm, cold and delete phases are disabled. However, the user can expand the section in the UI and edit configuration without needing to enable/disable the phase. Ideally, once the user edits any configuration piece within a phase, we _assume_ they want that phase enabled so this middleware will detect a change in a phase and enable if it is not enabled already. + +#### Generic phase data + +Each of our four phases have some similar and some unique configuration options. Instead of making each individual phase a specific action for that phase, the code is written more generically to capture any data change within a phase to a single action. Therefore, each phase component's configuration inputs will look similar, like: `setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value)`. The top level container for each phase will handle automatically prefixing the `setPhaseData` prop with the right phase: ` setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value),`. + +To complement this generic logic, there is a list of constants that are used to ensure the right pieces of data are changed. These are contained within `store/constants.js` + +### Diff View + +The third step of the wizard features a visual diff UI component which is custom to this feature. It is based off Ace/Brace and the custom code used to power is it lives in `lib/diff_ace_addons.js` and `lib/diff_tools.js`. The UI parts are in `sections/wizard/components/review/diff_view.js`. See those individual files for more detailed comments/explanations. + +### Validation + +Every step in the wizard features validation and will show error states after the user attempts to move to the next step assuming there are errors on the current page. + +This works by constantly revalidating the entire wizard state after each state change. This is technically optional as the method to trigger validation is manually called in each UI component that triggers a state change. + +It's important to note that the validation logic does not apply to a single step in the wizard, but will always validate the entire state tree. This helps prevent scenarios where a change in a step might invalidate a change in another step and we lose that validation state. + +Once a step change is initiated (like clicking Next Step), the current step is marked as able to see errors and will reject the change if there are errors. It will show a toast to the user that there are errors and make each error visible on the relevant UI control. + +As a way to consolidate showing these errors, there is a custom UI component called `ErrableFormRow` that wraps a `EuiFormRow` and it's child with the appropriate error states when appropriate. diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/base_path.js b/x-pack/plugins/index_lifecycle_management/common/constants/base_path.js new file mode 100644 index 0000000000000..5eea1d0ead4a4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/base_path.js @@ -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 const BASE_PATH = '/management/elasticsearch/index_lifecycle_management/'; diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/index.js b/x-pack/plugins/index_lifecycle_management/common/constants/index.js new file mode 100644 index 0000000000000..59b61f7b99f98 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PLUGIN } from './plugin'; +export { BASE_PATH } from './base_path'; diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/plugin.js b/x-pack/plugins/index_lifecycle_management/common/constants/plugin.js new file mode 100644 index 0000000000000..0261f57a93e8c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/plugin.js @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const PLUGIN = { + ID: 'index_lifecycle_management' +}; diff --git a/x-pack/plugins/index_lifecycle_management/index.js b/x-pack/plugins/index_lifecycle_management/index.js new file mode 100644 index 0000000000000..2824f8e345d50 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/index.js @@ -0,0 +1,38 @@ +/* + * 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 { resolve } from 'path'; +import { registerTemplatesRoutes } from './server/routes/api/templates'; +import { registerNodesRoutes } from './server/routes/api/nodes'; +import { registerPoliciesRoutes } from './server/routes/api/policies'; +import { registerLifecycleRoutes } from './server/routes/api/lifecycle'; +import { registerIndicesRoutes } from './server/routes/api/indices'; +import { registerLicenseChecker } from './server/lib/register_license_checker'; +import { PLUGIN } from './common/constants'; + +export function indexLifecycleManagement(kibana) { + return new kibana.Plugin({ + id: PLUGIN.ID, + publicDir: resolve(__dirname, 'public'), + require: ['kibana', 'elasticsearch', 'xpack_main'], + uiExports: { + managementSections: [ + 'plugins/index_lifecycle_management', + ] + }, + init: function (server) { + registerLicenseChecker(server); + registerTemplatesRoutes(server); + registerNodesRoutes(server); + registerPoliciesRoutes(server); + registerLifecycleRoutes(server); + registerIndicesRoutes(server); + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js new file mode 100644 index 0000000000000..778a33e389549 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -0,0 +1,58 @@ +/* + * 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 chrome from 'ui/chrome'; +let httpClient; +export const setHttpClient = (client) => { + httpClient = client; +}; +const apiPrefix = chrome.addBasePath('/api/index_lifecycle_management'); + +export async function loadNodes() { + const response = await httpClient.get(`${apiPrefix}/nodes/list`); + return response.data; +} + +export async function loadNodeDetails(selectedNodeAttrs) { + const response = await httpClient.get(`${apiPrefix}/nodes/${selectedNodeAttrs}/details`); + return response.data; +} + +export async function loadIndexTemplates() { + const response = await httpClient.get(`${apiPrefix}/templates`); + return response.data; +} + +export async function loadIndexTemplate(templateName) { + if (!templateName) { + return {}; + } + const response = await httpClient.get(`${apiPrefix}/template/${templateName}`); + return response.data; +} + +export async function loadPolicies() { + const response = await httpClient.get(`${apiPrefix}/policies`); + return response.data; +} + +export async function saveLifecycle(lifecycle, indexTemplatePatch) { + const response = await httpClient.post(`${apiPrefix}/lifecycle`, { lifecycle, indexTemplatePatch }); + return response.data; +} + +export async function bootstrap(indexName, aliasName) { + const response = await httpClient.post(`${apiPrefix}/indices/bootstrap`, { indexName, aliasName }); + return response.status === 200; +} + +export async function getAffectedIndices(indexTemplateName, policyName) { + const path = policyName + ? `${apiPrefix}/indices/affected/${indexTemplateName}/${encodeURIComponent(policyName)}` + : `${apiPrefix}/indices/affected/${indexTemplateName}`; + const response = await httpClient.get(path); + return response.data; +} diff --git a/x-pack/plugins/index_lifecycle_management/public/app.js b/x-pack/plugins/index_lifecycle_management/public/app.js new file mode 100644 index 0000000000000..78561b785f28c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/app.js @@ -0,0 +1,12 @@ +/* + * 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 { Wizard } from './sections/wizard'; + +export const App = () => ( + +); diff --git a/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js b/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js new file mode 100644 index 0000000000000..17f6e5e2d1661 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js @@ -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 React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; + +const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`; + + +export class LearnMoreLink extends React.PureComponent { + render() { + const { href, docPath } = this.props; + let url; + if (docPath) { + url = `${esBase}${docPath}`; + } else { + url = href; + } + return ( + + Learn more + + ); + + } +} \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/index.js b/x-pack/plugins/index_lifecycle_management/public/index.js new file mode 100644 index 0000000000000..ccde49edbdf5d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/index.js @@ -0,0 +1,8 @@ +/* + * 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 './register_management_section'; +import './register_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/public/less/main.less b/x-pack/plugins/index_lifecycle_management/public/less/main.less new file mode 100644 index 0000000000000..392512d93d0c3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/less/main.less @@ -0,0 +1,49 @@ +@import (reference) "~ui/styles/variables"; + +#indexLifecycleManagementReactRoot { + background: @globalColorLightestGray; + min-height: 100vh; +} + +.euiPageContent.ilmContent { + max-width: 1000px; + width: 100%; +} + +.ilmHrule { + // Less has a bug with calcs + width: calc(~"100% + 48px") !important; + margin-left: -24px; + margin-right: -24px; +} + +.ilmAlias { + display: inline-block; + background-color: #333; + color: white; + padding: 4px 8px; +} + +.ilmDiff__nav { + padding: 16px; + background: #f5f5f5; +} + +.ilmDiff__code { + +} + +.euiAnimateContentLoad { + animation: euiAnimContentLoad $euiAnimSpeedExtraSlow $euiAnimSlightResistance; +} + +@keyframes euiAnimContentLoad { + 0% { + opacity: 0; + transform: translateY(16px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js new file mode 100644 index 0000000000000..82c6e6b4d1e39 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_ace_addons.js @@ -0,0 +1,134 @@ +/* + * 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 ace from 'brace'; +import { ADDITION_PREFIX, REMOVAL_PREFIX } from './diff_tools'; + +function findInObject(key, obj, debug) { + const objKeys = Object.keys(obj); + for (const objKey of objKeys) { + if (objKey === key) { + return obj[objKey]; + } + if (typeof obj[objKey] === 'object' && !Array.isArray(obj[objKey])) { + const item = findInObject(key, obj[objKey], debug); + if (item !== false) { + return item; + } + } + } + return false; +} + +/** + * Utilty method that will determine if the current key/value pair + * is an addition or removal and return the appropriate ace classes + * for styling. This is called after finding a valid key/value match + * in our custom JSON diff mode for ace. + * + * @param {string} key + * @param {string} val + * @param {object} jsonObject + */ +function getDiffClasses(key, val, jsonObject) { + let value = val; + if (value.endsWith(',')) { + value = value.slice(0, -1); + } + if (value.startsWith('"')) { + value = value.slice(1, -1); + } + + const additionValue = findInObject(`${ADDITION_PREFIX}${key}`, jsonObject); + const removalValue = findInObject(`${REMOVAL_PREFIX}${key}`, jsonObject); + + const isAddition = Array.isArray(additionValue) + ? !!additionValue.find(v => v === value) + : (additionValue === value || (additionValue && value === '{')); + const isRemoval = Array.isArray(removalValue) + ? !!removalValue.find(v => v === value) + : (removalValue === value || (removalValue && value === '{')); + + let diffClasses = ''; + if (isAddition) { + diffClasses = 'diff_addition ace_variable'; + } else if (isRemoval) { + diffClasses = 'diff_removal ace_variable'; + } else { + diffClasses = 'variable'; + } + + return diffClasses; +} + +let currentJsonObject; +const getCurrentJsonObject = () => currentJsonObject; +export const setCurrentJsonObject = jsonObject => currentJsonObject = jsonObject; + +/** + * This function will update the ace editor to support a `DiffJsonMode` that will + * show a merged object (merged through `diff_tools:mergeAndPreserveDuplicateKeys`) + * and highlight additions and removals. The goal of this from a UI perspective is + * to help the user see a visual result of merging two javascript objects. + * + * Read this as a starter: https://github.com/ajaxorg/ace/wiki/Creating-or-Extending-an-Edit-Mode + */ +export const addDiffAddonsForAce = () => { + const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules') + .JsonHighlightRules; + class DiffJsonHighlightRules extends JsonHighlightRules { + constructor(props) { + super(props); + this.$rules = new JsonHighlightRules().getRules(); + + let currentArrayKey; + this.addRules({ + start: [ + { + token: (key, val) => { + return getDiffClasses(key, val, getCurrentJsonObject()); + }, + // This is designed to match a key:value pair represented in JSON + // like: + // "foo": "bar" + // Be aware when tweaking this that there are idiosyncracies with + // how these work internally in ace. + regex: '(?:"([\\w-+]+)"\\s*:\\s*([^\\n\\[]+)$)', + }, + { + token: key => { + currentArrayKey = key; + return 'variable'; + }, + next: 'array', + regex: '(?:"([\\w-+]+)"\\s*:\\s*\\[$)', + }, + ...this.$rules.start, + ], + array: [ + { + token: val => { + return getDiffClasses(currentArrayKey, val, getCurrentJsonObject()); + }, + next: 'start', + regex: '\\s*"([^"]+)"\\s*', + }, + ], + }); + } + } + + const JsonMode = ace.acequire('ace/mode/json').Mode; + class DiffJsonMode extends JsonMode { + constructor(props) { + super(props); + this.HighlightRules = DiffJsonHighlightRules; + } + } + + ace.define('ace/mode/diff_json', [], () => ({ + Mode: DiffJsonMode, + })); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js new file mode 100644 index 0000000000000..c06835261c759 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/diff_tools.js @@ -0,0 +1,192 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const ADDITION_PREFIX = '$$$'; +export const REMOVAL_PREFIX = '^^^'; + +/** + * Utility method that will properly escape the prefixes to use in a valid + * RegExp + * + * @param {string} prefix + */ +const escapePrefix = prefix => + prefix + .split('') + .map(i => `\\${i}`) + .join(''); +const removePrefixRegex = new RegExp( + `(${escapePrefix(ADDITION_PREFIX)})|(${escapePrefix(REMOVAL_PREFIX)})`, + 'g' +); + +export const isBoolean = value => + JSON.parse(value) === true || JSON.parse(value) === false; +const isObject = value => typeof value === 'object' && !Array.isArray(value); + +/** + * Utility method that will determine if the key/value pair provided is different + * than the value found using the key in the provided obj. + * + * @param {object} obj + * @param {string} key + * @param {object} value + */ +const isDifferent = (obj, key, value) => { + // If the object does not contain the key, then ignore since it's not a removal or addition + if (!obj.hasOwnProperty(key)) { + return false; + } + + // If we're dealing with an array, we need something better than a simple === comparison + if (Array.isArray(value)) { + return JSON.stringify(value) !== JSON.stringify(obj[key]); + } + + // If the value is an object, do not try and compare as this is called in a recursive function + // so safely ignore + if (typeof value === 'object') { + return false; + } + + // We should be dealing with primitives so do a basic comparison + return obj[key] !== value; +}; + +/** + * This utility method is called when an object exists in the target object + * but not in the source and we want to mark each part of the object as an + * addition + * + * @param {*} obj + */ +const getAdditions = obj => { + const result = {}; + for (const [key, value] of Object.entries(obj)) { + if (isObject(value)) { + result[`${ADDITION_PREFIX}${key}`] = getAdditions(value); + } else { + result[`${ADDITION_PREFIX}${key}`] = value; + } + } + return result; +}; + +/** + * This method is designed to remove all prefixes from the object previously added + * by `mergeAndPreserveDuplicateKeys` + * + * @param {object} obj + */ +export const removePrefixes = obj => { + if (typeof obj === 'string') { + return obj.replace(removePrefixRegex, ''); + } + + if (!obj || typeof obj !== 'object') { + return obj; + } + + return Object.keys(obj).reduce( + (newObj, key) => ({ + ...newObj, + [key.replace(removePrefixRegex, '')]: obj[key] && typeof obj[key] === 'object' ? + removePrefixes(obj[key]) : + obj[key], + }), {} + ); +}; + +/** + * This function is designed to recursively remove any prefixes added through the + * `mergeAndPreserveDuplicateKeys` process. + * + * @param {string} key + * @param {object} value + */ +const normalizeChange = (key, value) => { + if (typeof value === 'string') { + return { + key: removePrefixes(key), + value: removePrefixes(value) + }; + } + return Object.entries(value).reduce((accum, [key, value]) => { + if (typeof value === 'string') { + return { + key: removePrefixes(key), + value: removePrefixes(value) + }; + } + return normalizeChange(key, value); + }, {}); +}; + +/** + * This function is designed to merge two objects together, but instead of + * overriding key collisions, it will create two keys for each collision - the key + * from the source object will start with the `REMOVAL_PREFIX` and the key from the + * target object will start with the `ADDITION_PREFIX`. The resulting object from + * this function call will contain the merged object and potentially some + * `REMOVAL_PREFIX` and `ADDITION_PREFIX` keys. + * + * @param {object} source + * @param {object} target + */ +export const mergeAndPreserveDuplicateKeys = ( + source, + target, + result = {}, + changes = [] +) => { + for (const [key, value] of Object.entries(source)) { + if (isDifferent(target, key, value)) { + result[`${REMOVAL_PREFIX}${key}`] = value; + result[`${ADDITION_PREFIX}${key}`] = target[key]; + changes.push({ + key, + original: removePrefixes(value), + updated: removePrefixes(target[key]), + }); + } else if (isObject(value)) { + if (target.hasOwnProperty(key)) { + const recurseResult = mergeAndPreserveDuplicateKeys(value, target[key]); + result[key] = recurseResult.result; + changes.push(...recurseResult.changes); + } else { + result[key] = value; + } + } else { + result[key] = value; + } + } + + for (const [key, value] of Object.entries(target)) { + if ( + result.hasOwnProperty(key) || + result.hasOwnProperty(`${REMOVAL_PREFIX}${key}`) || + result.hasOwnProperty(`${ADDITION_PREFIX}${key}`) + ) { + continue; + } + + if (isObject(value)) { + result[`${ADDITION_PREFIX}${key}`] = getAdditions(value); + } else { + result[`${ADDITION_PREFIX}${key}`] = value; + } + + const normalized = normalizeChange(key, result[`${ADDITION_PREFIX}${key}`]); + changes.push({ + key: normalized.key, + updated: normalized.value, + }); + } + return { + result, + changes + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js new file mode 100644 index 0000000000000..39d260b05301e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js @@ -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. + */ + +export const hasErrors = (object, keysToIgnore = []) => { + let errors = false; + for (const [key, value] of Object.entries(object)) { + if (keysToIgnore.includes(key)) continue; + if (Array.isArray(value) && value.length > 0) { + errors = true; + break; + } else if (value) { + errors = hasErrors(value, keysToIgnore); + if (errors) { + break; + } + } + } + return errors; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js new file mode 100644 index 0000000000000..3813e632a0a73 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/lib/manage_angular_lifecycle.js @@ -0,0 +1,23 @@ +/* + * 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 { unmountComponentAtNode } from 'react-dom'; + +export const manageAngularLifecycle = ($scope, $route, elem) => { + const lastRoute = $route.current; + + const deregister = $scope.$on('$locationChangeSuccess', () => { + const currentRoute = $route.current; + if (lastRoute.$$route.template === currentRoute.$$route.template) { + $route.current = lastRoute; + } + }); + + $scope.$on('$destroy', () => { + deregister && deregister(); + elem && unmountComponentAtNode(elem); + }); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/main.html b/x-pack/plugins/index_lifecycle_management/public/main.html new file mode 100644 index 0000000000000..ca86144cbe934 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/main.html @@ -0,0 +1,3 @@ + +
+ diff --git a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js new file mode 100644 index 0000000000000..16c4de6cf3236 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js @@ -0,0 +1,17 @@ +/* + * 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 { management } from 'ui/management'; +import { BASE_PATH } from '../common/constants'; + +const esSection = management.getSection('elasticsearch'); +esSection.register('index_lifecycle_management', { + visible: true, + display: 'Index Lifecycle Management', + order: 1, + url: `#${BASE_PATH}home` +}); + diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js new file mode 100644 index 0000000000000..c601c63299f31 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { render } from 'react-dom'; +import { Provider } from 'react-redux'; +import { setHttpClient } from './api'; + +import { App } from './app'; +import { BASE_PATH } from '../common/constants/base_path'; +import { indexLifecycleManagementStore } from './store'; + +import routes from 'ui/routes'; + +import template from './main.html'; +import { manageAngularLifecycle } from './lib/manage_angular_lifecycle'; + +const renderReact = async (elem) => { + render( + + + , + elem + ); +}; + +routes.when(`${BASE_PATH}:view?/:id?`, { + template: template, + controllerAs: 'indexManagement', + controller: class IndexManagementController { + constructor($scope, $route, $http) { + setHttpClient($http); + + $scope.$$postDigest(() => { + const elem = document.getElementById('indexLifecycleManagementReactRoot'); + renderReact(elem); + manageAngularLifecycle($scope, $route, elem); + }); + } + } +}); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js new file mode 100644 index 0000000000000..01ee20f34d5db --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js @@ -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 { connect } from 'react-redux'; +import { Configuration as PresentationComponent } from './configuration'; +import { + getNodeOptions, + getSelectedPrimaryShardCount, + getSelectedReplicaCount, + getSelectedNodeAttrs, + getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount, +} from '../../../../../../store/selectors'; +import { + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + fetchNodes +} from '../../../../../../store/actions'; + +export const Configuration = connect( + state => ({ + nodeOptions: getNodeOptions(state), + selectedNodeAttrs: getSelectedNodeAttrs(state), + selectedPrimaryShardCount: getSelectedPrimaryShardCount(state), + selectedReplicaCount: getSelectedReplicaCount(state), + selectedNodeAttrs: getSelectedNodeAttrs(state), + isPrimaryShardCountHigherThanSelectedNodeAttrsCount: getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount(state), + }), + { + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js new file mode 100644 index 0000000000000..f1f1e28f3e6ec --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js @@ -0,0 +1,179 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiSelect, + EuiFieldNumber, + EuiCallOut, + EuiButtonEmpty, + EuiDescribedFormGroup, +} from '@elastic/eui'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; +import { + STRUCTURE_NODE_ATTRS, + STRUCTURE_PRIMARY_NODES, + STRUCTURE_REPLICAS, +} from '../../../../../../store/constants'; + +import { ErrableFormRow } from '../../../../form_errors'; +import { NodeAttrsDetails } from '../../../node_attrs_details'; + +export class Configuration extends Component { + static propTypes = { + fetchNodes: PropTypes.func.isRequired, + setSelectedNodeAttrs: PropTypes.func.isRequired, + setSelectedPrimaryShardCount: PropTypes.func.isRequired, + setSelectedReplicaCount: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + selectedPrimaryShardCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + selectedNodeAttrs: PropTypes.string.isRequired, + nodeOptions: PropTypes.array.isRequired, + selectedReplicaCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + isPrimaryShardCountHigherThanSelectedNodeAttrsCount: PropTypes.bool.isRequired, + }; + + constructor(props) { + super(props); + this.state = { + isShowingNodeDetailsFlyout: false, + }; + } + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + validate, + + nodeOptions, + selectedPrimaryShardCount, + selectedReplicaCount, + selectedNodeAttrs, + errors, + isShowingErrors, + isPrimaryShardCountHigherThanSelectedNodeAttrsCount, + } = this.props; + + const primaryNodeErrors = isPrimaryShardCountHigherThanSelectedNodeAttrsCount ? ( + + The shard count should be lower than the number of nodes that match the selected attributes. + + ) : null; + + return ( +
+ Configure hot indices} + titleSize="s" + description="A hot index is actively being written to." + fullWidth + > + this.setState({ isShowingNodeDetailsFlyout: true })} + > + View a list of nodes attached to this configuration + + ) : null + } + > + { + await setSelectedNodeAttrs(e.target.value); + validate(); + }} + options={nodeOptions} + /> + + + The best way to determine how many shards you need is to benchmark using realistic + data and queries on your hardware.{' '} + +

+ } + /> + + + + + { + await setSelectedPrimaryShardCount(e.target.value); + validate(); + }} + value={selectedPrimaryShardCount} + min={1} + /> + + + + + { + await setSelectedReplicaCount(e.target.value); + validate(); + }} + value={selectedReplicaCount} + min={0} + /> + + + + {this.state.isShowingNodeDetailsFlyout ? ( + this.setState({ isShowingNodeDetailsFlyout: false })} + /> + ) : null} + + {primaryNodeErrors} +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js new file mode 100644 index 0000000000000..2a387bd853de8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js @@ -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 { Configuration } from './configuration.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js new file mode 100644 index 0000000000000..a7c5cfb8f3dab --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js @@ -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 { TemplateSelection } from './template_selection.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js new file mode 100644 index 0000000000000..fb11120d37d3c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js @@ -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 { connect } from 'react-redux'; +import { TemplateSelection as PresentationComponent } from './template_selection'; +import { + getIndexTemplateOptions, + getSelectedIndexTemplateName, + getIndexName, + getAliasName, + getBootstrapEnabled, + getSelectedIndexTemplateIndices, +} from '../../../../../../store/selectors'; +import { + fetchIndexTemplates, + setSelectedIndexTemplate, + setAliasName, + setBootstrapEnabled, + setIndexName +} from '../../../../../../store/actions'; + +export const TemplateSelection = connect( + state => ({ + templateOptions: getIndexTemplateOptions(state), + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + bootstrapEnabled: getBootstrapEnabled(state), + aliasName: getAliasName(state), + indexName: getIndexName(state), + selectedIndexTemplateIndices: getSelectedIndexTemplateIndices(state), + }), + { + fetchIndexTemplates, + setSelectedIndexTemplate, + setBootstrapEnabled, + setIndexName, + setAliasName, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js new file mode 100644 index 0000000000000..c72cbdf641337 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js @@ -0,0 +1,163 @@ +/* + * 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, Component } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiSelect, + EuiFormRow, + EuiSwitch, + EuiFieldText, + EuiDescribedFormGroup, + EuiSpacer, +} from '@elastic/eui'; + +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; +import { ErrableFormRow } from '../../../../form_errors'; +import { + STRUCTURE_TEMPLATE_NAME, + STRUCTURE_INDEX_NAME, + STRUCTURE_ALIAS_NAME, +} from '../../../../../../store/constants'; + +export class TemplateSelection extends Component { + static propTypes = { + fetchIndexTemplates: PropTypes.func.isRequired, + setSelectedIndexTemplate: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + selectedIndexTemplateName: PropTypes.string.isRequired, + templateOptions: PropTypes.array.isRequired, + errors: PropTypes.object.isRequired, + isShowingErrors: PropTypes.bool.isRequired, + }; + + constructor(props) { + super(props); + this.state = { + isIncludingSystemIndices: false, + }; + } + + componentWillMount() { + this.props.fetchIndexTemplates(); + } + + onChangeIncludingSystemIndices = e => { + this.setState({ isIncludingSystemIndices: e.target.checked }); + }; + + render() { + const { + setSelectedIndexTemplate, + validate, + setBootstrapEnabled, + setIndexName, + setAliasName, + + bootstrapEnabled, + selectedIndexTemplateIndices, + indexName, + aliasName, + selectedIndexTemplateName, + errors, + isShowingErrors, + } = this.props; + + const { isIncludingSystemIndices } = this.state; + + const templateOptions = this.props.templateOptions.filter(option => { + if (option.value && option.value.startsWith('.') && !isIncludingSystemIndices) { + return false; + } + return true; + }); + + return ( + Select an index template} + fullWidth + titleSize="s" + description={ +

+ An index template defines the settings, mappings, and aliases to apply + when you create an index.{' '} + +

+ } + > + + + + { + await setSelectedIndexTemplate(e.target.value); + validate(); + }} + options={templateOptions} + /> + + + { + await setAliasName(e.target.value); + validate(); + }} + /> + + {selectedIndexTemplateName && selectedIndexTemplateIndices.length === 0 ? ( + + + setBootstrapEnabled(e.target.checked)} + label={Create an index with this index template} + /> + + {bootstrapEnabled ? ( + + + { + await setIndexName(e.target.value); + validate(); + }} + /> + + + ) : null} + + ) : null} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js new file mode 100644 index 0000000000000..20271f5f66ec2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js @@ -0,0 +1,10 @@ +/* + * 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 { IndexTemplate } from './index_template'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js new file mode 100644 index 0000000000000..70566e84867df --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js @@ -0,0 +1,83 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; + +import { TemplateSelection } from './components/template_selection'; +import { Configuration } from './components/configuration'; + +import { + EuiHorizontalRule, + EuiButton, +} from '@elastic/eui'; +import { hasErrors } from '../../../../lib/find_errors'; +import { + STRUCTURE_TEMPLATE_SELECTION, + STRUCTURE_CONFIGURATION, +} from '../../../../store/constants'; + +export class IndexTemplate extends Component { + static propTypes = { + done: PropTypes.func.isRequired, + + errors: PropTypes.object, + }; + + constructor(props) { + super(props); + this.state = { + isShowingErrors: false, + }; + } + + validate = async () => { + await this.props.validate(); + const noErrors = !hasErrors(this.props.errors); + return noErrors; + }; + + submit = async () => { + this.setState({ isShowingErrors: true }); + if (await this.validate()) { + this.props.done(); + } else { + toastNotifications.addDanger('Please fix the errors on the page'); + } + }; + + render() { + const { errors } = this.props; + const { isShowingErrors } = this.state; + + return ( +
+ + + + + + + Continue + +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js new file mode 100644 index 0000000000000..885e965c46c4b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js @@ -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 { NodeAttrsDetails } from './node_attrs_details.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js new file mode 100644 index 0000000000000..3128a38c2c34f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js @@ -0,0 +1,18 @@ +/* + * 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 { connect } from 'react-redux'; +import { NodeAttrsDetails as PresentationComponent } from './node_attrs_details'; +import { getNodeDetails, getExistingAllocationRules } from '../../../../store/selectors'; +import { fetchNodeDetails } from '../../../../store/actions'; + +export const NodeAttrsDetails = connect( + (state, ownProps) => ({ + details: getNodeDetails(state, ownProps.selectedNodeAttrs), + allocationRules: getExistingAllocationRules(state), + }), + { fetchNodeDetails } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js new file mode 100644 index 0000000000000..7cb2977e706ab --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js @@ -0,0 +1,80 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyout, + EuiTitle, + EuiInMemoryTable, + EuiSpacer, + EuiButtonEmpty, + EuiCallOut, + EuiPortal, +} from '@elastic/eui'; + +export class NodeAttrsDetails extends PureComponent { + static propTypes = { + fetchNodeDetails: PropTypes.func.isRequired, + close: PropTypes.func.isRequired, + + details: PropTypes.array, + selectedNodeAttrs: PropTypes.string.isRequired, + allocationRules: PropTypes.object, + }; + + componentWillMount() { + this.props.fetchNodeDetails(this.props.selectedNodeAttrs); + } + + render() { + const { selectedNodeAttrs, allocationRules, details, close } = this.props; + + return ( + + + + +

Nodes that contain the attribute: `{selectedNodeAttrs}`

+
+ + {allocationRules ? ( + + + Be aware that this index template has existing allocation rules + which will affect the list of nodes these indices can be allocated to. + + + + ) : null} + +
+ + + Close + + +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js new file mode 100644 index 0000000000000..bfeb21f8d32f1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js @@ -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 { connect } from 'react-redux'; +import { ColdPhase as PresentationComponent } from './cold_phase'; +import { + getNodeOptions, + getPhase, + getPhaseData +} from '../../../../../../store/selectors'; +import { setPhaseData, fetchNodes } from '../../../../../../store/actions'; +import { + PHASE_COLD, + PHASE_WARM, + PHASE_REPLICA_COUNT +} from '../../../../../../store/constants'; + +export const ColdPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_COLD), + nodeOptions: getNodeOptions(state), + warmPhaseReplicaCount: getPhaseData(state, PHASE_WARM, PHASE_REPLICA_COUNT) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value), + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js new file mode 100644 index 0000000000000..05f385e57dea8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -0,0 +1,232 @@ +/* + * 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, { PureComponent, Fragment } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiButtonEmpty, + EuiDescribedFormGroup, + EuiBadge, + EuiButton, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_NODE_ATTRS, + PHASE_REPLICA_COUNT +} from '../../../../../../store/constants'; +import { ErrableFormRow } from '../../../../form_errors'; + +export class ColdPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + showNodeDetailsFlyout: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, + [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired + }).isRequired, + warmPhaseReplicaCount: PropTypes.number.isRequired, + nodeOptions: PropTypes.array.isRequired + }; + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + setPhaseData, + validate, + showNodeDetailsFlyout, + + phaseData, + nodeOptions, + warmPhaseReplicaCount, + errors, + isShowingErrors + } = this.props; + + return ( + + Cold phase{' '} + {phaseData[PHASE_ENABLED] ? ( + Active + ) : null} +
+ } + titleSize="s" + description={ + +

+ A cold index is queried less frequently + and thus no longer needs to be on the most performant hardware. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + {phaseData[PHASE_ENABLED] ? ( + + +
+ + { + await setPhaseData(PHASE_ENABLED, false); + validate(); + }} + > + Deactive cold phase + +
+ + + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + min={1} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, + ]} + /> + + + + + + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} + > + View a list of nodes attached to this configuration + + ) : null} + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + validate(); + }} + min={0} + /> + + + + + + setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) + } + > + Set to same as warm phase + + + + +
+ ) : ( +
+ + { + await setPhaseData(PHASE_ENABLED, true); + validate(); + }} + > + Activate cold phase + +
+ )} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js new file mode 100644 index 0000000000000..e0d70ceb57726 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js @@ -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 { ColdPhase } from './cold_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js new file mode 100644 index 0000000000000..661489d2d9aa9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { connect } from 'react-redux'; +import { DeletePhase as PresentationComponent } from './delete_phase'; +import { getPhase } from '../../../../../../store/selectors'; +import { setPhaseData } from '../../../../../../store/actions'; +import { PHASE_DELETE } from '../../../../../../store/constants'; + +export const DeletePhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_DELETE) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_DELETE, key, value) + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js new file mode 100644 index 0000000000000..c714990824fa5 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -0,0 +1,159 @@ +/* + * 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, { PureComponent, Fragment } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiDescribedFormGroup, + EuiBadge, + EuiButton, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, +} from '../../../../../../store/constants'; +import { ErrableFormRow } from '../../../../form_errors'; + +export class DeletePhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired + }).isRequired + }; + + render() { + const { + setPhaseData, + validate, + phaseData, + errors, + isShowingErrors + } = this.props; + + return ( + + Delete phase{' '} + {phaseData[PHASE_ENABLED] ? ( + Active + ) : null} +
+ } + titleSize="s" + description={ + +

+ Use this phase to define how long to retain your data. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + {phaseData[PHASE_ENABLED] ? ( + + +
+ + { + await setPhaseData(PHASE_ENABLED, false); + validate(); + }} + > + Deactive delete phase + +
+ + + +

Configuration

+
+ + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + min={1} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, + ]} + /> + + + +
+ ) : ( +
+ + { + await setPhaseData(PHASE_ENABLED, true); + validate(); + }} + > + Activate delete phase + +
+ )} + + ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js new file mode 100644 index 0000000000000..5f909ab2c0f79 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js @@ -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 { DeletePhase } from './delete_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js new file mode 100644 index 0000000000000..1db810b49c19c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js @@ -0,0 +1,23 @@ +/* + * 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 { connect } from 'react-redux'; +import { HotPhase as PresentationComponent } from './hot_phase'; +import { getPhase } from '../../../../../../store/selectors'; +import { setPhaseData } from '../../../../../../store/actions'; +import { PHASE_HOT } from '../../../../../../store/constants'; + +export const HotPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_HOT) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_HOT, key, value) + }, +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js new file mode 100644 index 0000000000000..7af9cb5a6ce46 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -0,0 +1,211 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFieldNumber, + EuiSelect, + EuiSwitch, + EuiFormRow, + EuiDescribedFormGroup, + EuiBadge, +} from '@elastic/eui'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; +import { + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_ROLLOVER_ENABLED, + MAX_SIZE_TYPE_DOCUMENT +} from '../../../../../../store/constants'; + +import { ErrableFormRow } from '../../../../form_errors'; + +export class HotPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MAX_AGE]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MAX_AGE_UNITS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MAX_SIZE_STORED]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: PropTypes.string.isRequired + }).isRequired + }; + + render() { + const { + validate, + setPhaseData, + + phaseData, + isShowingErrors, + errors, + } = this.props; + + return ( + + Hot phase{' '} + Active +
+ } + titleSize="s" + description={ + +

+ This phase is required. A hot index is being queried and actively written to. + You can optimize this phase for write throughput. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + + If true, rollover the index when it gets too big or too old. The alias switches to the new index.{' '} + +

+ } + > + { + await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); + validate(); + }} + label="Enable rollover" + /> +
+ {phaseData[PHASE_ROLLOVER_ENABLED] ? ( + + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED, + e.target.value + ); + validate(); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'gb', text: 'gigabytes' }, + { value: MAX_SIZE_TYPE_DOCUMENT, text: 'documents' } + ]} + /> + + + + + + + + { + await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); + validate(); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_AGE_UNITS, + e.target.value + ); + validate(); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, + ]} + /> + + + + + ) : null} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js new file mode 100644 index 0000000000000..114e34c3ef4d0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js @@ -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 { HotPhase } from './hot_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js new file mode 100644 index 0000000000000..7eb5def486c87 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js @@ -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 { WarmPhase } from './warm_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js new file mode 100644 index 0000000000000..95176ef3498ca --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js @@ -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 { connect } from 'react-redux'; +import { WarmPhase as PresentationComponent } from './warm_phase'; +import { + getNodeOptions, + getPhase, + getSelectedReplicaCount, + getSelectedPrimaryShardCount +} from '../../../../../../store/selectors'; +import { setPhaseData, fetchNodes } from '../../../../../../store/actions'; +import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../../../store/constants'; + +export const WarmPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_WARM), + hotPhaseReplicaCount: Number(getSelectedReplicaCount(state)), + hotPhasePrimaryShardCount: Number(getSelectedPrimaryShardCount(state)), + hotPhaseRolloverEnabled: getPhase(state, PHASE_HOT)[PHASE_ROLLOVER_ENABLED], + nodeOptions: getNodeOptions(state) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_WARM, key, value), + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js new file mode 100644 index 0000000000000..478ea106c89b7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -0,0 +1,372 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiSwitch, + EuiButtonEmpty, + EuiDescribedFormGroup, + EuiBadge, + EuiButton, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + WARM_PHASE_ON_ROLLOVER, + PHASE_ROLLOVER_ALIAS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_NODE_ATTRS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_SHRINK_ENABLED, +} from '../../../../../../store/constants'; +import { ErrableFormRow } from '../../../../form_errors'; +import { LearnMoreLink } from '../../../../../../components/learn_more_link'; + +export class WarmPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + showNodeDetailsFlyout: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [WARM_PHASE_ON_ROLLOVER]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, + [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, + [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, + }).isRequired, + + hotPhaseReplicaCount: PropTypes.number.isRequired, + hotPhasePrimaryShardCount: PropTypes.number.isRequired, + + nodeOptions: PropTypes.array.isRequired, + }; + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + validate, + setPhaseData, + showNodeDetailsFlyout, + + phaseData, + hotPhaseReplicaCount, + hotPhasePrimaryShardCount, + nodeOptions, + errors, + isShowingErrors, + hotPhaseRolloverEnabled, + } = this.props; + + return ( + + Warm phase{' '} + {phaseData[PHASE_ENABLED] ? ( + Active + ) : null} +
+ } + titleSize="s" + description={ + +

+ Your index becomes read-only when it enters the warm phase. You can optimize this + phase for search. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + + {phaseData[PHASE_ENABLED] ? ( + + +
+ { + await setPhaseData(PHASE_ENABLED, false); + validate(); + }} + > + Deactivate warm phase + +
+
+ {hotPhaseRolloverEnabled ? ( + + { + await setPhaseData(WARM_PHASE_ON_ROLLOVER, e.target.checked); + validate(); + }} + /> + + ) : null} + {!phaseData[WARM_PHASE_ON_ROLLOVER] ? ( + + + + { + setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + validate(); + }} + min={1} + /> + + + + + { + await setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value); + validate(); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + { value: 's', text: 'seconds' }, + ]} + /> + + + + ) : null} + + + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} + > + View node details + + ) : null + } + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + validate(); + }} + /> + + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + validate(); + }} + min={0} + /> + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, hotPhaseReplicaCount); + validate(); + }} + > + Set to same as hot phase + + + + + + {hotPhasePrimaryShardCount > 1 ? ( + + + +

Shrink

+
+ + + Shrink the index into a new index with fewer primary shards.{' '} + + + + + + + { + await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); + validate(); + }} + label="Shrink index" + /> + {phaseData[PHASE_SHRINK_ENABLED] ? ( + + + + + { + await setPhaseData(PHASE_PRIMARY_SHARD_COUNT, e.target.value); + validate(); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + hotPhasePrimaryShardCount + ); + validate(); + }} + > + Set to same as hot phase + + + + + + + + ) : null} +
+ ) : null} + + + + +

Force merge

+
+ + + Reduce the number of segments in your shard by merging smaller files and clearing + deleted ones. + + + + + + { + await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); + validate(); + }} + /> + + + + {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( + + { + await setPhaseData(PHASE_FORCE_MERGE_SEGMENTS, e.target.value); + validate(); + }} + min={1} + /> + + ) : null} +
+ ) : ( + +
+ { + await setPhaseData(PHASE_ENABLED, true); + validate(); + }} + > + Activate warm phase + +
+
+ )} +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js new file mode 100644 index 0000000000000..d8a885b0b32f3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js @@ -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 { PolicyConfiguration } from './policy_configuration.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js new file mode 100644 index 0000000000000..233e39776265a --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js @@ -0,0 +1,47 @@ +/* + * 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 { connect } from 'react-redux'; +import { PolicyConfiguration as PresentationComponent } from './policy_configuration'; +import { + getSelectedPolicyName, + getAffectedIndexTemplates, + getSelectedIndexTemplateName, + getBootstrapEnabled, + getIndexName, + getAliasName, + getSelectedOriginalPolicyName, + getIsSelectedPolicySet, + getPolicies +} from '../../../../store/selectors'; +import { + setBootstrapEnabled, + setIndexName, + setAliasName, + setSelectedPolicyName, + unsetSelectedPolicy +} from '../../../../store/actions'; + +export const PolicyConfiguration = connect( + state => ({ + isSelectedPolicySet: getIsSelectedPolicySet(state), + selectedPolicyName: getSelectedPolicyName(state), + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + affectedIndexTemplates: getAffectedIndexTemplates(state), + bootstrapEnabled: getBootstrapEnabled(state), + indexName: getIndexName(state), + aliasName: getAliasName(state), + originalPolicyName: getSelectedOriginalPolicyName(state), + hasExistingPolicies: getPolicies(state).length > 0 + }), + { + setBootstrapEnabled, + setIndexName, + setAliasName, + setSelectedPolicyName, + unsetSelectedPolicy + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js new file mode 100644 index 0000000000000..df406b14c76d0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js @@ -0,0 +1,177 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; + +import { + EuiTitle, + EuiText, + EuiSpacer, + EuiHorizontalRule, + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; +import { HotPhase } from './components/hot_phase'; +import { WarmPhase } from './components/warm_phase'; +import { DeletePhase } from './components/delete_phase'; +import { ColdPhase } from './components/cold_phase'; +import { + PHASE_HOT, + PHASE_COLD, + PHASE_DELETE, + PHASE_WARM, +} from '../../../../store/constants'; +import { hasErrors } from '../../../../lib/find_errors'; +import { NodeAttrsDetails } from '../node_attrs_details'; +import { PolicySelection } from '../policy_selection/policy_selection.container'; + +export class PolicyConfiguration extends Component { + static propTypes = { + setSelectedPolicyName: PropTypes.func.isRequired, + setIndexName: PropTypes.func.isRequired, + setAliasName: PropTypes.func.isRequired, + setBootstrapEnabled: PropTypes.func.isRequired, + done: PropTypes.func.isRequired, + back: PropTypes.func.isRequired, + validate: PropTypes.func.isRequired, + selectedPolicyName: PropTypes.string.isRequired, + errors: PropTypes.object.isRequired, + bootstrapEnabled: PropTypes.bool.isRequired, + indexName: PropTypes.string.isRequired, + aliasName: PropTypes.string.isRequired, + originalPolicyName: PropTypes.string, + }; + + constructor(props) { + super(props); + this.state = { + isShowingErrors: false, + isShowingNodeDetailsFlyout: false, + selectedNodeAttrsForDetails: undefined, + }; + } + + validate = async () => { + await this.props.validate(); + const noErrors = !hasErrors(this.props.errors); + return noErrors; + }; + componentDidMount() { + window.scrollTo(0, 0); + } + submit = async () => { + this.setState({ isShowingErrors: true }); + if (await this.validate()) { + this.props.done(); + } else { + toastNotifications.addDanger('Please the fix errors on the page'); + } + }; + + showNodeDetailsFlyout = selectedNodeAttrsForDetails => { + this.setState({ isShowingNodeDetailsFlyout: true, selectedNodeAttrsForDetails }); + } + + render() { + const { + back, + + selectedPolicyName, + isSelectedPolicySet, + errors, + unsetSelectedPolicy, + hasExistingPolicies + } = this.props; + + const { isShowingErrors } = this.state; + + if (!isSelectedPolicySet) { + return ( + + ); + } + + return ( +
+ + + + + +

+ {!selectedPolicyName ? 'Create a lifecycle policy' : `Edit lifecycle policy ${selectedPolicyName}`} +

+
+
+ {hasExistingPolicies ? ( + + Change lifecycle policy + + ) : null} +
+ + +

Configure the phases of your data and when to transition between them.

+
+ + + + + + + + + + + + Back + +    + + Continue + + + {this.state.isShowingNodeDetailsFlyout ? ( + this.setState({ isShowingNodeDetailsFlyout: false })} + /> + ) : null} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js new file mode 100644 index 0000000000000..447bc384c368d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js @@ -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 { PolicySelection } from './policy_selection.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js new file mode 100644 index 0000000000000..59ce80fc1f0a4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js @@ -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 { connect } from 'react-redux'; +import { PolicySelection as PresentationComponent } from './policy_selection'; +import { + getPolicies, getIsSelectedPolicySet, getSelectedPolicyName, +} from '../../../../store/selectors'; +import { + fetchPolicies, + setSelectedPolicy, +} from '../../../../store/actions'; + +export const PolicySelection = connect( + state => ({ + isSelectedPolicySet: getIsSelectedPolicySet(state), + policies: getPolicies(state), + selectedPolicyName: getSelectedPolicyName(state), + }), + { + fetchPolicies, + setSelectedPolicy, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js new file mode 100644 index 0000000000000..e153d9fd1ce03 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js @@ -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 React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiDescribedFormGroup, + EuiFormRow, + EuiSelect, +} from '@elastic/eui'; + +export class PolicySelection extends Component { + static propTypes = { + fetchPolicies: PropTypes.func.isRequired, + setSelectedPolicy: PropTypes.func.isRequired, + + selectedPolicyName: PropTypes.string.isRequired, + policies: PropTypes.array.isRequired, + }; + + componentDidMount() { + this.props.fetchPolicies(); + } + selectPolicy(policyName) { + const policy = this.props.policies.find(policy => policy.name === policyName); + this.props.setSelectedPolicy(policy); + } + + render() { + const { isSelectedPolicySet, policies, selectedPolicyName } = this.props; + const policiesExist = policies.length > 0; + if (isSelectedPolicySet) { + return null; + } + let existingPoliciesSelect; + if (policiesExist) { + const options = policies.map(item => ({ value: item.name, text: item.name })); + options.unshift({ value: '', text: 'Select an existing policy' }); + existingPoliciesSelect = ( + + + { + await this.selectPolicy(e.target.value); + }} + /> + + + ); + } + + return ( + Select or create a lifecycle policy} + titleSize="s" + description={`An index lifecycle policy is a + blueprint for transitioning your data over time. + You can create a new policy${policiesExist ? ' or edit an existing policy and save it with a new name.' : '.'}`} + fullWidth + > + + + {existingPoliciesSelect} + +

- OR -

+
+ + this.selectPolicy()}>Create lifecycle policy + +
+
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js new file mode 100644 index 0000000000000..a89f7e2412c59 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js @@ -0,0 +1,107 @@ +/* + * 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, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiCodeEditor, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiToolTip, + EuiCode, +} from '@elastic/eui'; +import ace from 'brace'; +import 'brace/mode/json'; +import { + mergeAndPreserveDuplicateKeys, + removePrefixes, +} from '../../../../lib/diff_tools'; +import { addDiffAddonsForAce, setCurrentJsonObject } from '../../../../lib/diff_ace_addons'; + +export class DiffView extends PureComponent { + static propTypes = { + templateDiff: PropTypes.shape({ + originalFullIndexTemplate: PropTypes.object.isRequired, + newFullIndexTemplate: PropTypes.object.isRequired, + }).isRequired, + }; + + scrollToKey = (key, value) => { + const editorDom = this.aceEditor.aceEditor.refEditor; + const editor = ace.edit(editorDom); + const escapedValue = value.replace(/\^/g, '\\^'); + const range = editor.find(new RegExp(`"${key}"\\s*:\\s*"*(${escapedValue})"*`), { regex: true }); + if (!range) { + return; + } + editor.gotoLine(range.start.row + 1, range.start.column); + } + + render() { + const { + templateDiff: { originalFullIndexTemplate, newFullIndexTemplate }, + } = this.props; + + const { result: mergedJson, changes } = mergeAndPreserveDuplicateKeys( + originalFullIndexTemplate, + newFullIndexTemplate + ); + + // Strip the ^ and $ characters + const mergedJsonAsString = removePrefixes( + JSON.stringify(mergedJson, null, 2) + ); + + setCurrentJsonObject(mergedJson); + addDiffAddonsForAce(); + + return ( + + +
    + {changes.map(({ key, original, updated }) => ( +
  • + + Changing the value of {key} from {JSON.stringify(original)} + to {JSON.stringify(updated)} + + ) : ( + Setting a value of {JSON.stringify(updated)} for {key} + )} + > + this.scrollToKey(key, updated)}> + {key} + + +
  • + ))} +
+
+ + (this.aceEditor = aceEditor)} + mode="diff_json" + theme="github" + width="100%" + value={mergedJsonAsString} + setOptions={{ + useWorker: false, + readOnly: true, + }} + editorProps={{ + $blockScrolling: Infinity, + }} + /> + +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js new file mode 100644 index 0000000000000..defebab0131ea --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js @@ -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 { Review } from './review.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js new file mode 100644 index 0000000000000..044648dbbcabd --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js @@ -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 { connect } from 'react-redux'; +import { Review as PresentationComponent } from './review'; +import { + getSelectedIndexTemplateName, + getAffectedIndexTemplates, + getTemplateDiff, + getLifecycle, + getSelectedPolicyName, + getSaveAsNewPolicy, + getSelectedOriginalPolicyName, + getAliasName, + getBootstrapEnabled, + getIndexName, + getPolicies, + getIsNewPolicy +} from '../../../../store/selectors'; +import { + setSelectedPolicyName, + setSaveAsNewPolicy, + setAliasName, + setIndexName, + setBootstrapEnabled, +} from '../../../../store/actions'; + +export const Review = connect( + state => ({ + selectedIndexTemplateName: getSelectedIndexTemplateName(state), + affectedIndexTemplates: getAffectedIndexTemplates(state), + policies: getPolicies(state), + templateDiff: getTemplateDiff(state), + lifecycle: getLifecycle(state), + bootstrapEnabled: getBootstrapEnabled(state), + aliasName: getAliasName(state), + selectedPolicyName: getSelectedPolicyName(state), + saveAsNewPolicy: getSaveAsNewPolicy(state), + originalPolicyName: getSelectedOriginalPolicyName(state), + isNewPolicy: getIsNewPolicy(state), + + /* start might go away */ + indexName: getIndexName(state), + /* end might go away */ + }), + { + setSelectedPolicyName, + setSaveAsNewPolicy, + setBootstrapEnabled, + setIndexName, + setAliasName, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js new file mode 100644 index 0000000000000..20302c08de61f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js @@ -0,0 +1,286 @@ +/* + * 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, Component } from 'react'; +import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; + +import './review.less'; + +import 'brace/theme/github'; +import 'brace/mode/json'; +import 'brace/snippets/json'; +import 'brace/ext/language_tools'; + +import { + EuiTitle, + EuiCode, + EuiCallOut, + EuiSpacer, + EuiHorizontalRule, + EuiButton, + EuiFlexItem, + EuiText, + EuiButtonEmpty, + EuiFormRow, + EuiSwitch, + EuiFieldText, + EuiLoadingSpinner, + EuiFlexGroup, +} from '@elastic/eui'; +import { getAffectedIndices } from '../../../../api'; +import { DiffView } from './diff_view'; +import { ErrableFormRow } from '../../form_errors'; +import { STRUCTURE_POLICY_NAME } from '../../../../store/constants'; +import { hasErrors } from '../../../../lib/find_errors'; + +export class Review extends Component { + static propTypes = { + setSelectedPolicyName: PropTypes.func.isRequired, + setSaveAsNewPolicy: PropTypes.func.isRequired, + done: PropTypes.func.isRequired, + back: PropTypes.func.isRequired, + + selectedIndexTemplateName: PropTypes.string.isRequired, + affectedIndexTemplates: PropTypes.array.isRequired, + templateDiff: PropTypes.object.isRequired, + lifecycle: PropTypes.object.isRequired, + selectedPolicyName: PropTypes.string.isRequired, + saveAsNewPolicy: PropTypes.bool.isRequired, + originalPolicyName: PropTypes.string, + bootstrapEnabled: PropTypes.bool.isRequired, + aliasName: PropTypes.string.isRequired, + }; + + constructor(props) { + super(props); + this.state = { + selectedTab: 1, + affectedIndices: [], + isLoadingAffectedIndices: false, + }; + + this.currentFetchTimeout = null; + } + componentDidMount() { + window.scrollTo(0, 0); + } + fetchAffectedIndices = () => { + if (this.currentFetchTimeout) { + clearTimeout(this.currentFetchTimeout); + } + + this.setState({ isLoadingAffectedIndices: true }); + this.currentFetchTimeout = setTimeout(async () => { + const affectedIndices = await getAffectedIndices( + this.props.selectedIndexTemplateName, + this.props.selectedPolicyName + ); + this.setState({ affectedIndices, isLoadingAffectedIndices: false }); + }, 1000); + } + + async componentWillMount() { + this.fetchAffectedIndices(); + } + + async componentWillReceiveProps(nextProps) { + if (nextProps.selectedPolicyName !== this.props.selectedPolicyName) { + this.fetchAffectedIndices(); + } + } + + validate = async () => { + await this.props.validate(); + const noErrors = !hasErrors(this.props.errors); + return noErrors; + }; + + submit = async () => { + this.setState({ isShowingErrors: true }); + if (await this.validate()) { + this.props.done(); + } else { + toastNotifications.addDanger('Please fix the errors on the page'); + } + }; + + render() { + const { + done, + back, + setSelectedPolicyName, + setSaveAsNewPolicy, + validate, + + errors, + selectedPolicyName, + saveAsNewPolicy, + originalPolicyName, + selectedIndexTemplateName, + affectedIndexTemplates, + templateDiff, + lifecycle, + bootstrapEnabled, + aliasName, + policies, + isNewPolicy + } = this.props; + + const { affectedIndices, isLoadingAffectedIndices, isShowingErrors } = this.state; + + const showSaveChangedMessage = (originalPolicyName && !saveAsNewPolicy) + || (saveAsNewPolicy && !!policies.find(policy => policy.name === selectedPolicyName)); + + return ( +
+ + +

Review your policy changes

+
+ +

When you save a policy, your changes go into effect immediately.

+
+ + + + +

{`${affectedIndexTemplates.length} Index ${affectedIndexTemplates.length === 1 ? 'template' : 'templates'}`}

+
    + {affectedIndexTemplates.map(template => ( +
  • {template}
  • + ))} +
+

{`${affectedIndices.length} ${affectedIndices.length === 1 ? 'Index' : 'Indices' }`}

+ { isLoadingAffectedIndices ? ( + + ) : ( +
    + {affectedIndices.map(index =>
  • {index}
  • )} +
+ ) } +
+ + {bootstrapEnabled ? ( + + + +

You decided to bootstrap a new index. Point to this new alias going forward.

+

{aliasName} is your new alias

+
+
+ ) : null} + + + {templateDiff.hasChanged ? ( + + +

+ {selectedIndexTemplateName} index template changes +

+
+ + + +
+ ) : null} + + + + {originalPolicyName ? ( + + { showSaveChangedMessage ? ( + + +

Save changes to {selectedPolicyName} policy?

+
+ +

+ You are editing an existing policy. Any changes you make + will also change index templates that this policy is attached to. Alternately, you can save + these changes in a new policy and only change the index template you + selected. +

+
+ +
+ ) : null } + { isNewPolicy ? null : ( + + { + await setSaveAsNewPolicy(e.target.checked); + validate(); + }} + label={ + + Save this as a new policy + + } + /> + + )} + +
+ ) : null} + {saveAsNewPolicy || isNewPolicy ? ( + + +

Save your work

+
+ + + { + await setSelectedPolicyName(e.target.value); + validate(); + }} + /> + +
+ ) : null} +
+ + + + + Back + + + + + done(lifecycle)} + disabled={!selectedPolicyName} + > + Looks good, save changes + + + +    +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less new file mode 100644 index 0000000000000..e826e78a7bf7e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less @@ -0,0 +1,7 @@ +.ace_diff_addition { + background-color: #e6ffed; +} + +.ace_diff_removal { + background-color: #ffeef0; +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js new file mode 100644 index 0000000000000..fc3c29c4beb0c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js @@ -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, { cloneElement, Children, Fragment } from 'react'; +import { EuiFormRow } from '@elastic/eui'; + +export const ErrableFormRow = ({ + errorKey, + isShowingErrors, + errors, + children, + ...rest +}) => { + return ( + 0 + } + error={errors[errorKey]} + {...rest} + > + + {Children.map(children, child => cloneElement(child, { + isInvalid: isShowingErrors && errors[errorKey].length > 0, + }))} + + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js new file mode 100644 index 0000000000000..3a6f61a7c0492 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js @@ -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 { Wizard } from './wizard.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js new file mode 100644 index 0000000000000..84c94f2c82387 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js @@ -0,0 +1,37 @@ +/* + * 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 { connect } from 'react-redux'; +import '../../less/main.less'; +import { Wizard as PresentationComponent } from './wizard'; +import { saveLifecycle, fetchIndexTemplates } from '../../store/actions'; + +import { + getIndexTemplates, + getIndexTemplatePatch, + getBootstrapEnabled, + getIndexName, + getAliasName, + validateLifecycle, +} from '../../store/selectors'; + +export const Wizard = connect( + state => ({ + indexTemplatePatch: getIndexTemplatePatch(state), + bootstrapEnabled: getBootstrapEnabled(state), + indexName: getIndexName(state), + aliasName: getAliasName(state), + validateLifecycle: () => validateLifecycle(state), + indexTemplates: getIndexTemplates(state), + }), + { + fetchIndexTemplates, + saveLifecycle + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js new file mode 100644 index 0000000000000..a0dff6e332e30 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js @@ -0,0 +1,170 @@ +/* + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; +import { IndexTemplate } from './components/index_template'; +import { PolicyConfiguration } from './components/policy_configuration'; +import { Review } from './components/review'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiTitle, + EuiSpacer, + EuiStepsHorizontal, +} from '@elastic/eui'; +import { bootstrap } from '../../api'; +import { + STRUCTURE_INDEX_TEMPLATE, + STRUCTURE_POLICY_CONFIGURATION, + STRUCTURE_REVIEW, +} from '../../store/constants'; + +const STEP_INDEX_TEMPLATE = 1; +const STEP_POLICY_CONFIGURATION = 2; +const STEP_REVIEW = 3; +export class Wizard extends Component { + static propTypes = { + saveLifecycle: PropTypes.func.isRequired, + validateLifecycle: PropTypes.func.isRequired, + + indexTemplatePatch: PropTypes.object.isRequired, + bootstrapEnabled: PropTypes.bool.isRequired, + indexName: PropTypes.string.isRequired, + aliasName: PropTypes.string.isRequired, + fetchIndexTemplates: PropTypes.func.isRequired, + indexTemplates: PropTypes.array, + }; + + constructor(props) { + super(props); + + this.state = { + selectedStep: STEP_INDEX_TEMPLATE, + errors: this.getErrors(), + }; + } + componentDidMount() { + this.props.fetchIndexTemplates(); + } + onSelectedStepChanged = selectedStep => { + this.setState({ + selectedStep, + }); + }; + + getErrors = () => { + return this.props.validateLifecycle(); + }; + + validate = () => { + const errors = this.getErrors(); + this.setState({ errors }); + }; + + addLifecycle = async lifecycle => { + await this.props.saveLifecycle(lifecycle, this.props.indexTemplatePatch); + await this.bootstrap(); + // this.onSelectedStepChanged(5); + }; + + bootstrap = async () => { + const { indexName, aliasName, bootstrapEnabled } = this.props; + if (!bootstrapEnabled) { + return; + } + + const bootstrapSuccess = await bootstrap(indexName, aliasName); + if (bootstrapSuccess) { + toastNotifications.addSuccess( + 'Successfully bootstrapped an index and alias' + ); + // Bounce back to management + // this.onSelectedStepChanged(1); + // TODO: also clear state? + } else { + toastNotifications.addDanger('Unable to bootstrap an index and alias'); + } + }; + + renderContent() { + const { selectedStep, errors } = this.state; + + switch (selectedStep) { + case STEP_INDEX_TEMPLATE: + return ( + this.onSelectedStepChanged(selectedStep + 1)} + /> + ); + case STEP_POLICY_CONFIGURATION: + return ( + this.onSelectedStepChanged(selectedStep + 1)} + back={() => this.onSelectedStepChanged(selectedStep - 1)} + /> + ); + case STEP_REVIEW: + return ( + this.onSelectedStepChanged(selectedStep - 1)} + /> + ); + } + } + createStep(title, stepIndex) { + return { + title, + isSelected: this.state.selectedStep === stepIndex, + isComplete: this.state.selectedStep > stepIndex, + onClick: () => this.onSelectedStepChanged(stepIndex), + }; + } + render() { + const { indexTemplates } = this.props; + if (indexTemplates === null) { + // Loading... + return null; + } + + if (indexTemplates.length === 0) { + return ( +

No index templates found.

+ ); + } + const steps = [ + this.createStep('Select an index template', STEP_INDEX_TEMPLATE), + this.createStep('Configure a lifecycle policy', STEP_POLICY_CONFIGURATION), + this.createStep('Review and save', STEP_REVIEW), + ]; + + return ( + + + + +

Index lifecycle management

+
+ + + + + {this.renderContent()} +
+
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/general.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/general.js new file mode 100644 index 0000000000000..584488d4c2b42 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/general.js @@ -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 { createAction } from 'redux-actions'; + +export const setBootstrapEnabled = createAction('SET_BOOTSTRAP_ENABLED'); +export const setIndexName = createAction('SET_INDEX_NAME'); +export const setAliasName = createAction('SET_ALIAS_NAME'); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/index.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/index.js new file mode 100644 index 0000000000000..621cbf007d3b2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/index.js @@ -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. + */ + + + + +export * from './index_template'; +export * from './nodes'; +export * from './policies'; +export * from './lifecycle'; +export * from './general'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js new file mode 100644 index 0000000000000..c7bcd3518d036 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/index_template.js @@ -0,0 +1,107 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { loadIndexTemplates, loadIndexTemplate } from '../../api'; +import { getAlias } from '../selectors'; +import { + setPhaseData, + setIndexName, + setAliasName, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + setSelectedNodeAttrs, + setSelectedPolicyName, +} from '.'; +import { + PHASE_HOT, + PHASE_ROLLOVER_ALIAS, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE +} from '../constants'; + +export const fetchingIndexTemplates = createAction('FETCHING_INDEX_TEMPLATES'); +export const fetchedIndexTemplates = createAction('FETCHED_INDEX_TEMPLATES'); +export const fetchIndexTemplates = () => async dispatch => { + dispatch(fetchingIndexTemplates()); + + let templates; + try { + templates = await loadIndexTemplates(); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedIndexTemplates(templates)); +}; + +export const fetchedIndexTemplate = createAction('FETCHED_INDEX_TEMPLATE'); +export const fetchIndexTemplate = templateName => async (dispatch) => { + let template; + try { + template = await loadIndexTemplate(templateName); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + if (template.settings && template.settings.index) { + dispatch( + setSelectedPrimaryShardCount(template.settings.index.number_of_shards) + ); + dispatch( + setSelectedReplicaCount(template.settings.index.number_of_replicas) + ); + if ( + template.settings.index.routing && + template.settings.index.routing.allocation && + template.settings.index.routing.allocation.include + ) { + dispatch( + setSelectedNodeAttrs( + template.settings.index.routing.allocation.include.sattr_name + ) + ); + } + if (template.settings.index.lifecycle) { + dispatch(setSelectedPolicyName(template.settings.index.lifecycle.name)); + } + } + + let indexPattern = template.index_patterns[0]; + if (indexPattern.endsWith('*')) { + indexPattern = indexPattern.slice(0, -1); + } + dispatch(setIndexName(`${indexPattern}-00001`)); + dispatch(setAliasName(`${indexPattern}-alias`)); + dispatch(fetchedIndexTemplate(template)); +}; + +export const setSelectedIndexTemplateName = createAction( + 'SET_SELECTED_INDEX_TEMPLATE_NAME' +); + +export const setSelectedIndexTemplate = name => async (dispatch, getState) => { + // Await all of these to ensure they happen before the next round of validation + const promises = [ + dispatch(setSelectedIndexTemplateName(name)), + dispatch(fetchIndexTemplate(name)) + ]; + const alias = getAlias(getState()); + if (alias) { + promises.push(...[ + dispatch(setPhaseData(PHASE_HOT, PHASE_ROLLOVER_ALIAS, alias)), + dispatch(setPhaseData(PHASE_WARM, PHASE_ROLLOVER_ALIAS, alias)), + dispatch(setPhaseData(PHASE_COLD, PHASE_ROLLOVER_ALIAS, alias)), + dispatch(setPhaseData(PHASE_DELETE, PHASE_ROLLOVER_ALIAS, alias)) + ]); + } + await Promise.all(promises); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js new file mode 100644 index 0000000000000..676894d079093 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js @@ -0,0 +1,25 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { saveLifecycle as saveLifecycleApi } from '../../api'; + + +export const savedLifecycle = createAction('SAVED_LIFECYCLE'); +export const saveLifecycle = (lifecycle, indexTemplatePatch) => async dispatch => { + let saved; + try { + saved = await saveLifecycleApi(lifecycle, indexTemplatePatch); + } + catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + toastNotifications.addSuccess(`Successfully created lifecycle policy '${lifecycle.name}'`); + + dispatch(savedLifecycle(saved)); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js new file mode 100644 index 0000000000000..b806d1df3d380 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/nodes.js @@ -0,0 +1,47 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { loadNodes, loadNodeDetails } from '../../api'; +import { SET_SELECTED_NODE_ATTRS } from '../constants'; + +export const setSelectedNodeAttrs = createAction(SET_SELECTED_NODE_ATTRS); +export const setSelectedPrimaryShardCount = createAction( + 'SET_SELECTED_PRIMARY_SHARED_COUNT' +); +export const setSelectedReplicaCount = createAction( + 'SET_SELECTED_REPLICA_COUNT' +); +export const fetchedNodes = createAction('FETCHED_NODES'); +export const fetchNodes = () => async dispatch => { + let nodes; + try { + nodes = await loadNodes(); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedNodes(nodes)); +}; + +export const fetchedNodeDetails = createAction( + 'FETCHED_NODE_DETAILS', + (selectedNodeAttrs, details) => ({ + selectedNodeAttrs, + details, + }) +); +export const fetchNodeDetails = selectedNodeAttrs => async dispatch => { + let details; + try { + details = await loadNodeDetails(selectedNodeAttrs); + } catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedNodeDetails(selectedNodeAttrs, details)); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js new file mode 100644 index 0000000000000..74f79799108c0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -0,0 +1,37 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { toastNotifications } from 'ui/notify'; +import { loadPolicies } from '../../api'; +import { SET_PHASE_DATA } from '../constants'; +export const fetchedPolicies = createAction('FETCHED_POLICIES'); +export const setSelectedPolicy = createAction('SET_SELECTED_POLICY'); +export const unsetSelectedPolicy = createAction('UNSET_SELECTED_POLICY'); +export const setSelectedPolicyName = createAction('SET_SELECTED_POLICY_NAME'); +export const setSaveAsNewPolicy = createAction('SET_SAVE_AS_NEW_POLICY'); + +export const fetchPolicies = () => async dispatch => { + let policies; + try { + policies = await loadPolicies(); + } + catch (err) { + return toastNotifications.addDanger(err.data.message); + } + + dispatch(fetchedPolicies(policies)); + if (policies.length === 0) { + dispatch(setSelectedPolicy()); + } + return policies; +}; + + +export const setPhaseData = createAction(SET_PHASE_DATA, (phase, key, value) => ({ phase, key, value })); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js new file mode 100644 index 0000000000000..b8e918ef5e4b2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const SET_PHASE_DATA = 'SET_PHASE_DATA'; +export const SET_SELECTED_NODE_ATTRS = 'SET_SELECTED_NODE_ATTRS'; +export const PHASE_HOT = 'hot'; +export const PHASE_WARM = 'warm'; +export const PHASE_COLD = 'cold'; +export const PHASE_DELETE = 'delete'; + +export const PHASE_ENABLED = 'phaseEnabled'; + +export const MAX_SIZE_TYPE_DOCUMENT = 'd'; + +export const PHASE_ROLLOVER_ENABLED = 'rolloverEnabled'; +export const WARM_PHASE_ON_ROLLOVER = 'warmPhaseOnRollover'; +export const PHASE_ROLLOVER_ALIAS = 'selectedAlias'; +export const PHASE_ROLLOVER_MAX_AGE = 'selectedMaxAge'; +export const PHASE_ROLLOVER_MAX_AGE_UNITS = 'selectedMaxAgeUnits'; +export const PHASE_ROLLOVER_MAX_SIZE_STORED = 'selectedMaxSizeStored'; +export const PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS = 'selectedMaxSizeStoredUnits'; +export const PHASE_ROLLOVER_MAX_DOC_SIZE = 'selectedMaxDocSize'; +export const PHASE_ROLLOVER_AFTER = 'selectedAfter'; +export const PHASE_ROLLOVER_AFTER_UNITS = 'selectedAfterUnits'; + +export const PHASE_FORCE_MERGE_SEGMENTS = 'selectedForceMergeSegments'; +export const PHASE_FORCE_MERGE_ENABLED = 'forceMergeEnabled'; + +export const PHASE_SHRINK_ENABLED = 'shrinkEnabled'; + +export const PHASE_NODE_ATTRS = 'selectedNodeAttrs'; +export const PHASE_PRIMARY_SHARD_COUNT = 'selectedPrimaryShardCount'; +export const PHASE_REPLICA_COUNT = 'selectedReplicaCount'; + +export const PHASE_ATTRIBUTES_THAT_ARE_NUMBERS = [ + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_DOC_SIZE, + PHASE_ROLLOVER_AFTER, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, +]; + +export const STRUCTURE_INDEX_TEMPLATE = 'indexTemplate'; +export const STRUCTURE_TEMPLATE_SELECTION = 'templateSelection'; +export const STRUCTURE_TEMPLATE_NAME = 'templateName'; +export const STRUCTURE_CONFIGURATION = 'configuration'; +export const STRUCTURE_NODE_ATTRS = 'node_attrs'; +export const STRUCTURE_PRIMARY_NODES = 'primary_nodes'; +export const STRUCTURE_REPLICAS = 'replicas'; + +export const STRUCTURE_POLICY_CONFIGURATION = 'policyConfiguration'; + +export const STRUCTURE_REVIEW = 'review'; +export const STRUCTURE_POLICY_NAME = 'policyName'; +export const STRUCTURE_INDEX_NAME = 'indexName'; +export const STRUCTURE_ALIAS_NAME = 'aliasName'; + +export const ERROR_STRUCTURE = { + [STRUCTURE_INDEX_TEMPLATE]: { + [STRUCTURE_TEMPLATE_SELECTION]: { + [STRUCTURE_TEMPLATE_NAME]: [], + [STRUCTURE_INDEX_NAME]: [], + [STRUCTURE_ALIAS_NAME]: [] + }, + [STRUCTURE_CONFIGURATION]: { + [STRUCTURE_NODE_ATTRS]: [], + [STRUCTURE_PRIMARY_NODES]: [], + [STRUCTURE_REPLICAS]: [] + } + }, + [STRUCTURE_POLICY_CONFIGURATION]: { + [PHASE_HOT]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_MAX_AGE]: [], + [PHASE_ROLLOVER_MAX_AGE_UNITS]: [], + [PHASE_ROLLOVER_MAX_SIZE_STORED]: [], + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: [], + [PHASE_ROLLOVER_MAX_DOC_SIZE]: [] + }, + [PHASE_WARM]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_AFTER]: [], + [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_NODE_ATTRS]: [], + [PHASE_PRIMARY_SHARD_COUNT]: [], + [PHASE_REPLICA_COUNT]: [], + [PHASE_FORCE_MERGE_SEGMENTS]: [], + }, + [PHASE_COLD]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_AFTER]: [], + [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_NODE_ATTRS]: [], + [PHASE_REPLICA_COUNT]: [], + }, + [PHASE_DELETE]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_AFTER]: [], + [PHASE_ROLLOVER_AFTER_UNITS]: [], + }, + }, + [STRUCTURE_REVIEW]: { + [STRUCTURE_POLICY_NAME]: [], + } +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js new file mode 100644 index 0000000000000..66750a824cca8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js @@ -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 { + PHASE_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_NODE_ATTRS, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ALIAS, +} from '../constants'; + +export const defaultColdPhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 'd', + [PHASE_NODE_ATTRS]: '', + [PHASE_REPLICA_COUNT]: '' +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js new file mode 100644 index 0000000000000..236e20c0c36d4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ALIAS, +} from '../constants'; + +export const defaultDeletePhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 'd' +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js new file mode 100644 index 0000000000000..d0e45a963505b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/hot_phase.js @@ -0,0 +1,26 @@ +/* + * 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 { + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MAX_DOC_SIZE, +} from '../constants'; + +export const defaultHotPhase = { + [PHASE_ENABLED]: true, + [PHASE_ROLLOVER_ENABLED]: true, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_ROLLOVER_MAX_AGE]: '', + [PHASE_ROLLOVER_MAX_AGE_UNITS]: 'd', + [PHASE_ROLLOVER_MAX_SIZE_STORED]: '', + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: 'gb', + [PHASE_ROLLOVER_MAX_DOC_SIZE]: '', +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js new file mode 100644 index 0000000000000..a92f98fa8e022 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/index.js @@ -0,0 +1,10 @@ +/* + * 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 { defaultDeletePhase } from './delete_phase'; +export { defaultColdPhase } from './cold_phase'; +export { defaultHotPhase } from './hot_phase'; +export { defaultWarmPhase } from './warm_phase'; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js new file mode 100644 index 0000000000000..84ffa9c742043 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js @@ -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 { + PHASE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_ROLLOVER_AFTER, + PHASE_NODE_ATTRS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ALIAS, + PHASE_SHRINK_ENABLED, + WARM_PHASE_ON_ROLLOVER +} from '../constants'; + +export const defaultWarmPhase = { + [PHASE_ENABLED]: false, + [PHASE_ROLLOVER_ALIAS]: '', + [PHASE_FORCE_MERGE_SEGMENTS]: '', + [PHASE_FORCE_MERGE_ENABLED]: false, + [PHASE_ROLLOVER_AFTER]: '', + [PHASE_ROLLOVER_AFTER_UNITS]: 'd', + [PHASE_NODE_ATTRS]: '', + [PHASE_SHRINK_ENABLED]: true, + [PHASE_PRIMARY_SHARD_COUNT]: '', + [PHASE_REPLICA_COUNT]: '', + [WARM_PHASE_ON_ROLLOVER]: false +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/index.js b/x-pack/plugins/index_lifecycle_management/public/store/index.js new file mode 100644 index 0000000000000..808eb489bf913 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/index.js @@ -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 { indexLifecycleManagementStore } from './store'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js new file mode 100644 index 0000000000000..d7d390f608ccf --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setPhaseData } from '../actions'; +import { getPhaseData } from '../selectors'; +import { SET_PHASE_DATA, PHASE_ENABLED } from '../constants'; + +const setsPerPhase = {}; + +export const autoEnablePhase = store => next => action => { + const state = store.getState(); + + if (action.type === SET_PHASE_DATA) { + const { phase } = action.payload; + setsPerPhase[phase] = setsPerPhase[phase] || 0; + setsPerPhase[phase]++; + + if (setsPerPhase[phase] === 1 && !getPhaseData(state, phase, PHASE_ENABLED)) { + store.dispatch(setPhaseData(phase, PHASE_ENABLED, true)); + } + } + + return next(action); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js new file mode 100644 index 0000000000000..5d59ccd8c4fe7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js @@ -0,0 +1,37 @@ +/* + * 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 { setPhaseData } from '../actions'; +import { getPhaseData } from '../selectors'; +import { + SET_SELECTED_NODE_ATTRS, + SET_PHASE_DATA, + PHASE_WARM, + PHASE_NODE_ATTRS, + PHASE_COLD, +} from '../constants'; + +export const autoSetNodeAttrs = store => next => action => { + const state = store.getState(); + + if (action.type === SET_SELECTED_NODE_ATTRS) { + const warmPhaseAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); + if (!warmPhaseAttrs) { + store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, action.payload)); + } + } else if (action.type === SET_PHASE_DATA) { + const { phase, key, value } = action.payload; + + if (phase === PHASE_WARM && key === PHASE_NODE_ATTRS) { + const coldPhaseAttrs = getPhaseData(state, PHASE_COLD, PHASE_NODE_ATTRS); + if (!coldPhaseAttrs) { + store.dispatch(setPhaseData(PHASE_COLD, PHASE_NODE_ATTRS, value)); + } + } + } + + return next(action); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js new file mode 100644 index 0000000000000..fcb0a960db4e2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js @@ -0,0 +1,9 @@ +/* + * 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 { autoEnablePhase } from './auto_enable_phase'; +export { setSelectedPolicyFromSelectedTemplate } from './set_selected_policy_from_selected_template'; +export { autoSetNodeAttrs } from './auto_set_node_attrs'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js new file mode 100644 index 0000000000000..a87f9c3496d64 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js @@ -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 { + fetchedIndexTemplate, + fetchPolicies, + setSelectedPolicy, + setPhaseData +} from '../actions'; +import { getSelectedNodeAttrs, getPhaseData } from '../selectors'; +import { PHASE_WARM, PHASE_NODE_ATTRS, PHASE_COLD } from '../constants'; + +export const setSelectedPolicyFromSelectedTemplate = store => next => async action => { + if (action.type === fetchedIndexTemplate().type) { + const template = action.payload; + if (template.settings.index && template.settings.index.lifecycle) { + const policies = await fetchPolicies()(store.dispatch); + const selectedPolicy = policies.find(policy => policy.name === template.settings.index.lifecycle.name); + if (selectedPolicy) { + store.dispatch(setSelectedPolicy(selectedPolicy)); + + // We also want to update node attrs for future phases if they do not exist + const state = store.getState(); + const hotNodeAttrs = getSelectedNodeAttrs(state); + const warmNodeAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); + const coldNodeAttrs = getPhaseData(state, PHASE_COLD, PHASE_NODE_ATTRS); + + if (hotNodeAttrs && !warmNodeAttrs) { + store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, hotNodeAttrs)); + } + if ((hotNodeAttrs || warmNodeAttrs) && !coldNodeAttrs) { + store.dispatch(setPhaseData(PHASE_COLD, PHASE_NODE_ATTRS, warmNodeAttrs || hotNodeAttrs)); + } + } + } + } + + return next(action); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js new file mode 100644 index 0000000000000..abb56f5cdae2f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/general.js @@ -0,0 +1,38 @@ +/* + * 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 { handleActions } from 'redux-actions'; +import { setIndexName, setAliasName, setBootstrapEnabled } from '../actions/general'; + +const defaultState = { + bootstrapEnabled: false, + indexName: '', + aliasName: '', +}; + +export const general = handleActions({ + [setIndexName](state, { payload: indexName }) { + return { + ...state, + indexName, + }; + }, + [setAliasName](state, { payload: aliasName }) { + return { + ...state, + aliasName, + }; + }, + [setBootstrapEnabled](state, { payload: bootstrapEnabled }) { + return { + ...state, + bootstrapEnabled, + }; + } +}, defaultState); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js new file mode 100644 index 0000000000000..7225d9e0be9f5 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index.js @@ -0,0 +1,21 @@ +/* + * 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 { combineReducers } from 'redux'; +import { indexTemplate } from './index_template'; +import { nodes } from './nodes'; +import { policies } from './policies'; +import { general } from './general'; + +export const indexLifecycleManagement = combineReducers({ + indexTemplate, + nodes, + policies, + general, +}); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js new file mode 100644 index 0000000000000..19bc7af01954c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/index_template.js @@ -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 { handleActions } from 'redux-actions'; +import { + fetchingIndexTemplates, + fetchedIndexTemplates, + setSelectedIndexTemplateName, + fetchedIndexTemplate +} from '../actions/index_template'; + +const defaultState = { + isLoading: false, + fullSelectedIndexTemplate: null, + selectedIndexTemplateName: '', + indexTemplates: null, +}; + +export const indexTemplate = handleActions( + { + [fetchingIndexTemplates](state) { + return { + ...state, + isLoading: true + }; + }, + [fetchedIndexTemplates](state, { payload: indexTemplates }) { + return { + ...state, + isLoading: false, + indexTemplates + }; + }, + [fetchedIndexTemplate](state, { payload: fullSelectedIndexTemplate }) { + return { + ...state, + fullSelectedIndexTemplate, + }; + }, + [setSelectedIndexTemplateName](state, { payload: selectedIndexTemplateName }) { + return { + ...state, + selectedIndexTemplateName + }; + } + }, + defaultState +); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js new file mode 100644 index 0000000000000..5e8e01eab26df --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/nodes.js @@ -0,0 +1,80 @@ +/* + * 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 { handleActions } from 'redux-actions'; +import { + fetchedNodes, + setSelectedNodeAttrs, + setSelectedPrimaryShardCount, + setSelectedReplicaCount, + fetchedNodeDetails +} from '../actions/nodes'; + +const defaultState = { + isLoading: false, + selectedNodeAttrs: '', + selectedPrimaryShardCount: 1, + selectedReplicaCount: 1, + nodes: [], + details: {}, +}; + +export const nodes = handleActions( + { + [fetchedNodes](state, { payload: nodes }) { + return { + ...state, + isLoading: false, + nodes + }; + }, + [fetchedNodeDetails](state, { payload }) { + const { selectedNodeAttrs, details } = payload; + return { + ...state, + details: { + ...state.details, + [selectedNodeAttrs]: details, + } + }; + }, + [setSelectedNodeAttrs](state, { payload: selectedNodeAttrs }) { + return { + ...state, + selectedNodeAttrs + }; + }, + [setSelectedPrimaryShardCount](state, { payload }) { + let selectedPrimaryShardCount = parseInt(payload); + if (isNaN(selectedPrimaryShardCount)) { + selectedPrimaryShardCount = ''; + } + return { + ...state, + selectedPrimaryShardCount + }; + }, + [setSelectedReplicaCount](state, { payload }) { + let selectedReplicaCount; + if (payload != null) { + selectedReplicaCount = parseInt(payload); + if (isNaN(selectedReplicaCount)) { + selectedReplicaCount = ''; + } + } else { + // default value for Elasticsearch + selectedReplicaCount = 1; + } + + + return { + ...state, + selectedReplicaCount + }; + } + }, + defaultState +); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js new file mode 100644 index 0000000000000..5b2c288858385 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -0,0 +1,130 @@ +/* + * 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 { handleActions } from 'redux-actions'; +import { + fetchedPolicies, + setSelectedPolicy, + unsetSelectedPolicy, + setSelectedPolicyName, + setSaveAsNewPolicy, + setPhaseData +} from '../actions'; +import { policyFromES } from '../selectors'; +import { + PHASE_HOT, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE, + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, +} from '../constants'; + +import { + defaultColdPhase, + defaultDeletePhase, + defaultHotPhase, + defaultWarmPhase, +} from '../defaults'; +export const defaultPolicy = { + name: '', + saveAsNew: true, + isNew: true, + phases: { + [PHASE_HOT]: defaultHotPhase, + [PHASE_WARM]: defaultWarmPhase, + [PHASE_COLD]: defaultColdPhase, + [PHASE_DELETE]: defaultDeletePhase + } +}; + +const defaultState = { + isLoading: false, + originalPolicyName: undefined, + selectedPolicySet: false, + selectedPolicy: defaultPolicy, + policies: [] +}; + +export const policies = handleActions( + { + [fetchedPolicies](state, { payload: policies }) { + return { + ...state, + isLoading: false, + policies + }; + }, + [setSelectedPolicy](state, { payload: selectedPolicy }) { + if (!selectedPolicy) { + return { + ...state, + selectedPolicy: defaultPolicy, + selectedPolicySet: true, + }; + } + + return { + ...state, + originalPolicyName: selectedPolicy.name, + selectedPolicySet: true, + selectedPolicy: { + ...defaultPolicy, + ...policyFromES(selectedPolicy) + } + }; + }, + [unsetSelectedPolicy]() { + return defaultState; + }, + [setSelectedPolicyName](state, { payload: name }) { + return { + ...state, + selectedPolicy: { + ...state.selectedPolicy, + name + } + }; + }, + [setSaveAsNewPolicy](state, { payload: saveAsNew }) { + return { + ...state, + selectedPolicy: { + ...state.selectedPolicy, + saveAsNew + } + }; + }, + [setPhaseData](state, { payload }) { + const { phase, key } = payload; + + let value = payload.value; + if (PHASE_ATTRIBUTES_THAT_ARE_NUMBERS.includes(key)) { + value = parseInt(value); + if (isNaN(value)) { + value = ''; + } + } + + return { + ...state, + selectedPolicy: { + ...state.selectedPolicy, + phases: { + ...state.selectedPolicy.phases, + [phase]: { + ...state.selectedPolicy.phases[phase], + [key]: value + } + } + } + }; + } + }, + defaultState +); diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js new file mode 100644 index 0000000000000..41459d1bbb2c8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/general.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +export const getBootstrapEnabled = state => state.general.bootstrapEnabled; +export const getIndexName = state => state.general.indexName; +export const getAliasName = state => state.general.aliasName; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js new file mode 100644 index 0000000000000..621cbf007d3b2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index.js @@ -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. + */ + + + + +export * from './index_template'; +export * from './nodes'; +export * from './policies'; +export * from './lifecycle'; +export * from './general'; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js new file mode 100644 index 0000000000000..339d5380613ff --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -0,0 +1,177 @@ +/* + * 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 { createSelector } from 'reselect'; +import { get, merge, cloneDeep } from 'lodash'; +import { + getSaveAsNewPolicy, + getSelectedPolicyName, + getSelectedPrimaryShardCount, + getNodesFromSelectedNodeAttrs, + getSelectedReplicaCount, + getSelectedNodeAttrs +} from '.'; +import { getAliasName } from './general'; + +export const getIndexTemplates = state => { return state.indexTemplate.indexTemplates || []; }; +export const getIndexTemplateOptions = createSelector( + [state => getIndexTemplates(state)], + templates => { + if (!templates) { + return []; + } + + const options = templates.map(template => ({ + text: template.name, + value: template.name + })); + + options.sort((a, b) => a.text.localeCompare(b.text)); + options.unshift({ + text: '', + value: undefined + }); + + return options; + } +); +export const getSelectedIndexTemplateName = state => + state.indexTemplate.selectedIndexTemplateName; + +export const getSelectedIndexTemplate = createSelector( + [ + state => getSelectedIndexTemplateName(state), + state => getIndexTemplates(state) + ], + (selectedIndexTemplateName, allTemplates) => { + if (!allTemplates) { + return null; + } + return allTemplates.find( + template => template.name === selectedIndexTemplateName + ); + } +); + +export const getFullSelectedIndexTemplate = state => state.indexTemplate.fullSelectedIndexTemplate; + +export const getAlias = state => { + const indexTemplate = getSelectedIndexTemplate(state); + return get(indexTemplate, 'settings.indexlifecycle.rollover_alias'); +}; + +// TODO: add createSelector +export const getAffectedIndexTemplates = state => { + const selectedIndexTemplateName = getSelectedIndexTemplateName(state); + const indexTemplates = [selectedIndexTemplateName]; + + const selectedPolicyName = getSelectedPolicyName(state); + const allTemplates = getIndexTemplates(state); + indexTemplates.push( + ...allTemplates.reduce((accum, template) => { + if (template.index_lifecycle_name === selectedPolicyName && template.name !== selectedIndexTemplateName) { + accum.push(template.name); + } + return accum; + }, []) + ); + + return indexTemplates; +}; + +// TODO: add createSelector +export const getAffectedIndexPatterns = state => { + const indexPatterns = [...getSelectedIndexTemplate(state).index_patterns]; + + if (!getSaveAsNewPolicy(state)) { + const allTemplates = getIndexTemplates(state); + const selectedPolicyName = getSelectedPolicyName(state); + indexPatterns.push( + ...allTemplates.reduce((accum, template) => { + if (template.index_lifecycle_name === selectedPolicyName) { + accum.push(...template.index_patterns); + } + return accum; + }, []) + ); + } + + return indexPatterns; +}; + +export const getSelectedIndexTemplateIndices = state => { + const selectedIndexTemplate = getSelectedIndexTemplate(state); + if (selectedIndexTemplate) { + return selectedIndexTemplate.indices; + } + return undefined; +}; + +export const getExistingAllocationRules = state => { + const selectedIndexTemplate = getSelectedIndexTemplate(state); + if (selectedIndexTemplate) { + return selectedIndexTemplate.allocation_rules; + } + return undefined; +}; + +const hasJSONChanged = (json1, json2) => JSON.stringify(json1) !== JSON.stringify(json2); +export const getTemplateDiff = state => { + const originalFullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; + const attributeName = getSelectedNodeAttrs(state); + const baseNewFullIndexTemplate = { + settings: { + index: { + number_of_shards: getSelectedPrimaryShardCount(state) + '', + number_of_replicas: getSelectedReplicaCount(state) + '', + lifecycle: { + name: getSelectedPolicyName(state) + }, + } + } + }; + if (attributeName) { + baseNewFullIndexTemplate.routing = { + allocation: { + include: { + sattr_name: attributeName, + } + } + }; + } + const newFullIndexTemplate = merge(cloneDeep(originalFullIndexTemplate), baseNewFullIndexTemplate); + + return { + originalFullIndexTemplate, + newFullIndexTemplate, + hasChanged: hasJSONChanged(originalFullIndexTemplate, newFullIndexTemplate), + }; +}; + +export const getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount = state => { + const primaryShardCount = getSelectedPrimaryShardCount(state); + const selectedNodeAttrsCount = getNodesFromSelectedNodeAttrs(state); + + if (selectedNodeAttrsCount === null) { + return false; + } + + return primaryShardCount > selectedNodeAttrsCount; +}; + +export const getIndexTemplatePatch = state => { + return { + indexTemplate: getSelectedIndexTemplateName(state), + primaryShardCount: getSelectedPrimaryShardCount(state), + replicaCount: getSelectedReplicaCount(state), + lifecycleName: getSelectedPolicyName(state), + nodeAttrs: getSelectedNodeAttrs(state), + rolloverAlias: getAliasName(state) + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js new file mode 100644 index 0000000000000..45202099dedf8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -0,0 +1,238 @@ +/* + * 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 { + PHASE_HOT, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE, + PHASE_ENABLED, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_SIZE_STORED, + STRUCTURE_INDEX_TEMPLATE, + STRUCTURE_CONFIGURATION, + STRUCTURE_PRIMARY_NODES, + STRUCTURE_REPLICAS, + STRUCTURE_TEMPLATE_SELECTION, + STRUCTURE_TEMPLATE_NAME, + STRUCTURE_POLICY_NAME, + STRUCTURE_POLICY_CONFIGURATION, + STRUCTURE_INDEX_NAME, + STRUCTURE_ALIAS_NAME, + ERROR_STRUCTURE, + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_SHRINK_ENABLED, + STRUCTURE_REVIEW, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS +} from '../constants'; +import { + getPhase, + getPhases, + phaseToES, + getSelectedPolicyName, + getSelectedIndexTemplateName, + getFullSelectedIndexTemplate, + isNumber, + getSelectedPrimaryShardCount, + getSelectedReplicaCount, + getSaveAsNewPolicy, + getSelectedOriginalPolicyName, + getBootstrapEnabled, + getIndexName, + getAliasName, +} from '.'; + +export const validatePhase = (type, phase, state) => { + const errors = {}; + + if (!phase[PHASE_ENABLED]) { + return errors; + } + + if (phase[PHASE_ROLLOVER_ENABLED]) { + if ( + !isNumber(phase[PHASE_ROLLOVER_MAX_AGE]) && + !isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED]) + ) { + errors[PHASE_ROLLOVER_MAX_AGE] = [ + 'A maximum age is required' + ]; + errors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ + 'A maximum index size is required' + ]; + } + } + + for (const numberedAttribute of PHASE_ATTRIBUTES_THAT_ARE_NUMBERS) { + if (phase.hasOwnProperty(numberedAttribute) && phase[numberedAttribute] !== '') { + // If shrink is disabled, there is no need to validate this + if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && !phase[PHASE_SHRINK_ENABLED]) { + continue; + } + if (!isNumber(phase[numberedAttribute])) { + errors[numberedAttribute] = ['A number is required']; + } + else if (phase[numberedAttribute] < 0) { + errors[numberedAttribute] = ['Only positive numbers are allowed']; + } + else if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && phase[numberedAttribute] < 1) { + errors[numberedAttribute] = ['Only positive numbers are allowed']; + } + } + } + + if (phase[PHASE_SHRINK_ENABLED]) { + const selectedTemplate = getFullSelectedIndexTemplate(state); + // shrink options not shown in GUI for primary shard count of 1, so don't validate + if (selectedTemplate && selectedTemplate.settings.number_of_shards > 1) { + if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + errors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; + } + else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { + errors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers above 0 are allowed.']; + } + } + } + + if (phase[PHASE_FORCE_MERGE_ENABLED]) { + if (!isNumber(phase[PHASE_FORCE_MERGE_SEGMENTS])) { + errors[PHASE_FORCE_MERGE_SEGMENTS] = ['A number is required.']; + } + else if (phase[PHASE_FORCE_MERGE_SEGMENTS] < 1) { + errors[PHASE_FORCE_MERGE_SEGMENTS] = ['Only positive numbers above 0 are allowed.']; + } + } + + return errors; +}; + +export const validateLifecycle = state => { + // This method of deep copy does not always work but it should be fine here + const errors = JSON.parse(JSON.stringify(ERROR_STRUCTURE)); + if (!getSelectedIndexTemplateName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][ + STRUCTURE_TEMPLATE_NAME + ].push('An index template is required'); + } + + if (getBootstrapEnabled(state) && !getIndexName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_INDEX_NAME].push('An index name is required'); + } + + if (!getAliasName(state)) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('A write alias name is required'); + } + + if (!isNumber(getSelectedPrimaryShardCount(state))) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_PRIMARY_NODES + ].push('A value is required'); + } + else if (getSelectedPrimaryShardCount(state) < 1) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_PRIMARY_NODES + ].push('Only positive numbers are allowed'); + } + + if (!isNumber(getSelectedReplicaCount(state))) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_REPLICAS + ].push('A value is required'); + } + else if (getSelectedReplicaCount(state) < 0) { + errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ + STRUCTURE_REPLICAS + ].push('Only positive numbers are allowed'); + } + + if (!getSelectedPolicyName(state)) { + errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('A policy name is required'); + } + + if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { + errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('The policy name must be different'); + } + + // if (getSaveAsNewPolicy(state)) { + // const policyNames = getAllPolicyNamesFromTemplates(state); + // if (policyNames.includes(getSelectedPolicyName(state))) { + // errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('That policy name is already used.'); + // } + // } + + const hotPhase = getPhase(state, PHASE_HOT); + const warmPhase = getPhase(state, PHASE_WARM); + const coldPhase = getPhase(state, PHASE_COLD); + const deletePhase = getPhase(state, PHASE_DELETE); + + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT], + ...validatePhase(PHASE_HOT, hotPhase) + }; + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM], + ...validatePhase(PHASE_WARM, warmPhase, state) + }; + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD], + ...validatePhase(PHASE_COLD, coldPhase) + }; + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE] = { + ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE], + ...validatePhase(PHASE_DELETE, deletePhase) + }; + + if (warmPhase[PHASE_SHRINK_ENABLED]) { + if (isNumber(warmPhase[PHASE_PRIMARY_SHARD_COUNT]) && warmPhase[PHASE_PRIMARY_SHARD_COUNT] > 0) { + if (getSelectedPrimaryShardCount(state) % warmPhase[PHASE_PRIMARY_SHARD_COUNT] !== 0) { + errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM][PHASE_PRIMARY_SHARD_COUNT].push( + 'The shard count needs to be a divisor of the hot phase shard count.' + ); + } + } + } + + return errors; +}; + +export const getLifecycle = state => { + const phases = Object.entries(getPhases(state)).reduce( + (accum, [phaseName, phase]) => { + // Hot is ALWAYS enabled + if (phaseName === PHASE_HOT) { + phase[PHASE_ENABLED] = true; + } + + if (phase[PHASE_ENABLED]) { + accum[phaseName] = phaseToES(state, phase); + + // These seem to be constants + // TODO: verify this assumption + if (phaseName === PHASE_HOT) { + accum[phaseName].after = '0s'; + } + + if (phaseName === PHASE_DELETE) { + accum[phaseName].actions = { + ...accum[phaseName].actions, + delete: {} + }; + } + } + return accum; + }, + {} + ); + + return { + name: getSelectedPolicyName(state), + //type, TODO: figure this out (jsut store it and not let the user change it?) + phases + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js new file mode 100644 index 0000000000000..097704a96d475 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/nodes.js @@ -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 { createSelector } from 'reselect'; + +export const getNodes = state => state.nodes.nodes; +export const getNodeOptions = createSelector( + [state => getNodes(state)], + nodes => { + if (!nodes) { + return []; + } + + const options = Object.keys(nodes).map(attrs => ({ + text: `${attrs} (${nodes[attrs].length})`, + value: attrs, + })); + + options.sort((a, b) => a.value.localeCompare(b.value)); + return [{ text: 'Default allocation (don\'t use attributes)', value: '' }, ...options]; + } +); + +export const getSelectedPrimaryShardCount = state => + state.nodes.selectedPrimaryShardCount; +export const getSelectedReplicaCount = state => + state.nodes.selectedReplicaCount !== undefined ? state.nodes.selectedReplicaCount : 1; +export const getSelectedNodeAttrs = state => state.nodes.selectedNodeAttrs; +export const getNodesFromSelectedNodeAttrs = state => { + const nodes = getNodes(state)[getSelectedNodeAttrs(state)]; + if (nodes) { + return nodes.length; + } + return null; +}; + +export const getNodeDetails = (state, selectedNodeAttrs) => { + return state.nodes.details[selectedNodeAttrs]; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js new file mode 100644 index 0000000000000..665264d679ade --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -0,0 +1,236 @@ +/* + * 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 { + defaultColdPhase, + defaultDeletePhase, + defaultHotPhase, + defaultWarmPhase, +} from '../defaults'; +import { + PHASE_HOT, + PHASE_WARM, + PHASE_COLD, + PHASE_DELETE, + PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_ENABLED, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_NODE_ATTRS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ENABLED, + PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, + MAX_SIZE_TYPE_DOCUMENT, + WARM_PHASE_ON_ROLLOVER, + PHASE_SHRINK_ENABLED +} from '../constants'; +import { getIndexTemplates } from '.'; + +export const getPolicies = state => state.policies.policies; +export const getIsNewPolicy = state => state.policies.selectedPolicy.isNew; + +export const getSelectedPolicy = state => state.policies.selectedPolicy; +export const getIsSelectedPolicySet = state => state.policies.selectedPolicySet; +export const getSelectedOriginalPolicyName = state => state.policies.originalPolicyName; + +export const getSaveAsNewPolicy = state => + state.policies.selectedPolicy.saveAsNew; + +export const getSelectedPolicyName = state => { + if (!getSaveAsNewPolicy(state)) { + return getSelectedOriginalPolicyName(state); + } + return state.policies.selectedPolicy.name; +}; + +export const getAllPolicyNamesFromTemplates = state => { + return getIndexTemplates(state).map(template => template.index_lifecycle_name).filter(name => name); +}; + +export const getPhases = state => state.policies.selectedPolicy.phases; +export const getPhase = (state, phase) => + getPhases(state)[phase]; +export const getPhaseData = (state, phase, key) => { + if (PHASE_ATTRIBUTES_THAT_ARE_NUMBERS.includes(key)) { + return parseInt(getPhase(state, phase)[key]); + } + return getPhase(state, phase)[key]; +}; + +export const splitSizeAndUnits = field => { + let size; + let units; + + const result = /(\d+)(\w+)/.exec(field); + if (result) { + size = parseInt(result[1]) || 0; + units = result[2]; + } + + return { + size, + units + }; +}; + +export const isNumber = value => typeof value === 'number'; + +export const phaseFromES = (phase, phaseName, defaultPolicy) => { + const policy = { ...defaultPolicy }; + + if (!phase) { + return policy; + } + + policy[PHASE_ENABLED] = true; + policy[PHASE_ROLLOVER_ENABLED] = false; + + if (phase.after) { + if (phaseName === PHASE_WARM && phase.after === '0ms') { + policy[WARM_PHASE_ON_ROLLOVER] = true; + } else { + const { size: after, units: afterUnits } = splitSizeAndUnits( + phase.after + ); + policy[PHASE_ROLLOVER_AFTER] = after; + policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + } + } + if (phaseName === PHASE_WARM) { + policy[PHASE_SHRINK_ENABLED] = !!(phase.actions && phase.actions.shrink); + } + if (phase.actions) { + const actions = phase.actions; + + if (actions.rollover) { + const rollover = actions.rollover; + policy[PHASE_ROLLOVER_ENABLED] = true; + if (rollover.max_age) { + const { size: maxAge, units: maxAgeUnits } = splitSizeAndUnits( + rollover.max_age + ); + policy[PHASE_ROLLOVER_MAX_AGE] = maxAge; + policy[PHASE_ROLLOVER_MAX_AGE_UNITS] = maxAgeUnits; + } + if (rollover.max_size) { + const { size: maxSize, units: maxSizeUnits } = splitSizeAndUnits( + rollover.max_size + ); + policy[PHASE_ROLLOVER_MAX_SIZE_STORED] = maxSize; + policy[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] = maxSizeUnits; + } + if (rollover.max_docs) { + policy[PHASE_ROLLOVER_MAX_SIZE_STORED] = rollover.max_docs; + policy[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] = MAX_SIZE_TYPE_DOCUMENT; + } + } + + if (actions.allocate) { + const allocate = actions.allocate; + if (allocate.require) { + policy[PHASE_NODE_ATTRS] = allocate.require._name; + } + } + + if (actions.forcemerge) { + const forcemerge = actions.forcemerge; + policy[PHASE_FORCE_MERGE_ENABLED] = true; + policy[PHASE_FORCE_MERGE_SEGMENTS] = forcemerge.max_num_segments; + } + + if (actions.shrink) { + policy[PHASE_PRIMARY_SHARD_COUNT] = actions.shrink.number_of_shards; + } + + if (actions.replicas) { + const replicas = actions.replicas; + policy[PHASE_REPLICA_COUNT] = replicas.number_of_replicas; + } + } + return policy; +}; + +export const policyFromES = ({ name, type, phases }) => { + return { + name, + type, + phases: { + [PHASE_HOT]: phaseFromES(phases[PHASE_HOT], PHASE_HOT, defaultHotPhase), + [PHASE_WARM]: phaseFromES(phases[PHASE_WARM], PHASE_WARM, defaultWarmPhase), + [PHASE_COLD]: phaseFromES(phases[PHASE_COLD], PHASE_COLD, defaultColdPhase), + [PHASE_DELETE]: phaseFromES(phases[PHASE_DELETE], PHASE_DELETE, defaultDeletePhase) + }, + isNew: false, + saveAsNew: false + }; +}; + +export const phaseToES = (state, phase) => { + const esPhase = {}; + + if (!phase[PHASE_ENABLED]) { + return esPhase; + } + + if (isNumber(phase[PHASE_ROLLOVER_AFTER])) { + esPhase.after = `${phase[PHASE_ROLLOVER_AFTER]}${phase[PHASE_ROLLOVER_AFTER_UNITS]}`; + } + + esPhase.actions = {}; + + if (phase[PHASE_ROLLOVER_ENABLED]) { + esPhase.actions.rollover = {}; + + if (isNumber(phase[PHASE_ROLLOVER_MAX_AGE])) { + esPhase.actions.rollover.max_age = `${phase[PHASE_ROLLOVER_MAX_AGE]}${ + phase[PHASE_ROLLOVER_MAX_AGE_UNITS] + }`; + } else if (isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED])) { + if (phase[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] === MAX_SIZE_TYPE_DOCUMENT) { + esPhase.actions.rollover.max_docs = phase[PHASE_ROLLOVER_MAX_SIZE_STORED]; + } else { + esPhase.actions.rollover.max_size = `${phase[PHASE_ROLLOVER_MAX_SIZE_STORED]}${ + phase[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] + }`; + } + } + } + + if (phase[PHASE_NODE_ATTRS]) { + esPhase.actions.allocate = { + include: {}, // TODO: this seems to be a constant, confirm? + exclude: {}, // TODO: this seems to be a constant, confirm? + require: { + _name: phase[PHASE_NODE_ATTRS] + } + }; + if (isNumber(phase[PHASE_REPLICA_COUNT])) { + esPhase.actions.allocate.number_of_replicas = phase[PHASE_REPLICA_COUNT]; + } + } + + if (phase[PHASE_FORCE_MERGE_ENABLED]) { + esPhase.actions.forcemerge = { + max_num_segments: phase[PHASE_FORCE_MERGE_SEGMENTS] + }; + } + + if (phase[PHASE_SHRINK_ENABLED] && isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + esPhase.actions.shrink = { + number_of_shards: phase[PHASE_PRIMARY_SHARD_COUNT] + }; + } + return esPhase; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/store.js b/x-pack/plugins/index_lifecycle_management/public/store/store.js new file mode 100644 index 0000000000000..2e3b5c219dad8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/store/store.js @@ -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 { + createStore, + applyMiddleware, + compose +} from 'redux'; +import thunk from 'redux-thunk'; + +import { + indexLifecycleManagement +} from './reducers/'; +import { + autoEnablePhase, + setSelectedPolicyFromSelectedTemplate, + autoSetNodeAttrs +} from './middleware'; + +export const indexLifecycleManagementStore = (initialState = {}) => { + const enhancers = [applyMiddleware(thunk, autoEnablePhase, setSelectedPolicyFromSelectedTemplate, autoSetNodeAttrs)]; + + window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); + return createStore( + indexLifecycleManagement, + initialState, + compose(...enhancers) + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js new file mode 100644 index 0000000000000..b9a77a1a0362b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/call_with_request_factory.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { once } from 'lodash'; + +const callWithRequest = once((server) => { + const cluster = server.plugins.elasticsearch.getCluster('data'); + return cluster.callWithRequest; +}); + +export const callWithRequestFactory = (server, request) => { + return (...args) => { + return callWithRequest(server)(request, ...args); + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js new file mode 100644 index 0000000000000..787814d87dff9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/call_with_request_factory/index.js @@ -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 { callWithRequestFactory } from './call_with_request_factory'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js new file mode 100644 index 0000000000000..19a7b56759269 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/__tests__/check_license.js @@ -0,0 +1,146 @@ +/* + * 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'; +import { set } from 'lodash'; +import { checkLicense } from '../check_license'; + +describe('check_license', function () { + + let mockLicenseInfo; + beforeEach(() => mockLicenseInfo = {}); + + describe('license information is undefined', () => { + beforeEach(() => mockLicenseInfo = undefined); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is not available', () => { + beforeEach(() => mockLicenseInfo.isAvailable = () => false); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is available', () => { + beforeEach(() => { + mockLicenseInfo.isAvailable = () => true; + set(mockLicenseInfo, 'license.getType', () => 'basic'); + }); + + describe('& license is trial, standard, gold, platinum', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set isAvailable to true', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it ('should set enableLinks to true', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); + }); + + it('should not set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it ('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); + + describe('& license is basic', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set isAvailable to true', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it ('should set enableLinks to true', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); + }); + + it('should not set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set showLinks to true', () => { + expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js new file mode 100644 index 0000000000000..8a5a7d7029b71 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/check_license.js @@ -0,0 +1,58 @@ +/* + * 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 function checkLicense(xpackLicenseInfo) { + const pluginName = 'Index Management'; + + // If, for some reason, we cannot get the license information + // from Elasticsearch, assume worst case and disable + if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { + return { + isAvailable: false, + showLinks: true, + enableLinks: false, + message: `You cannot use ${pluginName} because license information is not available at this time.` + }; + } + + const VALID_LICENSE_MODES = [ + 'trial', + 'basic', + 'standard', + 'gold', + 'platinum' + ]; + + const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); + const isLicenseActive = xpackLicenseInfo.license.isActive(); + const licenseType = xpackLicenseInfo.license.getType(); + + // License is not valid + if (!isLicenseModeValid) { + return { + isAvailable: false, + showLinks: false, + message: `Your ${licenseType} license does not support ${pluginName}. Please upgrade your license.` + }; + } + + // License is valid but not active + if (!isLicenseActive) { + return { + isAvailable: false, + showLinks: true, + enableLinks: false, + message: `You cannot use ${pluginName} because your ${licenseType} license has expired.` + }; + } + + // License is valid and active + return { + isAvailable: true, + showLinks: true, + enableLinks: true + }; +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js new file mode 100644 index 0000000000000..f2c070fd44b6e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/check_license/index.js @@ -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 { checkLicense } from './check_license'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js new file mode 100644 index 0000000000000..443744ccb0cc8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_custom_error.js @@ -0,0 +1,21 @@ +/* + * 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'; +import { wrapCustomError } from '../wrap_custom_error'; + +describe('wrap_custom_error', () => { + describe('#wrapCustomError', () => { + it('should return a Boom object', () => { + const originalError = new Error('I am an error'); + const statusCode = 404; + const wrappedError = wrapCustomError(originalError, statusCode); + + expect(wrappedError.isBoom).to.be(true); + expect(wrappedError.output.statusCode).to.equal(statusCode); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js new file mode 100644 index 0000000000000..394c182140000 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_es_error.js @@ -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 expect from 'expect.js'; +import { wrapEsError } from '../wrap_es_error'; + +describe('wrap_es_error', () => { + describe('#wrapEsError', () => { + + let originalError; + beforeEach(() => { + originalError = new Error('I am an error'); + originalError.statusCode = 404; + }); + + it('should return a Boom object', () => { + const wrappedError = wrapEsError(originalError); + + expect(wrappedError.isBoom).to.be(true); + }); + + it('should return the correct Boom object', () => { + const wrappedError = wrapEsError(originalError); + + expect(wrappedError.output.statusCode).to.be(originalError.statusCode); + expect(wrappedError.output.payload.message).to.be(originalError.message); + }); + + it('should return the correct Boom object with custom message', () => { + const wrappedError = wrapEsError(originalError, { 404: 'No encontrado!' }); + + expect(wrappedError.output.statusCode).to.be(originalError.statusCode); + expect(wrappedError.output.payload.message).to.be('No encontrado!'); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js new file mode 100644 index 0000000000000..6d6a336417bef --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/__tests__/wrap_unknown_error.js @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { wrapUnknownError } from '../wrap_unknown_error'; + +describe('wrap_unknown_error', () => { + describe('#wrapUnknownError', () => { + it('should return a Boom object', () => { + const originalError = new Error('I am an error'); + const wrappedError = wrapUnknownError(originalError); + + expect(wrappedError.isBoom).to.be(true); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js new file mode 100644 index 0000000000000..f275f15637091 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/index.js @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { wrapCustomError } from './wrap_custom_error'; +export { wrapEsError } from './wrap_es_error'; +export { wrapUnknownError } from './wrap_unknown_error'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js new file mode 100644 index 0000000000000..890a366ac65c1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +/** + * Wraps a custom error into a Boom error response and returns it + * + * @param err Object error + * @param statusCode Error status code + * @return Object Boom error response + */ +export function wrapCustomError(err, statusCode) { + return Boom.wrap(err, statusCode); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js new file mode 100644 index 0000000000000..6ac4d50c7e0fe --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js @@ -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 Boom from 'boom'; + +/** + * Wraps an error thrown by the ES JS client into a Boom error response and returns it + * + * @param err Object Error thrown by ES JS client + * @param statusCodeToMessageMap Object Optional map of HTTP status codes => error messages + * @return Object Boom error response + */ +export function wrapEsError(err, statusCodeToMessageMap = {}) { + + const statusCode = err.statusCode; + + // If no custom message if specified for the error's status code, just + // wrap the error as a Boom error response and return it + if (!statusCodeToMessageMap[statusCode]) { + return Boom.wrap(err, err.statusCode); + } + + // Otherwise, use the custom message to create a Boom error response and + // return it + const message = statusCodeToMessageMap[statusCode]; + return Boom.create(statusCode, message); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js new file mode 100644 index 0000000000000..b0cdced7adbef --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +/** + * Wraps an unknown error into a Boom error response and returns it + * + * @param err Object Unknown error + * @return Object Boom error response + */ +export function wrapUnknownError(err) { + return Boom.wrap(err); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js new file mode 100644 index 0000000000000..d50ff9480d3e4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/__tests__/is_es_error_factory.js @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { isEsErrorFactory } from '../is_es_error_factory'; +import { set } from 'lodash'; + +class MockAbstractEsError {} + +describe('is_es_error_factory', () => { + + let mockServer; + let isEsError; + + beforeEach(() => { + const mockEsErrors = { + _Abstract: MockAbstractEsError + }; + mockServer = {}; + set(mockServer, 'plugins.elasticsearch.getCluster', () => ({ errors: mockEsErrors })); + + isEsError = isEsErrorFactory(mockServer); + }); + + describe('#isEsErrorFactory', () => { + + it('should return a function', () => { + expect(isEsError).to.be.a(Function); + }); + + describe('returned function', () => { + + it('should return true if passed-in err is a known esError', () => { + const knownEsError = new MockAbstractEsError(); + expect(isEsError(knownEsError)).to.be(true); + }); + + it('should return false if passed-in err is not a known esError', () => { + const unknownEsError = {}; + expect(isEsError(unknownEsError)).to.be(false); + + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js new file mode 100644 index 0000000000000..441648a8701e0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/index.js @@ -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 { isEsErrorFactory } from './is_es_error_factory'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js new file mode 100644 index 0000000000000..80daac5bd496d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/is_es_error_factory/is_es_error_factory.js @@ -0,0 +1,18 @@ +/* + * 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 { memoize } from 'lodash'; + +const esErrorsFactory = memoize((server) => { + return server.plugins.elasticsearch.getCluster('admin').errors; +}); + +export function isEsErrorFactory(server) { + const esErrors = esErrorsFactory(server); + return function isEsError(err) { + return err instanceof esErrors._Abstract; + }; +} diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js new file mode 100644 index 0000000000000..b72f8cc769731 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js @@ -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 expect from 'expect.js'; +import { licensePreRoutingFactory } from '../license_pre_routing_factory'; + +describe('license_pre_routing_factory', () => { + describe('#reportingFeaturePreRoutingFactory', () => { + let mockServer; + let mockLicenseCheckResults; + + beforeEach(() => { + mockServer = { + plugins: { + xpack_main: { + info: { + feature: () => ({ + getLicenseCheckResults: () => mockLicenseCheckResults + }) + } + } + } + }; + }); + + it('only instantiates one instance per server', () => { + const firstInstance = licensePreRoutingFactory(mockServer); + const secondInstance = licensePreRoutingFactory(mockServer); + + expect(firstInstance).to.be(secondInstance); + }); + + describe('isAvailable is false', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: false + }; + }); + + it ('replies with 403', (done) => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + licensePreRouting(stubRequest, (response) => { + expect(response).to.be.an(Error); + expect(response.isBoom).to.be(true); + expect(response.output.statusCode).to.be(403); + done(); + }); + }); + }); + + describe('isAvailable is true', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: true + }; + }); + + it ('replies with nothing', (done) => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + licensePreRouting(stubRequest, (response) => { + expect(response).to.be(undefined); + done(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js new file mode 100644 index 0000000000000..0743e443955f4 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/index.js @@ -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 { licensePreRoutingFactory } from './license_pre_routing_factory'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js new file mode 100644 index 0000000000000..b3720ab265393 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { once } from 'lodash'; +import { wrapCustomError } from '../error_wrappers'; +import { PLUGIN } from '../../../common/constants'; + +export const licensePreRoutingFactory = once((server) => { + const xpackMainPlugin = server.plugins.xpack_main; + + // License checking and enable/disable logic + function licensePreRouting(request, reply) { + const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); + if (!licenseCheckResults.isAvailable) { + const error = new Error(licenseCheckResults.message); + const statusCode = 403; + const wrappedError = wrapCustomError(error, statusCode); + reply(wrappedError); + } else { + reply(); + } + } + + return licensePreRouting; +}); + diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js new file mode 100644 index 0000000000000..7b0f97c38d129 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/index.js @@ -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 { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js new file mode 100644 index 0000000000000..35bc4b7533605 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/lib/register_license_checker/register_license_checker.js @@ -0,0 +1,21 @@ +/* + * 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 { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; +import { checkLicense } from '../check_license'; +import { PLUGIN } from '../../../common/constants'; + +export function registerLicenseChecker(server) { + const xpackMainPlugin = server.plugins.xpack_main; + const ilmPlugin = server.plugins.index_lifecycle_management; + + mirrorPluginStatus(xpackMainPlugin, ilmPlugin); + xpackMainPlugin.status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js new file mode 100644 index 0000000000000..915fb695bb468 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js @@ -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 { registerIndicesRoutes } from './register_indices_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js new file mode 100644 index 0000000000000..dcd053e096c52 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js @@ -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 { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; + +async function bootstrap(callWithRequest, payload) { + await callWithRequest('indices.create', { + index: payload.indexName, + body: { + aliases: { + [payload.aliasName]: { + is_write_alias: true + } + }, + } + }); +} + +export function registerBootstrapRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/indices/bootstrap', + method: 'POST', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await bootstrap(callWithRequest, request.payload); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [licensePreRouting] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js new file mode 100644 index 0000000000000..8f60bffb94b5a --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; + +async function fetchTemplates(callWithRequest) { + const params = { + method: 'GET', + path: '/_template', + // we allow 404 in case there are no templates + ignore: [404] + }; + + return await callWithRequest('transport.request', params); +} + +async function getAffectedIndices( + callWithRequest, + indexTemplateName, + policyName +) { + const templates = await fetchTemplates(callWithRequest); + + if (!templates || Object.keys(templates).length === 0 || templates.status === 404) { + return []; + } + + const indexPatterns = Object.entries(templates).reduce((accum, [templateName, template]) => { + const isMatchingTemplate = templateName === indexTemplateName; + const isMatchingPolicy = ( + policyName && + template.settings && + template.settings.index && + template.settings.index.lifecycle && + template.settings.index.lifecycle.name === policyName + ); + if (isMatchingTemplate || isMatchingPolicy) { + accum.push(...template.index_patterns); + } + return accum; + }, []); + + if (indexPatterns.length === 0) { + return []; + } + const indexParams = { + method: 'GET', + path: `/${indexPatterns.join(',')}`, + // we allow 404 in case there are no indices + ignore: [404] + }; + const indices = await callWithRequest('transport.request', indexParams); + + if (!indices || indices.status === 404) { + return []; + } + + return Object.keys(indices); +} + +export function registerGetAffectedRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: + '/api/index_lifecycle_management/indices/affected/{indexTemplateName}', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await getAffectedIndices( + callWithRequest, + request.params.indexTemplateName, + ); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [licensePreRouting] + } + }); + + server.route({ + path: + '/api/index_lifecycle_management/indices/affected/{indexTemplateName}/{policyName}', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await getAffectedIndices( + callWithRequest, + request.params.indexTemplateName, + request.params.policyName + ); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [licensePreRouting] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js new file mode 100644 index 0000000000000..1f94eb13d125e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js @@ -0,0 +1,13 @@ +/* + * 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 { registerBootstrapRoute } from './register_bootstrap_route'; +import { registerGetAffectedRoute } from './register_get_affected_route'; + +export function registerIndicesRoutes(server) { + registerBootstrapRoute(server); + registerGetAffectedRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js new file mode 100644 index 0000000000000..17f52a723405d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/index.js @@ -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 { registerLifecycleRoutes } from './register_lifecycle_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js new file mode 100644 index 0000000000000..14cf049454a34 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { merge } from 'lodash'; + +async function createLifecycle(callWithRequest, lifecycle) { + const body = { + policy: { + phases: lifecycle.phases, + } + }; + const params = { + method: 'PUT', + path: `/_ilm/${lifecycle.name}`, + ignore: [ 404 ], + body, + }; + + return await callWithRequest('transport.request', params); +} + +async function getIndexTemplate(callWithRequest, indexTemplate) { + const response = await callWithRequest('indices.getTemplate', { name: indexTemplate }); + return response[indexTemplate]; +} + +async function updateIndexTemplate(callWithRequest, indexTemplatePatch) { + // Fetch existing template + const template = await getIndexTemplate(callWithRequest, indexTemplatePatch.indexTemplate); + merge(template, { + settings: { + index: { + number_of_shards: indexTemplatePatch.primaryShardCount, + number_of_replicas: indexTemplatePatch.replicaCount, + lifecycle: { + name: indexTemplatePatch.lifecycleName, + rollover_alias: indexTemplatePatch.rolloverAlias + }, + routing: { + allocation: { + include: { + sattr_name: indexTemplatePatch.nodeAttrs, + } + } + } + } + } + }); + + const params = { + method: 'PUT', + path: `/_template/${indexTemplatePatch.indexTemplate}`, + ignore: [ 404 ], + body: template, + }; + + return await callWithRequest('transport.request', params); +} + +export function registerCreateRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/lifecycle', + method: 'POST', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await createLifecycle(callWithRequest, request.payload.lifecycle); + const response2 = await updateIndexTemplate(callWithRequest, request.payload.indexTemplatePatch); + reply([response, response2]); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js new file mode 100644 index 0000000000000..ba179d14b8112 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_lifecycle_routes.js @@ -0,0 +1,11 @@ +/* + * 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 { registerCreateRoute } from './register_create_route'; + +export function registerLifecycleRoutes(server) { + registerCreateRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js new file mode 100644 index 0000000000000..d6a9dd774e206 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/constants.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const NODE_ATTRS_KEYS_TO_IGNORE = [ + 'ml.enabled', + 'ml.machine_memory', + 'ml.max_open_jobs' +]; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js new file mode 100644 index 0000000000000..ef0ac271ae60e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/index.js @@ -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 { registerNodesRoutes } from './register_nodes_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js new file mode 100644 index 0000000000000..6cd882caf73a0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +function findMatchingNodes(stats, nodeAttrs) { + return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { + const attributes = stats.attributes || {}; + for (const [key, value] of Object.entries(attributes)) { + if (`${key}:${value}` === nodeAttrs) { + accum.push({ + nodeId, + stats, + }); + break; + } + } + return accum; + }, []); +} + +async function fetchNodeStats(callWithRequest) { + const params = { + format: 'json' + }; + + return await callWithRequest('nodes.stats', params); +} + +export function registerDetailsRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/nodes/{nodeAttrs}/details', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const stats = await fetchNodeStats(callWithRequest); + const response = findMatchingNodes(stats, request.params.nodeAttrs); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js new file mode 100644 index 0000000000000..8e92e6570edcf --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { NODE_ATTRS_KEYS_TO_IGNORE } from './constants'; + +function convertStatsIntoList(stats) { + return Object.entries(stats.nodes).reduce((accum, [nodeId, stats]) => { + const attributes = stats.attributes || {}; + for (const [key, value] of Object.entries(attributes)) { + if (!NODE_ATTRS_KEYS_TO_IGNORE.includes(key)) { + const attributeString = `${key}:${value}`; + accum[attributeString] = accum[attributeString] || []; + accum[attributeString].push(nodeId); + } + } + return accum; + }, {}); +} + +async function fetchNodeStats(callWithRequest) { + const params = { + format: 'json' + }; + + return await callWithRequest('nodes.stats', params); +} + +export function registerListRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/nodes/list', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const stats = await fetchNodeStats(callWithRequest); + const response = convertStatsIntoList(stats); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js new file mode 100644 index 0000000000000..341f1d4f1ebf3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_nodes_routes.js @@ -0,0 +1,13 @@ +/* + * 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 { registerListRoute } from './register_list_route'; +import { registerDetailsRoute } from './register_details_route'; + +export function registerNodesRoutes(server) { + registerListRoute(server); + registerDetailsRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js new file mode 100644 index 0000000000000..7c6103a3389ab --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/index.js @@ -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 { registerPoliciesRoutes } from './register_policies_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js new file mode 100644 index 0000000000000..75e88426f65f8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +function formatHits(hits) { + if (hits.status === 404) { + return []; + } + return Object.keys(hits).reduce((accum, lifecycleName) => { + const hit = hits[lifecycleName]; + accum.push({ + ...hit, + name: lifecycleName, + }); + return accum; + }, []); +} + +async function fetchPolicies(callWithRequest) { + const params = { + method: 'GET', + path: '/_ilm', + // we allow 404 since they may have no policies + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +export function registerFetchRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/policies', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const hits = await fetchPolicies(callWithRequest); + const response = formatHits(hits); + reply(response); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js new file mode 100644 index 0000000000000..676121eadccb1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_policies_routes.js @@ -0,0 +1,11 @@ +/* + * 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 { registerFetchRoute } from './register_fetch_route'; + +export function registerPoliciesRoutes(server) { + registerFetchRoute(server); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js new file mode 100644 index 0000000000000..dc9a0acaaf09b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/index.js @@ -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 { registerTemplatesRoutes } from './register_templates_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js new file mode 100644 index 0000000000000..1a4c5529b8da8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +async function formatTemplates(templates, callWithRequest) { + const formattedTemplates = []; + const templateNames = Object.keys(templates); + for (const templateName of templateNames) { + const { settings, index_patterns } = templates[templateName]; // eslint-disable-line camelcase + const formattedTemplate = { + index_lifecycle_name: settings.index && settings.index.lifecycle ? settings.index.lifecycle.name : undefined, + index_patterns, + allocation_rules: settings.index && settings.index.routing ? settings.index.routing : undefined, + settings, + name: templateName, + }; + + const { indices } = await fetchIndices(index_patterns, callWithRequest); + formattedTemplate.indices = indices ? Object.keys(indices) : []; + formattedTemplates.push(formattedTemplate); + } + return formattedTemplates; +} + +async function fetchTemplates(callWithRequest) { + const params = { + method: 'GET', + path: '/_template', + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +async function fetchIndices(indexPatterns, callWithRequest) { + const params = { + method: 'GET', + path: `/${indexPatterns}/_stats`, + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +export function registerFetchRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/templates', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const hits = await fetchTemplates(callWithRequest); + const templates = await formatTemplates(hits, callWithRequest); + reply(templates); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js new file mode 100644 index 0000000000000..690960e953c8c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +async function fetchTemplate(callWithRequest, templateName) { + const params = { + method: 'GET', + path: `/_template/${templateName}`, + // we allow 404 incase the user shutdown security in-between the check and now + ignore: [ 404 ] + }; + + return await callWithRequest('transport.request', params); +} + +export function registerGetRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/template/{templateName}', + method: 'GET', + handler: async (request, reply) => { + const callWithRequest = callWithRequestFactory(server, request); + const templateName = request.params.templateName; + + try { + const template = await fetchTemplate(callWithRequest, templateName); + reply(template[templateName]); + } catch (err) { + if (isEsError(err)) { + return reply(wrapEsError(err)); + } + + reply(wrapUnknownError(err)); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js new file mode 100644 index 0000000000000..9750c0157b965 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_templates_routes.js @@ -0,0 +1,16 @@ +/* + * 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 { registerFetchRoute } from './register_fetch_route'; +import { registerGetRoute } from './register_get_route'; + +export function registerTemplatesRoutes(server) { + registerFetchRoute(server); + registerGetRoute(server); +} diff --git a/x-pack/plugins/index_management/public/styles/table.less b/x-pack/plugins/index_management/public/styles/table.less index a93fe289f9b63..c93f91e79224b 100644 --- a/x-pack/plugins/index_management/public/styles/table.less +++ b/x-pack/plugins/index_management/public/styles/table.less @@ -12,7 +12,7 @@ .indexTableHorizontalScrollContainer { overflow-x: auto; max-width: 100%; - height: 100vh; + min-height: 100vh; } .indexTableHorizontalScroll { min-width: 800px; diff --git a/yarn.lock b/yarn.lock index 25c1cab071a68..04650f15c3d1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11375,9 +11375,6 @@ regex-cache@^0.4.2: regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" regexpu-core@^1.0.0: version "1.0.0" From b7d83de508282748b435c9e02e34b7b604f8fe42 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 4 Oct 2018 12:14:31 -0400 Subject: [PATCH 070/102] adjusting to changes in ES API --- .../index_lifecycle_management/README.md | 2 +- .../components/cold_phase/cold_phase.js | 19 +++++++-------- .../components/delete_phase/delete_phase.js | 19 +++++++-------- .../components/hot_phase/hot_phase.js | 1 - .../components/warm_phase/warm_phase.js | 19 +++++++-------- .../public/store/constants.js | 18 +++++++------- .../public/store/defaults/cold_phase.js | 8 +++---- .../public/store/defaults/delete_phase.js | 8 +++---- .../public/store/defaults/warm_phase.js | 8 +++---- .../public/store/selectors/lifecycle.js | 2 +- .../public/store/selectors/policies.js | 24 +++++++++---------- 11 files changed, 62 insertions(+), 66 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md index 14297875d177c..a25136adbf8de 100644 --- a/x-pack/plugins/index_lifecycle_management/README.md +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -51,7 +51,7 @@ One example is the `auto_enable_phase.js` middleware. By default, the warm, cold #### Generic phase data -Each of our four phases have some similar and some unique configuration options. Instead of making each individual phase a specific action for that phase, the code is written more generically to capture any data change within a phase to a single action. Therefore, each phase component's configuration inputs will look similar, like: `setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value)`. The top level container for each phase will handle automatically prefixing the `setPhaseData` prop with the right phase: ` setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value),`. +Each of our four phases have some similar and some unique configuration options. Instead of making each individual phase a specific action for that phase, the code is written more generically to capture any data change within a phase to a single action. Therefore, each phase component's configuration inputs will look similar, like: `setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value)`. The top level container for each phase will handle automatically prefixing the `setPhaseData` prop with the right phase: ` setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value),`. To complement this generic logic, there is a list of constants that are used to ensure the right pieces of data are changed. These are contained within `store/constants.js` diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js index 05f385e57dea8..63c0da1a47f8a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js @@ -24,8 +24,8 @@ import { import { PHASE_ENABLED, PHASE_ROLLOVER_ALIAS, - PHASE_ROLLOVER_AFTER, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_NODE_ATTRS, PHASE_REPLICA_COUNT } from '../../../../../../store/constants'; @@ -42,11 +42,11 @@ export class ColdPhase extends PureComponent { phaseData: PropTypes.shape({ [PHASE_ENABLED]: PropTypes.bool.isRequired, [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, - [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]).isRequired, - [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ PropTypes.number, @@ -123,14 +123,14 @@ export class ColdPhase extends PureComponent { { - setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); validate(); }} min={1} @@ -140,14 +140,13 @@ export class ColdPhase extends PureComponent { - setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value) } options={[ { value: 'd', text: 'days' }, { value: 'h', text: 'hours' }, - { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js index c714990824fa5..ab5258cc1a67e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js @@ -26,8 +26,8 @@ import { } from '@elastic/eui'; import { PHASE_ENABLED, - PHASE_ROLLOVER_AFTER, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, } from '../../../../../../store/constants'; import { ErrableFormRow } from '../../../../form_errors'; @@ -40,11 +40,11 @@ export class DeletePhase extends PureComponent { errors: PropTypes.object.isRequired, phaseData: PropTypes.shape({ [PHASE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([ + [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]).isRequired, - [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired }).isRequired }; @@ -109,14 +109,14 @@ export class DeletePhase extends PureComponent { { - setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); validate(); }} min={1} @@ -126,14 +126,13 @@ export class DeletePhase extends PureComponent { - setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value) + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value) } options={[ { value: 'd', text: 'days' }, { value: 'h', text: 'hours' }, - { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js index 7af9cb5a6ce46..347b0f1d8117f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js @@ -197,7 +197,6 @@ export class HotPhase extends PureComponent { options={[ { value: 'd', text: 'days' }, { value: 'h', text: 'hours' }, - { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js index 478ea106c89b7..8685f0b86a22a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js @@ -31,8 +31,8 @@ import { PHASE_NODE_ATTRS, PHASE_PRIMARY_SHARD_COUNT, PHASE_REPLICA_COUNT, - PHASE_ROLLOVER_AFTER, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_SHRINK_ENABLED, } from '../../../../../../store/constants'; import { ErrableFormRow } from '../../../../form_errors'; @@ -57,8 +57,8 @@ export class WarmPhase extends PureComponent { [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) .isRequired, [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - [PHASE_ROLLOVER_AFTER]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - [PHASE_ROLLOVER_AFTER_UNITS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, }).isRequired, hotPhaseReplicaCount: PropTypes.number.isRequired, @@ -147,14 +147,14 @@ export class WarmPhase extends PureComponent { { - setPhaseData(PHASE_ROLLOVER_AFTER, e.target.value); + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); validate(); }} min={1} @@ -164,15 +164,14 @@ export class WarmPhase extends PureComponent { { - await setPhaseData(PHASE_ROLLOVER_AFTER_UNITS, e.target.value); + await setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value); validate(); }} options={[ { value: 'd', text: 'days' }, { value: 'h', text: 'hours' }, - { value: 's', text: 'seconds' }, ]} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js index b8e918ef5e4b2..748a4843e20af 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/constants.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -23,8 +23,8 @@ export const PHASE_ROLLOVER_MAX_AGE_UNITS = 'selectedMaxAgeUnits'; export const PHASE_ROLLOVER_MAX_SIZE_STORED = 'selectedMaxSizeStored'; export const PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS = 'selectedMaxSizeStoredUnits'; export const PHASE_ROLLOVER_MAX_DOC_SIZE = 'selectedMaxDocSize'; -export const PHASE_ROLLOVER_AFTER = 'selectedAfter'; -export const PHASE_ROLLOVER_AFTER_UNITS = 'selectedAfterUnits'; +export const PHASE_ROLLOVER_MINIMUM_AGE = 'selectedMinimumAge'; +export const PHASE_ROLLOVER_MINIMUM_AGE_UNITS = 'selectedMinimumAgeUnits'; export const PHASE_FORCE_MERGE_SEGMENTS = 'selectedForceMergeSegments'; export const PHASE_FORCE_MERGE_ENABLED = 'forceMergeEnabled'; @@ -39,7 +39,7 @@ export const PHASE_ATTRIBUTES_THAT_ARE_NUMBERS = [ PHASE_ROLLOVER_MAX_AGE, PHASE_ROLLOVER_MAX_SIZE_STORED, PHASE_ROLLOVER_MAX_DOC_SIZE, - PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_MINIMUM_AGE, PHASE_FORCE_MERGE_SEGMENTS, PHASE_PRIMARY_SHARD_COUNT, PHASE_REPLICA_COUNT, @@ -84,8 +84,8 @@ export const ERROR_STRUCTURE = { }, [PHASE_WARM]: { [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_AFTER]: [], - [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_ROLLOVER_MINIMUM_AGE]: [], + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], [PHASE_NODE_ATTRS]: [], [PHASE_PRIMARY_SHARD_COUNT]: [], [PHASE_REPLICA_COUNT]: [], @@ -93,15 +93,15 @@ export const ERROR_STRUCTURE = { }, [PHASE_COLD]: { [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_AFTER]: [], - [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_ROLLOVER_MINIMUM_AGE]: [], + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], [PHASE_NODE_ATTRS]: [], [PHASE_REPLICA_COUNT]: [], }, [PHASE_DELETE]: { [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_AFTER]: [], - [PHASE_ROLLOVER_AFTER_UNITS]: [], + [PHASE_ROLLOVER_MINIMUM_AGE]: [], + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], }, }, [STRUCTURE_REVIEW]: { diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js index 66750a824cca8..dcc2c6156f119 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/cold_phase.js @@ -5,18 +5,18 @@ */ import { PHASE_ENABLED, - PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_MINIMUM_AGE, PHASE_NODE_ATTRS, PHASE_REPLICA_COUNT, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_ROLLOVER_ALIAS, } from '../constants'; export const defaultColdPhase = { [PHASE_ENABLED]: false, [PHASE_ROLLOVER_ALIAS]: '', - [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 'd', + [PHASE_ROLLOVER_MINIMUM_AGE]: '', + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: 'd', [PHASE_NODE_ATTRS]: '', [PHASE_REPLICA_COUNT]: '' }; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js index 236e20c0c36d4..e5326615e536a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/delete_phase.js @@ -6,8 +6,8 @@ import { PHASE_ENABLED, PHASE_ROLLOVER_ENABLED, - PHASE_ROLLOVER_AFTER, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_ROLLOVER_ALIAS, } from '../constants'; @@ -15,6 +15,6 @@ export const defaultDeletePhase = { [PHASE_ENABLED]: false, [PHASE_ROLLOVER_ENABLED]: false, [PHASE_ROLLOVER_ALIAS]: '', - [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 'd' + [PHASE_ROLLOVER_MINIMUM_AGE]: '', + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: 'd' }; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js index 84ffa9c742043..a18648f911730 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/defaults/warm_phase.js @@ -7,11 +7,11 @@ import { PHASE_ENABLED, PHASE_FORCE_MERGE_SEGMENTS, PHASE_FORCE_MERGE_ENABLED, - PHASE_ROLLOVER_AFTER, + PHASE_ROLLOVER_MINIMUM_AGE, PHASE_NODE_ATTRS, PHASE_PRIMARY_SHARD_COUNT, PHASE_REPLICA_COUNT, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_ROLLOVER_ALIAS, PHASE_SHRINK_ENABLED, WARM_PHASE_ON_ROLLOVER @@ -22,8 +22,8 @@ export const defaultWarmPhase = { [PHASE_ROLLOVER_ALIAS]: '', [PHASE_FORCE_MERGE_SEGMENTS]: '', [PHASE_FORCE_MERGE_ENABLED]: false, - [PHASE_ROLLOVER_AFTER]: '', - [PHASE_ROLLOVER_AFTER_UNITS]: 'd', + [PHASE_ROLLOVER_MINIMUM_AGE]: '', + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: 'd', [PHASE_NODE_ATTRS]: '', [PHASE_SHRINK_ENABLED]: true, [PHASE_PRIMARY_SHARD_COUNT]: '', diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index 45202099dedf8..640885e7056d8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -215,7 +215,7 @@ export const getLifecycle = state => { // These seem to be constants // TODO: verify this assumption if (phaseName === PHASE_HOT) { - accum[phaseName].after = '0s'; + accum[phaseName].minimum_age = '0s'; } if (phaseName === PHASE_DELETE) { diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 665264d679ade..5d644cae1c252 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -18,8 +18,8 @@ import { PHASE_WARM, PHASE_COLD, PHASE_DELETE, - PHASE_ROLLOVER_AFTER, - PHASE_ROLLOVER_AFTER_UNITS, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_ROLLOVER_ENABLED, PHASE_ROLLOVER_MAX_AGE, PHASE_ROLLOVER_MAX_AGE_UNITS, @@ -97,15 +97,15 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { policy[PHASE_ENABLED] = true; policy[PHASE_ROLLOVER_ENABLED] = false; - if (phase.after) { - if (phaseName === PHASE_WARM && phase.after === '0ms') { + if (phase.minimum_age) { + if (phaseName === PHASE_WARM && phase.minimum_age === '0ms') { policy[WARM_PHASE_ON_ROLLOVER] = true; } else { - const { size: after, units: afterUnits } = splitSizeAndUnits( - phase.after + const { size: minAge, units: minAgeUnits } = splitSizeAndUnits( + phase.minimum_age ); - policy[PHASE_ROLLOVER_AFTER] = after; - policy[PHASE_ROLLOVER_AFTER_UNITS] = afterUnits; + policy[PHASE_ROLLOVER_MINIMUM_AGE] = minAge; + policy[PHASE_ROLLOVER_MINIMUM_AGE_UNITS] = minAgeUnits; } } if (phaseName === PHASE_WARM) { @@ -162,10 +162,10 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { return policy; }; -export const policyFromES = ({ name, type, phases }) => { +export const policyFromES = (policy) => { + const { name, policy: { phases } } = policy; return { name, - type, phases: { [PHASE_HOT]: phaseFromES(phases[PHASE_HOT], PHASE_HOT, defaultHotPhase), [PHASE_WARM]: phaseFromES(phases[PHASE_WARM], PHASE_WARM, defaultWarmPhase), @@ -184,8 +184,8 @@ export const phaseToES = (state, phase) => { return esPhase; } - if (isNumber(phase[PHASE_ROLLOVER_AFTER])) { - esPhase.after = `${phase[PHASE_ROLLOVER_AFTER]}${phase[PHASE_ROLLOVER_AFTER_UNITS]}`; + if (isNumber(phase[PHASE_ROLLOVER_MINIMUM_AGE])) { + esPhase.minimum_age = `${phase[PHASE_ROLLOVER_MINIMUM_AGE]}${phase[PHASE_ROLLOVER_MINIMUM_AGE_UNITS]}`; } esPhase.actions = {}; From c5aef9076d3757bac0acc90051024cd595709202 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 22 Oct 2018 12:30:02 -0400 Subject: [PATCH 071/102] adding version and modified date to policies table --- .../components/policy_table/policy_table.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index a718d6c020a02..33e8b3f6cf91e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -5,6 +5,7 @@ */ import React, { Component } from 'react'; +import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { NoMatch } from '../no_match'; @@ -40,6 +41,12 @@ const HEADERS = { coveredIndices: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.coveredIndicesHeader', { defaultMessage: 'Covered Indices', }), + version: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.versionHeader', { + defaultMessage: 'Version', + }), + modified_date: i18n.translate('xpack.indexLifecycleMgmt.policyTable.headers.modifiedDateHeader', { + defaultMessage: 'Modified date', + }), }; export class PolicyTableUi extends Component { @@ -170,15 +177,14 @@ export class PolicyTableUi extends Component { {value} ); - } else if (fieldName === 'coveredIndices') { - if (!value) { - return null; - } + } else if (fieldName === 'coveredIndices' && value) { return ( {value.length} ({value.join(', ')}) ); + } else if (fieldName === 'modified_date' && value) { + return moment(value).format('YYYY-MM-DD HH:mm:ss'); } return value; } From 0712fd13b45b28ddb8815d4cabea905796dc82d3 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 25 Oct 2018 14:49:33 -0400 Subject: [PATCH 072/102] implementing new CRUD approach --- .../public/api/index.js | 8 +- .../index_lifecycle_management/public/app.js | 6 +- .../public/register_management_section.js | 2 +- .../public/register_routes.js | 2 +- .../cold_phase/cold_phase.container.js | 34 ++ .../components/cold_phase/cold_phase.js | 224 +++++++++++ .../components/cold_phase}/index.js | 4 +- .../delete_phase/delete_phase.container.js | 20 + .../components/delete_phase/delete_phase.js | 152 ++++++++ .../components/delete_phase/index.js | 7 + .../hot_phase/hot_phase.container.js | 23 ++ .../components/hot_phase/hot_phase.js | 202 ++++++++++ .../edit_policy/components/hot_phase/index.js | 7 + .../components/node_attrs_details/index.js | 7 + .../node_attrs_details.container.js | 18 + .../node_attrs_details/node_attrs_details.js | 80 ++++ .../components/warm_phase/index.js | 7 + .../warm_phase/warm_phase.container.js | 33 ++ .../components/warm_phase/warm_phase.js | 352 ++++++++++++++++++ .../edit_policy/edit_policy.container.js | 43 +++ .../sections/edit_policy/edit_policy.js | 236 ++++++++++++ .../sections/edit_policy/form_errors.js | 36 ++ .../public/sections/edit_policy/index.js | 7 + .../components/policy_table/policy_table.js | 13 +- .../public/store/actions/lifecycle.js | 13 +- .../public/store/actions/policies.js | 3 +- .../public/store/constants.js | 72 ++-- .../store/middleware/auto_enable_phase.js | 27 -- .../store/middleware/auto_set_node_attrs.js | 37 -- ..._selected_policy_from_selected_template.js | 42 --- .../public/store/reducers/policies.js | 2 + .../public/store/selectors/lifecycle.js | 121 ++---- .../public/store/selectors/policies.js | 2 +- .../public/store/store.js | 8 +- .../api/lifecycle/register_create_route.js | 42 +-- 35 files changed, 1573 insertions(+), 319 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js rename x-pack/plugins/index_lifecycle_management/public/{store/middleware => sections/edit_policy/components/cold_phase}/index.js (53%) create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/form_errors.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index 31f1cb9226910..683ba29a2dbb2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -44,15 +44,11 @@ export async function deletePolicies(policyNames) { return response.data; } -export async function saveLifecycle(lifecycle, indexTemplatePatch) { - const response = await httpClient.post(`${apiPrefix}/lifecycle`, { lifecycle, indexTemplatePatch }); +export async function saveLifecycle(lifecycle) { + const response = await httpClient.post(`${apiPrefix}/lifecycle`, { lifecycle }); return response.data; } -export async function bootstrap(indexName, aliasName) { - const response = await httpClient.post(`${apiPrefix}/indices/bootstrap`, { indexName, aliasName }); - return response.status === 200; -} export async function getAffectedIndices(indexTemplateName, policyName) { const path = policyName diff --git a/x-pack/plugins/index_lifecycle_management/public/app.js b/x-pack/plugins/index_lifecycle_management/public/app.js index abcae68e948c4..d973dbb56cb5d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/app.js +++ b/x-pack/plugins/index_lifecycle_management/public/app.js @@ -6,15 +6,15 @@ import React from 'react'; import { HashRouter, Switch, Route } from 'react-router-dom'; -import { Wizard } from './sections/wizard'; +import { EditPolicy } from './sections/edit_policy'; import { PolicyTable } from './sections/policy_table'; import { BASE_PATH } from '../common/constants'; export const App = () => ( - - + + ); diff --git a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js index b1364aa7de612..461f94c573c5f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js @@ -12,6 +12,6 @@ esSection.register('index_lifecycle_management', { visible: true, display: 'Index Lifecycle Management', order: 1, - url: `#${BASE_PATH}wizard` + url: `#${BASE_PATH}policies` }); diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js index acffbf8717154..6395abca92c3c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_routes.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -31,7 +31,7 @@ const renderReact = async (elem) => { ); }; -routes.when(`${BASE_PATH}:view?/:id?`, { +routes.when(`${BASE_PATH}:view?/:action?/:id?`, { template: template, controllerAs: 'indexManagement', controller: class IndexManagementController { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.container.js new file mode 100644 index 0000000000000..d8f2b97e253a3 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.container.js @@ -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 { connect } from 'react-redux'; +import { ColdPhase as PresentationComponent } from './cold_phase'; +import { + getNodeOptions, + getPhase, + getPhaseData +} from '../../../../store/selectors'; +import { setPhaseData, fetchNodes } from '../../../../store/actions'; +import { + PHASE_COLD, + PHASE_WARM, + PHASE_REPLICA_COUNT +} from '../../../../store/constants'; + +export const ColdPhase = connect( + (state) => ({ + phaseData: getPhase(state, PHASE_COLD), + nodeOptions: getNodeOptions(state), + warmPhaseReplicaCount: getPhaseData(state, PHASE_WARM, PHASE_REPLICA_COUNT) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value), + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js new file mode 100644 index 0000000000000..3e24bc18da1be --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js @@ -0,0 +1,224 @@ +/* + * 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, { PureComponent, Fragment } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiButtonEmpty, + EuiDescribedFormGroup, + EuiBadge, + EuiButton, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, + PHASE_NODE_ATTRS, + PHASE_REPLICA_COUNT +} from '../../../../store/constants'; +import { ErrableFormRow } from '../../form_errors'; + +export class ColdPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + showNodeDetailsFlyout: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, + [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired + }).isRequired, + warmPhaseReplicaCount: PropTypes.number.isRequired, + nodeOptions: PropTypes.array.isRequired + }; + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + setPhaseData, + showNodeDetailsFlyout, + phaseData, + nodeOptions, + warmPhaseReplicaCount, + errors, + isShowingErrors + } = this.props; + + return ( + + Cold phase{' '} + {phaseData[PHASE_ENABLED] ? ( + Active + ) : null} +
+ } + titleSize="s" + description={ + +

+ A cold index is queried less frequently + and thus no longer needs to be on the most performant hardware. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + {phaseData[PHASE_ENABLED] ? ( + + +
+ + { + await setPhaseData(PHASE_ENABLED, false); + }} + > + Deactive cold phase + +
+ + + + + + { + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); + }} + min={1} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + ]} + /> + + + + + + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} + > + View a list of nodes attached to this configuration + + ) : null} + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + }} + /> + + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + }} + min={0} + /> + + + + + + setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) + } + > + Set to same as warm phase + + + + +
+ ) : ( +
+ + { + await setPhaseData(PHASE_ENABLED, true); + + }} + > + Activate cold phase + +
+ )} + + ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/index.js similarity index 53% rename from x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js rename to x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/index.js index fcb0a960db4e2..e0d70ceb57726 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/index.js @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { autoEnablePhase } from './auto_enable_phase'; -export { setSelectedPolicyFromSelectedTemplate } from './set_selected_policy_from_selected_template'; -export { autoSetNodeAttrs } from './auto_set_node_attrs'; +export { ColdPhase } from './cold_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.container.js new file mode 100644 index 0000000000000..dcb0f9eb63107 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.container.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { connect } from 'react-redux'; +import { DeletePhase as PresentationComponent } from './delete_phase'; +import { getPhase } from '../../../../store/selectors'; +import { setPhaseData } from '../../../../store/actions'; +import { PHASE_DELETE } from '../../../../store/constants'; + +export const DeletePhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_DELETE) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_DELETE, key, value) + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js new file mode 100644 index 0000000000000..14e1540144e0f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js @@ -0,0 +1,152 @@ +/* + * 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, { PureComponent, Fragment } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiDescribedFormGroup, + EuiBadge, + EuiButton, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, +} from '../../../../store/constants'; +import { ErrableFormRow } from '../../form_errors'; + +export class DeletePhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired + }).isRequired + }; + + render() { + const { + setPhaseData, + phaseData, + errors, + isShowingErrors + } = this.props; + + return ( + + Delete phase{' '} + {phaseData[PHASE_ENABLED] ? ( + Active + ) : null} +
+ } + titleSize="s" + description={ + +

+ Use this phase to define how long to retain your data. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + {phaseData[PHASE_ENABLED] ? ( + + +
+ + { + await setPhaseData(PHASE_ENABLED, false); + }} + > + Deactive delete phase + +
+ + + +

Configuration

+
+ + + + + { + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); + }} + min={1} + /> + + + + + + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value) + } + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + ]} + /> + + + +
+ ) : ( +
+ + { + await setPhaseData(PHASE_ENABLED, true); + }} + > + Activate delete phase + +
+ )} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/index.js new file mode 100644 index 0000000000000..5f909ab2c0f79 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/index.js @@ -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 { DeletePhase } from './delete_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.container.js new file mode 100644 index 0000000000000..32a7100e3f646 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.container.js @@ -0,0 +1,23 @@ +/* + * 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 { connect } from 'react-redux'; +import { HotPhase as PresentationComponent } from './hot_phase'; +import { getPhase } from '../../../../store/selectors'; +import { setPhaseData } from '../../../../store/actions'; +import { PHASE_HOT } from '../../../../store/constants'; + +export const HotPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_HOT) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_HOT, key, value) + }, +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js new file mode 100644 index 0000000000000..4706b6527aae8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js @@ -0,0 +1,202 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFieldNumber, + EuiSelect, + EuiSwitch, + EuiFormRow, + EuiDescribedFormGroup, + EuiBadge, +} from '@elastic/eui'; +import { LearnMoreLink } from '../../../../components/learn_more_link'; +import { + PHASE_ROLLOVER_ALIAS, + PHASE_ROLLOVER_MAX_AGE, + PHASE_ROLLOVER_MAX_AGE_UNITS, + PHASE_ROLLOVER_MAX_SIZE_STORED, + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + PHASE_ROLLOVER_ENABLED, + MAX_SIZE_TYPE_DOCUMENT +} from '../../../../store/constants'; + +import { ErrableFormRow } from '../../form_errors'; + +export class HotPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MAX_AGE]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MAX_AGE_UNITS]: PropTypes.string.isRequired, + [PHASE_ROLLOVER_MAX_SIZE_STORED]: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired, + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: PropTypes.string.isRequired + }).isRequired + }; + + render() { + const { + setPhaseData, + phaseData, + isShowingErrors, + errors, + } = this.props; + + return ( + + Hot phase{' '} + Active + + } + titleSize="s" + description={ + +

+ This phase is required. A hot index is being queried and actively written to. + You can optimize this phase for write throughput. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + + If true, rollover the index when it gets too big or too old. The alias switches to the new index.{' '} + +

+ } + > + { + await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); + }} + label="Enable rollover" + /> +
+ {phaseData[PHASE_ROLLOVER_ENABLED] ? ( + + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED, + e.target.value + ); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, + e.target.value + ); + }} + options={[ + { value: 'gb', text: 'gigabytes' }, + { value: MAX_SIZE_TYPE_DOCUMENT, text: 'documents' } + ]} + /> + + + + + + + + { + await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_ROLLOVER_MAX_AGE_UNITS, + e.target.value + ); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + ]} + /> + + + + + ) : null} +
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/index.js new file mode 100644 index 0000000000000..114e34c3ef4d0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/index.js @@ -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 { HotPhase } from './hot_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/index.js new file mode 100644 index 0000000000000..885e965c46c4b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/index.js @@ -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 { NodeAttrsDetails } from './node_attrs_details.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.container.js new file mode 100644 index 0000000000000..3128a38c2c34f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.container.js @@ -0,0 +1,18 @@ +/* + * 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 { connect } from 'react-redux'; +import { NodeAttrsDetails as PresentationComponent } from './node_attrs_details'; +import { getNodeDetails, getExistingAllocationRules } from '../../../../store/selectors'; +import { fetchNodeDetails } from '../../../../store/actions'; + +export const NodeAttrsDetails = connect( + (state, ownProps) => ({ + details: getNodeDetails(state, ownProps.selectedNodeAttrs), + allocationRules: getExistingAllocationRules(state), + }), + { fetchNodeDetails } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js new file mode 100644 index 0000000000000..7cb2977e706ab --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js @@ -0,0 +1,80 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyout, + EuiTitle, + EuiInMemoryTable, + EuiSpacer, + EuiButtonEmpty, + EuiCallOut, + EuiPortal, +} from '@elastic/eui'; + +export class NodeAttrsDetails extends PureComponent { + static propTypes = { + fetchNodeDetails: PropTypes.func.isRequired, + close: PropTypes.func.isRequired, + + details: PropTypes.array, + selectedNodeAttrs: PropTypes.string.isRequired, + allocationRules: PropTypes.object, + }; + + componentWillMount() { + this.props.fetchNodeDetails(this.props.selectedNodeAttrs); + } + + render() { + const { selectedNodeAttrs, allocationRules, details, close } = this.props; + + return ( + + + + +

Nodes that contain the attribute: `{selectedNodeAttrs}`

+
+ + {allocationRules ? ( + + + Be aware that this index template has existing allocation rules + which will affect the list of nodes these indices can be allocated to. + + + + ) : null} + +
+ + + Close + + +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/index.js new file mode 100644 index 0000000000000..7eb5def486c87 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/index.js @@ -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 { WarmPhase } from './warm_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js new file mode 100644 index 0000000000000..b6d1c579de58f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js @@ -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 { connect } from 'react-redux'; +import { WarmPhase as PresentationComponent } from './warm_phase'; +import { + getNodeOptions, + getPhase, + getSelectedReplicaCount, + getSelectedPrimaryShardCount +} from '../../../../store/selectors'; +import { setPhaseData, fetchNodes } from '../../../../store/actions'; +import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../store/constants'; + +export const WarmPhase = connect( + state => ({ + phaseData: getPhase(state, PHASE_WARM), + hotPhaseReplicaCount: Number(getSelectedReplicaCount(state)), + hotPhasePrimaryShardCount: Number(getSelectedPrimaryShardCount(state)), + hotPhaseRolloverEnabled: getPhase(state, PHASE_HOT)[PHASE_ROLLOVER_ENABLED], + nodeOptions: getNodeOptions(state) + }), + { + setPhaseData: (key, value) => setPhaseData(PHASE_WARM, key, value), + fetchNodes + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js new file mode 100644 index 0000000000000..4320e043489f0 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js @@ -0,0 +1,352 @@ +/* + * 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, PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiTextColor, + EuiFormRow, + EuiFieldNumber, + EuiSelect, + EuiSwitch, + EuiButtonEmpty, + EuiDescribedFormGroup, + EuiBadge, + EuiButton, +} from '@elastic/eui'; +import { + PHASE_ENABLED, + WARM_PHASE_ON_ROLLOVER, + PHASE_ROLLOVER_ALIAS, + PHASE_FORCE_MERGE_ENABLED, + PHASE_FORCE_MERGE_SEGMENTS, + PHASE_NODE_ATTRS, + PHASE_PRIMARY_SHARD_COUNT, + PHASE_REPLICA_COUNT, + PHASE_ROLLOVER_MINIMUM_AGE, + PHASE_ROLLOVER_MINIMUM_AGE_UNITS, + PHASE_SHRINK_ENABLED, +} from '../../../../store/constants'; +import { ErrableFormRow } from '../../form_errors'; +import { LearnMoreLink } from '../../../../components/learn_more_link'; + +export class WarmPhase extends PureComponent { + static propTypes = { + setPhaseData: PropTypes.func.isRequired, + showNodeDetailsFlyout: PropTypes.func.isRequired, + + isShowingErrors: PropTypes.bool.isRequired, + errors: PropTypes.object.isRequired, + phaseData: PropTypes.shape({ + [PHASE_ENABLED]: PropTypes.bool.isRequired, + [WARM_PHASE_ON_ROLLOVER]: PropTypes.bool.isRequired, + [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, + [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, + [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, + [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, + [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + .isRequired, + [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, + }).isRequired, + + hotPhaseReplicaCount: PropTypes.number.isRequired, + hotPhasePrimaryShardCount: PropTypes.number.isRequired, + + nodeOptions: PropTypes.array.isRequired, + }; + + componentWillMount() { + this.props.fetchNodes(); + } + + render() { + const { + setPhaseData, + showNodeDetailsFlyout, + + phaseData, + hotPhaseReplicaCount, + hotPhasePrimaryShardCount, + nodeOptions, + errors, + isShowingErrors, + hotPhaseRolloverEnabled, + } = this.props; + + return ( + + Warm phase{' '} + {phaseData[PHASE_ENABLED] ? ( + Active + ) : null} + + } + titleSize="s" + description={ + +

+ Your index becomes read-only when it enters the warm phase. You can optimize this + phase for search. +

+ {isShowingErrors ? ( + + +

This phase contains errors

+
+
+ ) : null} +
+ } + fullWidth + > + + {phaseData[PHASE_ENABLED] ? ( + + +
+ { + await setPhaseData(PHASE_ENABLED, false); + }} + > + Deactivate warm phase + +
+
+ {hotPhaseRolloverEnabled ? ( + + { + await setPhaseData(WARM_PHASE_ON_ROLLOVER, e.target.checked); + }} + /> + + ) : null} + {!phaseData[WARM_PHASE_ON_ROLLOVER] ? ( + + + + { + setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); + }} + min={1} + /> + + + + + { + await setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value); + }} + options={[ + { value: 'd', text: 'days' }, + { value: 'h', text: 'hours' }, + ]} + /> + + + + ) : null} + + + + showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} + > + View node details + + ) : null + } + > + { + await setPhaseData(PHASE_NODE_ATTRS, e.target.value); + }} + /> + + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); + }} + min={0} + /> + + + + + { + await setPhaseData(PHASE_REPLICA_COUNT, hotPhaseReplicaCount); + }} + > + Set to same as hot phase + + + + + + + + + +

Shrink

+
+ + + Shrink the index into a new index with fewer primary shards.{' '} + + + + + + + { + await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); + }} + label="Shrink index" + /> + {phaseData[PHASE_SHRINK_ENABLED] ? ( + + + + + { + await setPhaseData(PHASE_PRIMARY_SHARD_COUNT, e.target.value); + }} + min={1} + /> + + + + + { + await setPhaseData( + PHASE_PRIMARY_SHARD_COUNT, + hotPhasePrimaryShardCount + ); + }} + > + Set to same as hot phase + + + + + + + ) : null} +
+ + +

Force merge

+
+ + + Reduce the number of segments in your shard by merging smaller files and clearing + deleted ones. + + + + + + { + await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); + }} + /> + + + + {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( + + { + await setPhaseData(PHASE_FORCE_MERGE_SEGMENTS, e.target.value); + }} + min={1} + /> + + ) : null} +
+ ) : ( + +
+ { + await setPhaseData(PHASE_ENABLED, true); + }} + > + Activate warm phase + +
+
+ )} +
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js new file mode 100644 index 0000000000000..2f3ab4a14b37a --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js @@ -0,0 +1,43 @@ +/* + * 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 { connect } from 'react-redux'; +import { EditPolicy as PresentationComponent } from './edit_policy'; +import { + getSaveAsNewPolicy, + getSelectedPolicy, + getAffectedIndexTemplates, + validateLifecycle, + getLifecycle, + getPolicies, + isPolicyListLoaded, +} from '../../store/selectors'; +import { + setSelectedPolicy, + setSelectedPolicyName, + setSaveAsNewPolicy, + saveLifecyclePolicy, + fetchPolicies, +} from '../../store/actions'; + +export const EditPolicy = connect( + state => ({ + errors: validateLifecycle(state), + selectedPolicy: getSelectedPolicy(state), + affectedIndexTemplates: getAffectedIndexTemplates(state), + saveAsNewPolicy: getSaveAsNewPolicy(state), + lifecycle: getLifecycle(state), + policies: getPolicies(state), + isPolicyListLoaded: isPolicyListLoaded(state), + }), + { + setSelectedPolicy, + setSelectedPolicyName, + setSaveAsNewPolicy, + saveLifecyclePolicy, + fetchPolicies, + } +)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js new file mode 100644 index 0000000000000..573fd98043285 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js @@ -0,0 +1,236 @@ +/* + * 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, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { toastNotifications } from 'ui/notify'; +import { goToPolicyList } from '../../services/navigation'; + +import { + EuiPage, + EuiPageBody, + EuiFieldText, + EuiPageContent, + EuiFormRow, + EuiTitle, + EuiText, + EuiSpacer, + EuiSwitch, + EuiHorizontalRule, + EuiButton, + EuiButtonEmpty, +} from '@elastic/eui'; +import { HotPhase } from './components/hot_phase'; +import { WarmPhase } from './components/warm_phase'; +import { DeletePhase } from './components/delete_phase'; +import { ColdPhase } from './components/cold_phase'; +import { + PHASE_HOT, + PHASE_COLD, + PHASE_DELETE, + PHASE_WARM, + STRUCTURE_POLICY_NAME, +} from '../../store/constants'; +import { hasErrors } from '../../lib/find_errors'; +import { NodeAttrsDetails } from './components/node_attrs_details'; +import { ErrableFormRow } from './form_errors'; + +export class EditPolicy extends Component { + static propTypes = { + selectedPolicy: PropTypes.string.isRequired, + errors: PropTypes.object.isRequired, + }; + + constructor(props) { + super(props); + this.state = { + isShowingErrors: false, + isShowingNodeDetailsFlyout: false, + selectedNodeAttrsForDetails: undefined, + }; + } + selectPolicy = policyName => { + const { setSelectedPolicy, policies } = this.props; + const selectedPolicy = policies.find(policy => { + return policy.name === policyName; + }); + if (selectedPolicy) { + setSelectedPolicy(selectedPolicy); + } + }; + componentDidMount() { + window.scrollTo(0, 0); + const { + match: { + params: { policyName }, + }, + isPolicyListLoaded, + fetchPolicies, + } = this.props; + if (policyName) { + if (isPolicyListLoaded) { + this.selectPolicy(policyName); + } else { + fetchPolicies(true, () => { + this.selectPolicy(policyName); + }); + } + } + } + backToPolicyList = () => { + this.props.setSelectedPolicy(null); + goToPolicyList(); + } + submit = async () => { + this.setState({ isShowingErrors: true }); + const { + errors, + saveLifecyclePolicy, + lifecycle, + saveAsNewPolicy, + } = this.props; + if (hasErrors(errors)) { + toastNotifications.addDanger('Please the fix errors on the page'); + } else { + const success = await saveLifecyclePolicy(lifecycle, saveAsNewPolicy); + if (success) { + this.backToPolicyList(); + } + } + }; + + showNodeDetailsFlyout = selectedNodeAttrsForDetails => { + this.setState({ isShowingNodeDetailsFlyout: true, selectedNodeAttrsForDetails }); + }; + render() { + const { + selectedPolicy, + errors, + match: { + params: { policyName }, + }, + setSaveAsNewPolicy, + saveAsNewPolicy, + setSelectedPolicyName, + } = this.props; + const selectedPolicyName = selectedPolicy.name; + const { isShowingErrors } = this.state; + + return ( + + + + +

+ {!selectedPolicyName + ? 'Create a lifecycle policy' + : `Edit lifecycle policy ${selectedPolicyName}`} +

+
+
+ + +

Configure the phases of your data and when to transition between them.

+
+ + + {policyName ? ( + + + +

+ You are editing an existing policy. Any changes you make + will also change index templates that this policy is attached to. + Alternately, you can save these changes in a new policy and only change + the index template you selected. +

+
+ +
+ {policyName ? ( + + { + await setSaveAsNewPolicy(e.target.checked); + }} + label={ + + Save this as a new policy + + } + /> + + ) : null} +
+ ) : null} + {saveAsNewPolicy || !policyName ? ( + + + { + await setSelectedPolicyName(e.target.value); + }} + /> + + + ) : null} +
+ + + + + + + + + + + Back + +    + + Save your policy + + {this.state.isShowingNodeDetailsFlyout ? ( + this.setState({ isShowingNodeDetailsFlyout: false })} + /> + ) : null} +
+
+
+
+ ); + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/form_errors.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/form_errors.js new file mode 100644 index 0000000000000..fc3c29c4beb0c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/form_errors.js @@ -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, { cloneElement, Children, Fragment } from 'react'; +import { EuiFormRow } from '@elastic/eui'; + +export const ErrableFormRow = ({ + errorKey, + isShowingErrors, + errors, + children, + ...rest +}) => { + return ( + 0 + } + error={errors[errorKey]} + {...rest} + > + + {Children.map(children, child => cloneElement(child, { + isInvalid: isShowingErrors && errors[errorKey].length > 0, + }))} + + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/index.js new file mode 100644 index 0000000000000..e4154a76289b6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/index.js @@ -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 { EditPolicy } from './edit_policy.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index 33e8b3f6cf91e..f14d3249f890b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -9,6 +9,7 @@ import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { NoMatch } from '../no_match'; +import { BASE_PATH } from '../../../../../common/constants'; import { EuiButton, EuiLink, @@ -170,9 +171,7 @@ export class PolicyTableUi extends Component { { - - }} + href={`#${BASE_PATH}policies/edit/${value}`} > {value} @@ -317,6 +316,14 @@ export class PolicyTableUi extends Component { aria-label="Search policies" />
+ + + Create new policy + +
diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js index 06287ed7160b9..f03c0018034a5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/lifecycle.js @@ -4,24 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createAction } from 'redux-actions'; import { toastNotifications } from 'ui/notify'; import { saveLifecycle as saveLifecycleApi } from '../../api'; -export const savedLifecycle = createAction('SAVED_LIFECYCLE'); -export const saveLifecycle = (lifecycle, indexTemplatePatch) => async dispatch => { - let saved; +export const saveLifecyclePolicy = (lifecycle, isNew) => async () => { try { - saved = await saveLifecycleApi(lifecycle, indexTemplatePatch); + await saveLifecycleApi(lifecycle); } catch (err) { toastNotifications.addDanger(err.data.message); return false; } - - toastNotifications.addSuccess(`Successfully created lifecycle policy '${lifecycle.name}'`); - - dispatch(savedLifecycle(saved)); + const verb = isNew ? 'created' : 'updated'; + toastNotifications.addSuccess(`Successfully ${verb} lifecycle policy '${lifecycle.name}'`); return true; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js index 8f31e0deb3814..926b524921481 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/actions/policies.js @@ -22,7 +22,7 @@ export const policyPageChanged = createAction('POLICY_PAGE_CHANGED'); export const policySortDirectionChanged = createAction('POLICY_SORT_DIRECTION_CHANGED'); export const policyFilterChanged = createAction('POLICY_FILTER_CHANGED'); -export const fetchPolicies = (withIndices) => async dispatch => { +export const fetchPolicies = (withIndices, callback) => async dispatch => { let policies; try { policies = await loadPolicies(withIndices); @@ -35,6 +35,7 @@ export const fetchPolicies = (withIndices) => async dispatch => { if (policies.length === 0) { dispatch(setSelectedPolicy()); } + callback && callback(); return policies; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/constants.js b/x-pack/plugins/index_lifecycle_management/public/store/constants.js index 748a4843e20af..873e4acd72312 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/constants.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/constants.js @@ -61,50 +61,34 @@ export const STRUCTURE_INDEX_NAME = 'indexName'; export const STRUCTURE_ALIAS_NAME = 'aliasName'; export const ERROR_STRUCTURE = { - [STRUCTURE_INDEX_TEMPLATE]: { - [STRUCTURE_TEMPLATE_SELECTION]: { - [STRUCTURE_TEMPLATE_NAME]: [], - [STRUCTURE_INDEX_NAME]: [], - [STRUCTURE_ALIAS_NAME]: [] - }, - [STRUCTURE_CONFIGURATION]: { - [STRUCTURE_NODE_ATTRS]: [], - [STRUCTURE_PRIMARY_NODES]: [], - [STRUCTURE_REPLICAS]: [] - } + [PHASE_HOT]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_MAX_AGE]: [], + [PHASE_ROLLOVER_MAX_AGE_UNITS]: [], + [PHASE_ROLLOVER_MAX_SIZE_STORED]: [], + [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: [], + [PHASE_ROLLOVER_MAX_DOC_SIZE]: [] }, - [STRUCTURE_POLICY_CONFIGURATION]: { - [PHASE_HOT]: { - [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_MAX_AGE]: [], - [PHASE_ROLLOVER_MAX_AGE_UNITS]: [], - [PHASE_ROLLOVER_MAX_SIZE_STORED]: [], - [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: [], - [PHASE_ROLLOVER_MAX_DOC_SIZE]: [] - }, - [PHASE_WARM]: { - [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_MINIMUM_AGE]: [], - [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], - [PHASE_NODE_ATTRS]: [], - [PHASE_PRIMARY_SHARD_COUNT]: [], - [PHASE_REPLICA_COUNT]: [], - [PHASE_FORCE_MERGE_SEGMENTS]: [], - }, - [PHASE_COLD]: { - [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_MINIMUM_AGE]: [], - [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], - [PHASE_NODE_ATTRS]: [], - [PHASE_REPLICA_COUNT]: [], - }, - [PHASE_DELETE]: { - [PHASE_ROLLOVER_ALIAS]: [], - [PHASE_ROLLOVER_MINIMUM_AGE]: [], - [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], - }, + [PHASE_WARM]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_MINIMUM_AGE]: [], + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], + [PHASE_NODE_ATTRS]: [], + [PHASE_PRIMARY_SHARD_COUNT]: [], + [PHASE_REPLICA_COUNT]: [], + [PHASE_FORCE_MERGE_SEGMENTS]: [], }, - [STRUCTURE_REVIEW]: { - [STRUCTURE_POLICY_NAME]: [], - } + [PHASE_COLD]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_MINIMUM_AGE]: [], + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], + [PHASE_NODE_ATTRS]: [], + [PHASE_REPLICA_COUNT]: [], + }, + [PHASE_DELETE]: { + [PHASE_ROLLOVER_ALIAS]: [], + [PHASE_ROLLOVER_MINIMUM_AGE]: [], + [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: [], + }, + [STRUCTURE_POLICY_NAME]: [], }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js deleted file mode 100644 index d7d390f608ccf..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_enable_phase.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { setPhaseData } from '../actions'; -import { getPhaseData } from '../selectors'; -import { SET_PHASE_DATA, PHASE_ENABLED } from '../constants'; - -const setsPerPhase = {}; - -export const autoEnablePhase = store => next => action => { - const state = store.getState(); - - if (action.type === SET_PHASE_DATA) { - const { phase } = action.payload; - setsPerPhase[phase] = setsPerPhase[phase] || 0; - setsPerPhase[phase]++; - - if (setsPerPhase[phase] === 1 && !getPhaseData(state, phase, PHASE_ENABLED)) { - store.dispatch(setPhaseData(phase, PHASE_ENABLED, true)); - } - } - - return next(action); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js deleted file mode 100644 index 5d59ccd8c4fe7..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/auto_set_node_attrs.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { setPhaseData } from '../actions'; -import { getPhaseData } from '../selectors'; -import { - SET_SELECTED_NODE_ATTRS, - SET_PHASE_DATA, - PHASE_WARM, - PHASE_NODE_ATTRS, - PHASE_COLD, -} from '../constants'; - -export const autoSetNodeAttrs = store => next => action => { - const state = store.getState(); - - if (action.type === SET_SELECTED_NODE_ATTRS) { - const warmPhaseAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); - if (!warmPhaseAttrs) { - store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, action.payload)); - } - } else if (action.type === SET_PHASE_DATA) { - const { phase, key, value } = action.payload; - - if (phase === PHASE_WARM && key === PHASE_NODE_ATTRS) { - const coldPhaseAttrs = getPhaseData(state, PHASE_COLD, PHASE_NODE_ATTRS); - if (!coldPhaseAttrs) { - store.dispatch(setPhaseData(PHASE_COLD, PHASE_NODE_ATTRS, value)); - } - } - } - - return next(action); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js b/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js deleted file mode 100644 index a87f9c3496d64..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/store/middleware/set_selected_policy_from_selected_template.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - fetchedIndexTemplate, - fetchPolicies, - setSelectedPolicy, - setPhaseData -} from '../actions'; -import { getSelectedNodeAttrs, getPhaseData } from '../selectors'; -import { PHASE_WARM, PHASE_NODE_ATTRS, PHASE_COLD } from '../constants'; - -export const setSelectedPolicyFromSelectedTemplate = store => next => async action => { - if (action.type === fetchedIndexTemplate().type) { - const template = action.payload; - if (template.settings.index && template.settings.index.lifecycle) { - const policies = await fetchPolicies()(store.dispatch); - const selectedPolicy = policies.find(policy => policy.name === template.settings.index.lifecycle.name); - if (selectedPolicy) { - store.dispatch(setSelectedPolicy(selectedPolicy)); - - // We also want to update node attrs for future phases if they do not exist - const state = store.getState(); - const hotNodeAttrs = getSelectedNodeAttrs(state); - const warmNodeAttrs = getPhaseData(state, PHASE_WARM, PHASE_NODE_ATTRS); - const coldNodeAttrs = getPhaseData(state, PHASE_COLD, PHASE_NODE_ATTRS); - - if (hotNodeAttrs && !warmNodeAttrs) { - store.dispatch(setPhaseData(PHASE_WARM, PHASE_NODE_ATTRS, hotNodeAttrs)); - } - if ((hotNodeAttrs || warmNodeAttrs) && !coldNodeAttrs) { - store.dispatch(setPhaseData(PHASE_COLD, PHASE_NODE_ATTRS, warmNodeAttrs || hotNodeAttrs)); - } - } - } - } - - return next(action); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js index bfeb72a6e50e8..27b5304fec1fa 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/reducers/policies.js @@ -49,6 +49,7 @@ export const defaultPolicy = { const defaultState = { isLoading: false, + isLoaded: false, originalPolicyName: undefined, selectedPolicySet: false, selectedPolicy: defaultPolicy, @@ -68,6 +69,7 @@ export const policies = handleActions( return { ...state, isLoading: false, + isLoaded: true, policies }; }, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index 640885e7056d8..cfd3d389a5628 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -13,21 +13,11 @@ import { PHASE_ROLLOVER_ENABLED, PHASE_ROLLOVER_MAX_AGE, PHASE_ROLLOVER_MAX_SIZE_STORED, - STRUCTURE_INDEX_TEMPLATE, - STRUCTURE_CONFIGURATION, - STRUCTURE_PRIMARY_NODES, - STRUCTURE_REPLICAS, - STRUCTURE_TEMPLATE_SELECTION, - STRUCTURE_TEMPLATE_NAME, STRUCTURE_POLICY_NAME, - STRUCTURE_POLICY_CONFIGURATION, - STRUCTURE_INDEX_NAME, - STRUCTURE_ALIAS_NAME, ERROR_STRUCTURE, PHASE_ATTRIBUTES_THAT_ARE_NUMBERS, PHASE_PRIMARY_SHARD_COUNT, PHASE_SHRINK_ENABLED, - STRUCTURE_REVIEW, PHASE_FORCE_MERGE_ENABLED, PHASE_FORCE_MERGE_SEGMENTS } from '../constants'; @@ -36,23 +26,17 @@ import { getPhases, phaseToES, getSelectedPolicyName, - getSelectedIndexTemplateName, - getFullSelectedIndexTemplate, isNumber, getSelectedPrimaryShardCount, - getSelectedReplicaCount, getSaveAsNewPolicy, getSelectedOriginalPolicyName, - getBootstrapEnabled, - getIndexName, - getAliasName, } from '.'; -export const validatePhase = (type, phase, state) => { - const errors = {}; +export const validatePhase = (type, phase, errors) => { + const phaseErrors = {}; if (!phase[PHASE_ENABLED]) { - return errors; + return; } if (phase[PHASE_ROLLOVER_ENABLED]) { @@ -60,10 +44,10 @@ export const validatePhase = (type, phase, state) => { !isNumber(phase[PHASE_ROLLOVER_MAX_AGE]) && !isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED]) ) { - errors[PHASE_ROLLOVER_MAX_AGE] = [ + phaseErrors[PHASE_ROLLOVER_MAX_AGE] = [ 'A maximum age is required' ]; - errors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ + phaseErrors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ 'A maximum index size is required' ]; } @@ -76,93 +60,56 @@ export const validatePhase = (type, phase, state) => { continue; } if (!isNumber(phase[numberedAttribute])) { - errors[numberedAttribute] = ['A number is required']; + phaseErrors[numberedAttribute] = ['A number is required']; } else if (phase[numberedAttribute] < 0) { - errors[numberedAttribute] = ['Only positive numbers are allowed']; + phaseErrors[numberedAttribute] = ['Only positive numbers are allowed']; } else if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && phase[numberedAttribute] < 1) { - errors[numberedAttribute] = ['Only positive numbers are allowed']; + phaseErrors[numberedAttribute] = ['Only positive numbers are allowed']; } } } if (phase[PHASE_SHRINK_ENABLED]) { - const selectedTemplate = getFullSelectedIndexTemplate(state); - // shrink options not shown in GUI for primary shard count of 1, so don't validate - if (selectedTemplate && selectedTemplate.settings.number_of_shards > 1) { - if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { - errors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; - } - else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { - errors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers above 0 are allowed.']; - } + if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { + phaseErrors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; + } + else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { + phaseErrors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers are allowed.']; } } if (phase[PHASE_FORCE_MERGE_ENABLED]) { if (!isNumber(phase[PHASE_FORCE_MERGE_SEGMENTS])) { - errors[PHASE_FORCE_MERGE_SEGMENTS] = ['A number is required.']; + phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = ['A number is required.']; } else if (phase[PHASE_FORCE_MERGE_SEGMENTS] < 1) { - errors[PHASE_FORCE_MERGE_SEGMENTS] = ['Only positive numbers above 0 are allowed.']; + phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = ['Only positive numbers above 0 are allowed.']; } } - - return errors; + errors[type] = { + ...errors[type], + ...phaseErrors + }; }; export const validateLifecycle = state => { // This method of deep copy does not always work but it should be fine here const errors = JSON.parse(JSON.stringify(ERROR_STRUCTURE)); - if (!getSelectedIndexTemplateName(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][ - STRUCTURE_TEMPLATE_NAME - ].push('An index template is required'); - } - - if (getBootstrapEnabled(state) && !getIndexName(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_INDEX_NAME].push('An index name is required'); - } - - if (!getAliasName(state)) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_TEMPLATE_SELECTION][STRUCTURE_ALIAS_NAME].push('A write alias name is required'); - } - - if (!isNumber(getSelectedPrimaryShardCount(state))) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ - STRUCTURE_PRIMARY_NODES - ].push('A value is required'); - } - else if (getSelectedPrimaryShardCount(state) < 1) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ - STRUCTURE_PRIMARY_NODES - ].push('Only positive numbers are allowed'); - } - - if (!isNumber(getSelectedReplicaCount(state))) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ - STRUCTURE_REPLICAS - ].push('A value is required'); - } - else if (getSelectedReplicaCount(state) < 0) { - errors[STRUCTURE_INDEX_TEMPLATE][STRUCTURE_CONFIGURATION][ - STRUCTURE_REPLICAS - ].push('Only positive numbers are allowed'); - } if (!getSelectedPolicyName(state)) { - errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('A policy name is required'); + errors[STRUCTURE_POLICY_NAME].push('A policy name is required'); } if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { - errors[STRUCTURE_REVIEW][STRUCTURE_POLICY_NAME].push('The policy name must be different'); + errors[STRUCTURE_POLICY_NAME].push('The policy name must be different'); } // if (getSaveAsNewPolicy(state)) { // const policyNames = getAllPolicyNamesFromTemplates(state); // if (policyNames.includes(getSelectedPolicyName(state))) { - // errors[STRUCTURE_POLICY_CONFIGURATION][STRUCTURE_POLICY_NAME].push('That policy name is already used.'); + // errors[STRUCTURE_POLICY_NAME].push('That policy name is already used.'); // } // } @@ -171,33 +118,19 @@ export const validateLifecycle = state => { const coldPhase = getPhase(state, PHASE_COLD); const deletePhase = getPhase(state, PHASE_DELETE); - errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT] = { - ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_HOT], - ...validatePhase(PHASE_HOT, hotPhase) - }; - errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM] = { - ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM], - ...validatePhase(PHASE_WARM, warmPhase, state) - }; - errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD] = { - ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_COLD], - ...validatePhase(PHASE_COLD, coldPhase) - }; - errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE] = { - ...errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_DELETE], - ...validatePhase(PHASE_DELETE, deletePhase) - }; - + validatePhase(PHASE_HOT, hotPhase, errors); + validatePhase(PHASE_WARM, warmPhase, errors); + validatePhase(PHASE_COLD, coldPhase, errors); + validatePhase(PHASE_DELETE, deletePhase, errors); if (warmPhase[PHASE_SHRINK_ENABLED]) { if (isNumber(warmPhase[PHASE_PRIMARY_SHARD_COUNT]) && warmPhase[PHASE_PRIMARY_SHARD_COUNT] > 0) { if (getSelectedPrimaryShardCount(state) % warmPhase[PHASE_PRIMARY_SHARD_COUNT] !== 0) { - errors[STRUCTURE_POLICY_CONFIGURATION][PHASE_WARM][PHASE_PRIMARY_SHARD_COUNT].push( + errors[PHASE_WARM][PHASE_PRIMARY_SHARD_COUNT].push( 'The shard count needs to be a divisor of the hot phase shard count.' ); } } } - return errors; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index b4b7e72545c36..9cf603c071faf 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -43,7 +43,6 @@ import { filterItems, sortTable } from '../../services'; export const getPolicies = state => state.policies.policies; export const getIsNewPolicy = state => state.policies.selectedPolicy.isNew; - export const getSelectedPolicy = state => state.policies.selectedPolicy; export const getIsSelectedPolicySet = state => state.policies.selectedPolicySet; export const getSelectedOriginalPolicyName = state => state.policies.originalPolicyName; @@ -51,6 +50,7 @@ export const getPolicyFilter = (state) => state.policies.filter; export const getPolicySort = (state) => state.policies.sort; export const getPolicyCurrentPage = (state) => state.policies.currentPage; export const getPolicyPageSize = (state) => state.policies.pageSize; +export const isPolicyListLoaded = (state) => state.policies.isLoaded; const getFilteredPolicies = createSelector( getPolicies, diff --git a/x-pack/plugins/index_lifecycle_management/public/store/store.js b/x-pack/plugins/index_lifecycle_management/public/store/store.js index 2e3b5c219dad8..151a7a5bf8b50 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/store.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/store.js @@ -14,14 +14,10 @@ import thunk from 'redux-thunk'; import { indexLifecycleManagement } from './reducers/'; -import { - autoEnablePhase, - setSelectedPolicyFromSelectedTemplate, - autoSetNodeAttrs -} from './middleware'; + export const indexLifecycleManagementStore = (initialState = {}) => { - const enhancers = [applyMiddleware(thunk, autoEnablePhase, setSelectedPolicyFromSelectedTemplate, autoSetNodeAttrs)]; + const enhancers = [applyMiddleware(thunk)]; window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); return createStore( diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js index 14cf049454a34..ca1a021c6100a 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -11,7 +11,6 @@ import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; -import { merge } from 'lodash'; async function createLifecycle(callWithRequest, lifecycle) { const body = { @@ -29,44 +28,6 @@ async function createLifecycle(callWithRequest, lifecycle) { return await callWithRequest('transport.request', params); } -async function getIndexTemplate(callWithRequest, indexTemplate) { - const response = await callWithRequest('indices.getTemplate', { name: indexTemplate }); - return response[indexTemplate]; -} - -async function updateIndexTemplate(callWithRequest, indexTemplatePatch) { - // Fetch existing template - const template = await getIndexTemplate(callWithRequest, indexTemplatePatch.indexTemplate); - merge(template, { - settings: { - index: { - number_of_shards: indexTemplatePatch.primaryShardCount, - number_of_replicas: indexTemplatePatch.replicaCount, - lifecycle: { - name: indexTemplatePatch.lifecycleName, - rollover_alias: indexTemplatePatch.rolloverAlias - }, - routing: { - allocation: { - include: { - sattr_name: indexTemplatePatch.nodeAttrs, - } - } - } - } - } - }); - - const params = { - method: 'PUT', - path: `/_template/${indexTemplatePatch.indexTemplate}`, - ignore: [ 404 ], - body: template, - }; - - return await callWithRequest('transport.request', params); -} - export function registerCreateRoute(server) { const isEsError = isEsErrorFactory(server); const licensePreRouting = licensePreRoutingFactory(server); @@ -79,8 +40,7 @@ export function registerCreateRoute(server) { try { const response = await createLifecycle(callWithRequest, request.payload.lifecycle); - const response2 = await updateIndexTemplate(callWithRequest, request.payload.indexTemplatePatch); - reply([response, response2]); + reply(response); } catch (err) { if (isEsError(err)) { return reply(wrapEsError(err)); From 71543748da9a9c01398432fdc6bfee7ad07f6b79 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 25 Oct 2018 15:31:04 -0400 Subject: [PATCH 073/102] simplified delete --- .../public/api/index.js | 4 +- .../components/policy_table/confirm_delete.js | 42 ++---- .../components/policy_table/policy_table.js | 133 +++++------------- 3 files changed, 49 insertions(+), 130 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index 683ba29a2dbb2..542acfe8e987b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -39,8 +39,8 @@ export async function loadPolicies(withIndices) { return response.data; } -export async function deletePolicies(policyNames) { - const response = await httpClient.delete(`${apiPrefix}/policies/${policyNames.join(',')}`); +export async function deletePolicy(policyName) { + const response = await httpClient.delete(`${apiPrefix}/policies/${policyName}`); return response.data; } diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js index 3b20042c7f3a4..9b3e250d6f958 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js @@ -4,60 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; import { toastNotifications } from 'ui/notify'; -import { deletePolicies } from '../../../../api'; +import { deletePolicy } from '../../../../api'; export class ConfirmDelete extends Component { - deletePolicies = async () => { - const { policiesToDelete, callback } = this.props; - const policyNames = policiesToDelete.map(policy => { - return policy.name; - }); + deletePolicy = async () => { + const { policyToDelete, callback } = this.props; + const policyName = policyToDelete.name; try { - await deletePolicies(policyNames); - toastNotifications.addSuccess(`Deleted policies ${policyNames.join(', ')}`); + await deletePolicy(policyName); + toastNotifications.addSuccess(`Deleted policy ${policyName}`); } catch (e) { - toastNotifications.addDanger(`Error deleting policies ${policyNames.join(', ')}`); + toastNotifications.addDanger(`Error deleting policy ${policyName}`); } if (callback) { callback(); } }; render() { - const { policiesToDelete, onCancel } = this.props; - const moreThanOne = policiesToDelete.length > 1; - const title = moreThanOne - ? `Delete ${policiesToDelete.length} policies` - : `Delete policy '${policiesToDelete[0].name}'`; + const { policyToDelete, onCancel } = this.props; + const title = `Delete policy '${policyToDelete.name}'`; return ( {}} >
- -

- You are about to delete {moreThanOne ? 'these' : 'this'} polic - {moreThanOne ? 'ies' : 'y'}: -

-
    - {policiesToDelete.map( - (({ name, coveredIndices }) => ( -
  • - {name} {coveredIndices ? `: ${coveredIndices.join(',')}` : null} -
  • - ): null) - )} -
-

This operation cannot be undone.

diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index f14d3249f890b..1ed6d7ab44863 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -13,7 +13,7 @@ import { BASE_PATH } from '../../../../../common/constants'; import { EuiButton, EuiLink, - EuiCheckbox, + EuiButtonIcon, EuiFieldSearch, EuiFlexGroup, EuiFlexItem, @@ -23,11 +23,9 @@ import { EuiTableBody, EuiTableHeader, EuiTableHeaderCell, - EuiTableHeaderCellCheckbox, EuiTablePagination, EuiTableRow, EuiTableRowCell, - EuiTableRowCellCheckbox, EuiTitle, EuiText, EuiPageBody, @@ -51,24 +49,6 @@ const HEADERS = { }; export class PolicyTableUi extends Component { - static getDerivedStateFromProps(props, state) { - // Deselect any policies which no longer exist, e.g. they've been deleted. - const { selectedPoliciesMap } = state; - const policyNames = props.policies.map(policy => policy.name); - const selectedPolicyNames = Object.keys(selectedPoliciesMap); - const missingPolicyNames = selectedPolicyNames.filter(selectedpolicyName => { - return !policyNames.includes(selectedpolicyName); - }); - - if (missingPolicyNames.length) { - const newMap = { ...selectedPoliciesMap }; - missingPolicyNames.forEach(missingPolicyName => delete newMap[missingPolicyName]); - return { selectedPoliciesMap: newMap }; - } - - return null; - } - constructor(props) { super(props); @@ -81,19 +61,21 @@ export class PolicyTableUi extends Component { this.props.fetchPolicies(true); } deleteConfirmation() { - if (!this.state.showDeleteConfirmation) { + const { policyToDelete } = this.state; + if (!policyToDelete) { return null; } return ( this.setState({ policyToDelete: null })} /> ); } handleDelete = () => { this.props.fetchPolicies(true); - this.setState({ showDeleteConfirmation: false }); + this.setState({ policyToDelete: null }); } onSort = column => { const { sortField, isSortAscending, policySortChanged } = this.props; @@ -101,54 +83,9 @@ export class PolicyTableUi extends Component { policySortChanged(column, newIsSortAscending); }; - toggleAll = () => { - const allSelected = this.areAllItemsSelected(); - if (allSelected) { - return this.setState({ selectedPoliciesMap: {} }); - } - const { policies } = this.props; - const selectedPoliciesMap = {}; - policies.forEach(({ name }) => { - selectedPoliciesMap[name] = true; - }); - this.setState({ - selectedPoliciesMap - }); - }; - - toggleItem = name => { - this.setState(({ selectedPoliciesMap }) => { - const newMap = { ...selectedPoliciesMap }; - if (newMap[name]) { - delete newMap[name]; - } else { - newMap[name] = true; - } - return { - selectedPoliciesMap: newMap - }; - }); - }; - - isItemSelected = name => { - return !!this.state.selectedPoliciesMap[name]; - }; - getSelectedPolicies() { - return this.props.policies.filter(({ name }) => { - return this.isItemSelected(name); - }); - } - areAllItemsSelected = () => { - const { policies } = this.props; - const unselectedItem = policies.find( - policy => !this.isItemSelected(policy.name) - ); - return !unselectedItem; - }; - buildHeader() { const { sortField, isSortAscending } = this.props; - return Object.entries(HEADERS).map(([fieldName, label]) => { + const headers = Object.entries(HEADERS).map(([fieldName, label]) => { const isSorted = sortField === fieldName; return ( ); }); + headers.push( + + ); + return headers; } buildRowCell(fieldName, value) { @@ -189,8 +134,8 @@ export class PolicyTableUi extends Component { } buildRowCells(policy) { - return Object.keys(HEADERS).map(fieldName => { - const { name } = policy; + const { name } = policy; + const cells = Object.keys(HEADERS).map(fieldName => { const value = policy[fieldName]; return ( ); }); + cells.push( + + this.setState({ policyToDelete: policy })} + iconType="trash" + /> + + ); + return cells; } buildRows() { - const { policies = [], detailPanelpolicyName } = this.props; + const { policies = [] } = this.props; return policies.map(policy => { const { name } = policy; return ( - - { - this.toggleItem(name); - }} - data-test-subj="policyTableRowCheckbox" - /> - {this.buildRowCells(policy)} ); @@ -271,7 +218,7 @@ export class PolicyTableUi extends Component {

@@ -280,7 +227,7 @@ export class PolicyTableUi extends Component {

@@ -331,14 +278,6 @@ export class PolicyTableUi extends Component { {policies.length > 0 ? ( - - - {this.buildHeader()} {this.buildRows()} From d0a45ae37e1b3c911fd4c4b28ab012dcc2f3273d Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 25 Oct 2018 15:34:38 -0400 Subject: [PATCH 074/102] cleanup edit_policy --- .../public/sections/edit_policy/edit_policy.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js index 573fd98043285..5cf7026f7bd55 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js @@ -40,7 +40,7 @@ import { ErrableFormRow } from './form_errors'; export class EditPolicy extends Component { static propTypes = { - selectedPolicy: PropTypes.string.isRequired, + selectedPolicy: PropTypes.object.isRequired, errors: PropTypes.object.isRequired, }; @@ -214,11 +214,11 @@ export class EditPolicy extends Component { isShowingErrors={isShowingErrors && hasErrors(errors[PHASE_DELETE])} /> - - Back + + Cancel    - + Save your policy {this.state.isShowingNodeDetailsFlyout ? ( From 450a993542205703c8d3b92b4a9c371fbf11412e Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 25 Oct 2018 15:36:14 -0400 Subject: [PATCH 075/102] removed wizard code --- .../configuration/configuration.container.js | 41 -- .../components/configuration/configuration.js | 179 --------- .../components/configuration/index.js | 7 - .../components/template_selection/index.js | 7 - .../template_selection.container.js | 41 -- .../template_selection/template_selection.js | 163 -------- .../wizard/components/index_template/index.js | 10 - .../index_template/index_template.js | 83 ---- .../components/node_attrs_details/index.js | 7 - .../node_attrs_details.container.js | 18 - .../node_attrs_details/node_attrs_details.js | 80 ---- .../cold_phase/cold_phase.container.js | 34 -- .../components/cold_phase/cold_phase.js | 231 ----------- .../components/cold_phase/index.js | 7 - .../delete_phase/delete_phase.container.js | 20 - .../components/delete_phase/delete_phase.js | 158 -------- .../components/delete_phase/index.js | 7 - .../hot_phase/hot_phase.container.js | 23 -- .../components/hot_phase/hot_phase.js | 210 ---------- .../components/hot_phase/index.js | 7 - .../components/warm_phase/index.js | 7 - .../warm_phase/warm_phase.container.js | 33 -- .../components/warm_phase/warm_phase.js | 371 ------------------ .../components/policy_configuration/index.js | 7 - .../policy_configuration.container.js | 47 --- .../policy_configuration.js | 177 --------- .../components/policy_selection/index.js | 7 - .../policy_selection.container.js | 30 -- .../policy_selection/policy_selection.js | 84 ---- .../wizard/components/review/diff_view.js | 107 ----- .../wizard/components/review/index.js | 7 - .../components/review/review.container.js | 59 --- .../wizard/components/review/review.js | 286 -------------- .../wizard/components/review/review.less | 7 - .../public/sections/wizard/form_errors.js | 36 -- .../public/sections/wizard/index.js | 7 - .../sections/wizard/wizard.container.js | 37 -- .../public/sections/wizard/wizard.js | 177 --------- 38 files changed, 2819 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js delete mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js deleted file mode 100644 index 01ee20f34d5db..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.container.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { Configuration as PresentationComponent } from './configuration'; -import { - getNodeOptions, - getSelectedPrimaryShardCount, - getSelectedReplicaCount, - getSelectedNodeAttrs, - getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount, -} from '../../../../../../store/selectors'; -import { - setSelectedNodeAttrs, - setSelectedPrimaryShardCount, - setSelectedReplicaCount, - fetchNodes -} from '../../../../../../store/actions'; - -export const Configuration = connect( - state => ({ - nodeOptions: getNodeOptions(state), - selectedNodeAttrs: getSelectedNodeAttrs(state), - selectedPrimaryShardCount: getSelectedPrimaryShardCount(state), - selectedReplicaCount: getSelectedReplicaCount(state), - selectedNodeAttrs: getSelectedNodeAttrs(state), - isPrimaryShardCountHigherThanSelectedNodeAttrsCount: getIsPrimaryShardCountHigherThanSelectedNodeAttrsCount(state), - }), - { - setSelectedNodeAttrs, - setSelectedPrimaryShardCount, - setSelectedReplicaCount, - fetchNodes - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js deleted file mode 100644 index f1f1e28f3e6ec..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/configuration.js +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiSelect, - EuiFieldNumber, - EuiCallOut, - EuiButtonEmpty, - EuiDescribedFormGroup, -} from '@elastic/eui'; -import { LearnMoreLink } from '../../../../../../components/learn_more_link'; -import { - STRUCTURE_NODE_ATTRS, - STRUCTURE_PRIMARY_NODES, - STRUCTURE_REPLICAS, -} from '../../../../../../store/constants'; - -import { ErrableFormRow } from '../../../../form_errors'; -import { NodeAttrsDetails } from '../../../node_attrs_details'; - -export class Configuration extends Component { - static propTypes = { - fetchNodes: PropTypes.func.isRequired, - setSelectedNodeAttrs: PropTypes.func.isRequired, - setSelectedPrimaryShardCount: PropTypes.func.isRequired, - setSelectedReplicaCount: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - - selectedPrimaryShardCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - selectedNodeAttrs: PropTypes.string.isRequired, - nodeOptions: PropTypes.array.isRequired, - selectedReplicaCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - isShowingErrors: PropTypes.bool.isRequired, - errors: PropTypes.object.isRequired, - isPrimaryShardCountHigherThanSelectedNodeAttrsCount: PropTypes.bool.isRequired, - }; - - constructor(props) { - super(props); - this.state = { - isShowingNodeDetailsFlyout: false, - }; - } - - componentWillMount() { - this.props.fetchNodes(); - } - - render() { - const { - setSelectedNodeAttrs, - setSelectedPrimaryShardCount, - setSelectedReplicaCount, - validate, - - nodeOptions, - selectedPrimaryShardCount, - selectedReplicaCount, - selectedNodeAttrs, - errors, - isShowingErrors, - isPrimaryShardCountHigherThanSelectedNodeAttrsCount, - } = this.props; - - const primaryNodeErrors = isPrimaryShardCountHigherThanSelectedNodeAttrsCount ? ( - - The shard count should be lower than the number of nodes that match the selected attributes. - - ) : null; - - return ( -
- Configure hot indices} - titleSize="s" - description="A hot index is actively being written to." - fullWidth - > - this.setState({ isShowingNodeDetailsFlyout: true })} - > - View a list of nodes attached to this configuration - - ) : null - } - > - { - await setSelectedNodeAttrs(e.target.value); - validate(); - }} - options={nodeOptions} - /> - - - The best way to determine how many shards you need is to benchmark using realistic - data and queries on your hardware.{' '} - -

- } - /> - - - - - { - await setSelectedPrimaryShardCount(e.target.value); - validate(); - }} - value={selectedPrimaryShardCount} - min={1} - /> - - - - - { - await setSelectedReplicaCount(e.target.value); - validate(); - }} - value={selectedReplicaCount} - min={0} - /> - - - - {this.state.isShowingNodeDetailsFlyout ? ( - this.setState({ isShowingNodeDetailsFlyout: false })} - /> - ) : null} - - {primaryNodeErrors} -
-
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js deleted file mode 100644 index 2a387bd853de8..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/configuration/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { Configuration } from './configuration.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js deleted file mode 100644 index a7c5cfb8f3dab..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { TemplateSelection } from './template_selection.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js deleted file mode 100644 index fb11120d37d3c..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.container.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { connect } from 'react-redux'; -import { TemplateSelection as PresentationComponent } from './template_selection'; -import { - getIndexTemplateOptions, - getSelectedIndexTemplateName, - getIndexName, - getAliasName, - getBootstrapEnabled, - getSelectedIndexTemplateIndices, -} from '../../../../../../store/selectors'; -import { - fetchIndexTemplates, - setSelectedIndexTemplate, - setAliasName, - setBootstrapEnabled, - setIndexName -} from '../../../../../../store/actions'; - -export const TemplateSelection = connect( - state => ({ - templateOptions: getIndexTemplateOptions(state), - selectedIndexTemplateName: getSelectedIndexTemplateName(state), - bootstrapEnabled: getBootstrapEnabled(state), - aliasName: getAliasName(state), - indexName: getIndexName(state), - selectedIndexTemplateIndices: getSelectedIndexTemplateIndices(state), - }), - { - fetchIndexTemplates, - setSelectedIndexTemplate, - setBootstrapEnabled, - setIndexName, - setAliasName, - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js deleted file mode 100644 index c72cbdf641337..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/components/template_selection/template_selection.js +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, Component } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiSelect, - EuiFormRow, - EuiSwitch, - EuiFieldText, - EuiDescribedFormGroup, - EuiSpacer, -} from '@elastic/eui'; - -import { LearnMoreLink } from '../../../../../../components/learn_more_link'; -import { ErrableFormRow } from '../../../../form_errors'; -import { - STRUCTURE_TEMPLATE_NAME, - STRUCTURE_INDEX_NAME, - STRUCTURE_ALIAS_NAME, -} from '../../../../../../store/constants'; - -export class TemplateSelection extends Component { - static propTypes = { - fetchIndexTemplates: PropTypes.func.isRequired, - setSelectedIndexTemplate: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - - selectedIndexTemplateName: PropTypes.string.isRequired, - templateOptions: PropTypes.array.isRequired, - errors: PropTypes.object.isRequired, - isShowingErrors: PropTypes.bool.isRequired, - }; - - constructor(props) { - super(props); - this.state = { - isIncludingSystemIndices: false, - }; - } - - componentWillMount() { - this.props.fetchIndexTemplates(); - } - - onChangeIncludingSystemIndices = e => { - this.setState({ isIncludingSystemIndices: e.target.checked }); - }; - - render() { - const { - setSelectedIndexTemplate, - validate, - setBootstrapEnabled, - setIndexName, - setAliasName, - - bootstrapEnabled, - selectedIndexTemplateIndices, - indexName, - aliasName, - selectedIndexTemplateName, - errors, - isShowingErrors, - } = this.props; - - const { isIncludingSystemIndices } = this.state; - - const templateOptions = this.props.templateOptions.filter(option => { - if (option.value && option.value.startsWith('.') && !isIncludingSystemIndices) { - return false; - } - return true; - }); - - return ( - Select an index template} - fullWidth - titleSize="s" - description={ -

- An index template defines the settings, mappings, and aliases to apply - when you create an index.{' '} - -

- } - > - - - - { - await setSelectedIndexTemplate(e.target.value); - validate(); - }} - options={templateOptions} - /> - - - { - await setAliasName(e.target.value); - validate(); - }} - /> - - {selectedIndexTemplateName && selectedIndexTemplateIndices.length === 0 ? ( - - - setBootstrapEnabled(e.target.checked)} - label={Create an index with this index template} - /> - - {bootstrapEnabled ? ( - - - { - await setIndexName(e.target.value); - validate(); - }} - /> - - - ) : null} - - ) : null} -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js deleted file mode 100644 index 20271f5f66ec2..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -export { IndexTemplate } from './index_template'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js deleted file mode 100644 index 70566e84867df..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/index_template/index_template.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { toastNotifications } from 'ui/notify'; - -import { TemplateSelection } from './components/template_selection'; -import { Configuration } from './components/configuration'; - -import { - EuiHorizontalRule, - EuiButton, -} from '@elastic/eui'; -import { hasErrors } from '../../../../lib/find_errors'; -import { - STRUCTURE_TEMPLATE_SELECTION, - STRUCTURE_CONFIGURATION, -} from '../../../../store/constants'; - -export class IndexTemplate extends Component { - static propTypes = { - done: PropTypes.func.isRequired, - - errors: PropTypes.object, - }; - - constructor(props) { - super(props); - this.state = { - isShowingErrors: false, - }; - } - - validate = async () => { - await this.props.validate(); - const noErrors = !hasErrors(this.props.errors); - return noErrors; - }; - - submit = async () => { - this.setState({ isShowingErrors: true }); - if (await this.validate()) { - this.props.done(); - } else { - toastNotifications.addDanger('Please fix the errors on the page'); - } - }; - - render() { - const { errors } = this.props; - const { isShowingErrors } = this.state; - - return ( -
- - - - - - - Continue - -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js deleted file mode 100644 index 885e965c46c4b..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { NodeAttrsDetails } from './node_attrs_details.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js deleted file mode 100644 index 3128a38c2c34f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.container.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { connect } from 'react-redux'; -import { NodeAttrsDetails as PresentationComponent } from './node_attrs_details'; -import { getNodeDetails, getExistingAllocationRules } from '../../../../store/selectors'; -import { fetchNodeDetails } from '../../../../store/actions'; - -export const NodeAttrsDetails = connect( - (state, ownProps) => ({ - details: getNodeDetails(state, ownProps.selectedNodeAttrs), - allocationRules: getExistingAllocationRules(state), - }), - { fetchNodeDetails } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js deleted file mode 100644 index 7cb2977e706ab..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/node_attrs_details/node_attrs_details.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, PureComponent } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlyout, - EuiTitle, - EuiInMemoryTable, - EuiSpacer, - EuiButtonEmpty, - EuiCallOut, - EuiPortal, -} from '@elastic/eui'; - -export class NodeAttrsDetails extends PureComponent { - static propTypes = { - fetchNodeDetails: PropTypes.func.isRequired, - close: PropTypes.func.isRequired, - - details: PropTypes.array, - selectedNodeAttrs: PropTypes.string.isRequired, - allocationRules: PropTypes.object, - }; - - componentWillMount() { - this.props.fetchNodeDetails(this.props.selectedNodeAttrs); - } - - render() { - const { selectedNodeAttrs, allocationRules, details, close } = this.props; - - return ( - - - - -

Nodes that contain the attribute: `{selectedNodeAttrs}`

-
- - {allocationRules ? ( - - - Be aware that this index template has existing allocation rules - which will affect the list of nodes these indices can be allocated to. - - - - ) : null} - -
- - - Close - - -
-
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js deleted file mode 100644 index bfeb21f8d32f1..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.container.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { ColdPhase as PresentationComponent } from './cold_phase'; -import { - getNodeOptions, - getPhase, - getPhaseData -} from '../../../../../../store/selectors'; -import { setPhaseData, fetchNodes } from '../../../../../../store/actions'; -import { - PHASE_COLD, - PHASE_WARM, - PHASE_REPLICA_COUNT -} from '../../../../../../store/constants'; - -export const ColdPhase = connect( - state => ({ - phaseData: getPhase(state, PHASE_COLD), - nodeOptions: getNodeOptions(state), - warmPhaseReplicaCount: getPhaseData(state, PHASE_WARM, PHASE_REPLICA_COUNT) - }), - { - setPhaseData: (key, value) => setPhaseData(PHASE_COLD, key, value), - fetchNodes - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js deleted file mode 100644 index 63c0da1a47f8a..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/cold_phase.js +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { PureComponent, Fragment } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiText, - EuiTextColor, - EuiFormRow, - EuiFieldNumber, - EuiSelect, - EuiButtonEmpty, - EuiDescribedFormGroup, - EuiBadge, - EuiButton, -} from '@elastic/eui'; -import { - PHASE_ENABLED, - PHASE_ROLLOVER_ALIAS, - PHASE_ROLLOVER_MINIMUM_AGE, - PHASE_ROLLOVER_MINIMUM_AGE_UNITS, - PHASE_NODE_ATTRS, - PHASE_REPLICA_COUNT -} from '../../../../../../store/constants'; -import { ErrableFormRow } from '../../../../form_errors'; - -export class ColdPhase extends PureComponent { - static propTypes = { - setPhaseData: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - showNodeDetailsFlyout: PropTypes.func.isRequired, - - isShowingErrors: PropTypes.bool.isRequired, - errors: PropTypes.object.isRequired, - phaseData: PropTypes.shape({ - [PHASE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, - [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired, - [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, - [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, - [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired - }).isRequired, - warmPhaseReplicaCount: PropTypes.number.isRequired, - nodeOptions: PropTypes.array.isRequired - }; - - componentWillMount() { - this.props.fetchNodes(); - } - - render() { - const { - setPhaseData, - validate, - showNodeDetailsFlyout, - - phaseData, - nodeOptions, - warmPhaseReplicaCount, - errors, - isShowingErrors - } = this.props; - - return ( - - Cold phase{' '} - {phaseData[PHASE_ENABLED] ? ( - Active - ) : null} - - } - titleSize="s" - description={ - -

- A cold index is queried less frequently - and thus no longer needs to be on the most performant hardware. -

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} -
- } - fullWidth - > - {phaseData[PHASE_ENABLED] ? ( - - -
- - { - await setPhaseData(PHASE_ENABLED, false); - validate(); - }} - > - Deactive cold phase - -
- - - - - - { - setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); - validate(); - }} - min={1} - /> - - - - - - setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value) - } - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' }, - ]} - /> - - - - - - - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} - > - View a list of nodes attached to this configuration -
- ) : null} - > - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - - - - - - { - await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); - validate(); - }} - min={0} - /> - - - - - - setPhaseData(PHASE_REPLICA_COUNT, warmPhaseReplicaCount) - } - > - Set to same as warm phase - - - - - - ) : ( -
- - { - await setPhaseData(PHASE_ENABLED, true); - validate(); - }} - > - Activate cold phase - -
- )} - - ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js deleted file mode 100644 index e0d70ceb57726..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/cold_phase/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { ColdPhase } from './cold_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js deleted file mode 100644 index 661489d2d9aa9..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.container.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { connect } from 'react-redux'; -import { DeletePhase as PresentationComponent } from './delete_phase'; -import { getPhase } from '../../../../../../store/selectors'; -import { setPhaseData } from '../../../../../../store/actions'; -import { PHASE_DELETE } from '../../../../../../store/constants'; - -export const DeletePhase = connect( - state => ({ - phaseData: getPhase(state, PHASE_DELETE) - }), - { - setPhaseData: (key, value) => setPhaseData(PHASE_DELETE, key, value) - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js deleted file mode 100644 index ab5258cc1a67e..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/delete_phase.js +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import React, { PureComponent, Fragment } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiSpacer, - EuiText, - EuiTextColor, - EuiFormRow, - EuiFieldNumber, - EuiSelect, - EuiDescribedFormGroup, - EuiBadge, - EuiButton, -} from '@elastic/eui'; -import { - PHASE_ENABLED, - PHASE_ROLLOVER_MINIMUM_AGE, - PHASE_ROLLOVER_MINIMUM_AGE_UNITS, -} from '../../../../../../store/constants'; -import { ErrableFormRow } from '../../../../form_errors'; - -export class DeletePhase extends PureComponent { - static propTypes = { - setPhaseData: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - - isShowingErrors: PropTypes.bool.isRequired, - errors: PropTypes.object.isRequired, - phaseData: PropTypes.shape({ - [PHASE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired, - [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired - }).isRequired - }; - - render() { - const { - setPhaseData, - validate, - phaseData, - errors, - isShowingErrors - } = this.props; - - return ( - - Delete phase{' '} - {phaseData[PHASE_ENABLED] ? ( - Active - ) : null} - - } - titleSize="s" - description={ - -

- Use this phase to define how long to retain your data. -

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} -
- } - fullWidth - > - {phaseData[PHASE_ENABLED] ? ( - - -
- - { - await setPhaseData(PHASE_ENABLED, false); - validate(); - }} - > - Deactive delete phase - -
- - - -

Configuration

-
- - - - - { - setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); - validate(); - }} - min={1} - /> - - - - - - setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value) - } - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' }, - ]} - /> - - - -
- ) : ( -
- - { - await setPhaseData(PHASE_ENABLED, true); - validate(); - }} - > - Activate delete phase - -
- )} -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js deleted file mode 100644 index 5f909ab2c0f79..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/delete_phase/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { DeletePhase } from './delete_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js deleted file mode 100644 index 1db810b49c19c..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.container.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { HotPhase as PresentationComponent } from './hot_phase'; -import { getPhase } from '../../../../../../store/selectors'; -import { setPhaseData } from '../../../../../../store/actions'; -import { PHASE_HOT } from '../../../../../../store/constants'; - -export const HotPhase = connect( - state => ({ - phaseData: getPhase(state, PHASE_HOT) - }), - { - setPhaseData: (key, value) => setPhaseData(PHASE_HOT, key, value) - }, -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js deleted file mode 100644 index 347b0f1d8117f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/hot_phase.js +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, PureComponent } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiText, - EuiTextColor, - EuiFieldNumber, - EuiSelect, - EuiSwitch, - EuiFormRow, - EuiDescribedFormGroup, - EuiBadge, -} from '@elastic/eui'; -import { LearnMoreLink } from '../../../../../../components/learn_more_link'; -import { - PHASE_ROLLOVER_ALIAS, - PHASE_ROLLOVER_MAX_AGE, - PHASE_ROLLOVER_MAX_AGE_UNITS, - PHASE_ROLLOVER_MAX_SIZE_STORED, - PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, - PHASE_ROLLOVER_ENABLED, - MAX_SIZE_TYPE_DOCUMENT -} from '../../../../../../store/constants'; - -import { ErrableFormRow } from '../../../../form_errors'; - -export class HotPhase extends PureComponent { - static propTypes = { - setPhaseData: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - - isShowingErrors: PropTypes.bool.isRequired, - errors: PropTypes.object.isRequired, - phaseData: PropTypes.shape({ - [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, - [PHASE_ROLLOVER_MAX_AGE]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired, - [PHASE_ROLLOVER_MAX_AGE_UNITS]: PropTypes.string.isRequired, - [PHASE_ROLLOVER_MAX_SIZE_STORED]: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired, - [PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS]: PropTypes.string.isRequired - }).isRequired - }; - - render() { - const { - validate, - setPhaseData, - - phaseData, - isShowingErrors, - errors, - } = this.props; - - return ( - - Hot phase{' '} - Active - - } - titleSize="s" - description={ - -

- This phase is required. A hot index is being queried and actively written to. - You can optimize this phase for write throughput. -

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} -
- } - fullWidth - > - - If true, rollover the index when it gets too big or too old. The alias switches to the new index.{' '} - -

- } - > - { - await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); - validate(); - }} - label="Enable rollover" - /> -
- {phaseData[PHASE_ROLLOVER_ENABLED] ? ( - - - - - - { - await setPhaseData( - PHASE_ROLLOVER_MAX_SIZE_STORED, - e.target.value - ); - validate(); - }} - min={1} - /> - - - - - { - await setPhaseData( - PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS, - e.target.value - ); - validate(); - }} - options={[ - { value: 'gb', text: 'gigabytes' }, - { value: MAX_SIZE_TYPE_DOCUMENT, text: 'documents' } - ]} - /> - - - - - - - - { - await setPhaseData(PHASE_ROLLOVER_MAX_AGE, e.target.value); - validate(); - }} - min={1} - /> - - - - - { - await setPhaseData( - PHASE_ROLLOVER_MAX_AGE_UNITS, - e.target.value - ); - validate(); - }} - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' }, - ]} - /> - - - - - ) : null} -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js deleted file mode 100644 index 114e34c3ef4d0..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/hot_phase/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { HotPhase } from './hot_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js deleted file mode 100644 index 7eb5def486c87..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { WarmPhase } from './warm_phase.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js deleted file mode 100644 index 95176ef3498ca..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.container.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { WarmPhase as PresentationComponent } from './warm_phase'; -import { - getNodeOptions, - getPhase, - getSelectedReplicaCount, - getSelectedPrimaryShardCount -} from '../../../../../../store/selectors'; -import { setPhaseData, fetchNodes } from '../../../../../../store/actions'; -import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../../../store/constants'; - -export const WarmPhase = connect( - state => ({ - phaseData: getPhase(state, PHASE_WARM), - hotPhaseReplicaCount: Number(getSelectedReplicaCount(state)), - hotPhasePrimaryShardCount: Number(getSelectedPrimaryShardCount(state)), - hotPhaseRolloverEnabled: getPhase(state, PHASE_HOT)[PHASE_ROLLOVER_ENABLED], - nodeOptions: getNodeOptions(state) - }), - { - setPhaseData: (key, value) => setPhaseData(PHASE_WARM, key, value), - fetchNodes - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js deleted file mode 100644 index 8685f0b86a22a..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/components/warm_phase/warm_phase.js +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiSpacer, - EuiText, - EuiTextColor, - EuiFormRow, - EuiFieldNumber, - EuiSelect, - EuiSwitch, - EuiButtonEmpty, - EuiDescribedFormGroup, - EuiBadge, - EuiButton, -} from '@elastic/eui'; -import { - PHASE_ENABLED, - WARM_PHASE_ON_ROLLOVER, - PHASE_ROLLOVER_ALIAS, - PHASE_FORCE_MERGE_ENABLED, - PHASE_FORCE_MERGE_SEGMENTS, - PHASE_NODE_ATTRS, - PHASE_PRIMARY_SHARD_COUNT, - PHASE_REPLICA_COUNT, - PHASE_ROLLOVER_MINIMUM_AGE, - PHASE_ROLLOVER_MINIMUM_AGE_UNITS, - PHASE_SHRINK_ENABLED, -} from '../../../../../../store/constants'; -import { ErrableFormRow } from '../../../../form_errors'; -import { LearnMoreLink } from '../../../../../../components/learn_more_link'; - -export class WarmPhase extends PureComponent { - static propTypes = { - setPhaseData: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - showNodeDetailsFlyout: PropTypes.func.isRequired, - - isShowingErrors: PropTypes.bool.isRequired, - errors: PropTypes.object.isRequired, - phaseData: PropTypes.shape({ - [PHASE_ENABLED]: PropTypes.bool.isRequired, - [WARM_PHASE_ON_ROLLOVER]: PropTypes.bool.isRequired, - [PHASE_ROLLOVER_ALIAS]: PropTypes.string.isRequired, - [PHASE_FORCE_MERGE_ENABLED]: PropTypes.bool.isRequired, - [PHASE_FORCE_MERGE_SEGMENTS]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) - .isRequired, - [PHASE_NODE_ATTRS]: PropTypes.string.isRequired, - [PHASE_PRIMARY_SHARD_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) - .isRequired, - [PHASE_REPLICA_COUNT]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, - }).isRequired, - - hotPhaseReplicaCount: PropTypes.number.isRequired, - hotPhasePrimaryShardCount: PropTypes.number.isRequired, - - nodeOptions: PropTypes.array.isRequired, - }; - - componentWillMount() { - this.props.fetchNodes(); - } - - render() { - const { - validate, - setPhaseData, - showNodeDetailsFlyout, - - phaseData, - hotPhaseReplicaCount, - hotPhasePrimaryShardCount, - nodeOptions, - errors, - isShowingErrors, - hotPhaseRolloverEnabled, - } = this.props; - - return ( - - Warm phase{' '} - {phaseData[PHASE_ENABLED] ? ( - Active - ) : null} - - } - titleSize="s" - description={ - -

- Your index becomes read-only when it enters the warm phase. You can optimize this - phase for search. -

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} -
- } - fullWidth - > - - {phaseData[PHASE_ENABLED] ? ( - - -
- { - await setPhaseData(PHASE_ENABLED, false); - validate(); - }} - > - Deactivate warm phase - -
-
- {hotPhaseRolloverEnabled ? ( - - { - await setPhaseData(WARM_PHASE_ON_ROLLOVER, e.target.checked); - validate(); - }} - /> - - ) : null} - {!phaseData[WARM_PHASE_ON_ROLLOVER] ? ( - - - - { - setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE, e.target.value); - validate(); - }} - min={1} - /> - - - - - { - await setPhaseData(PHASE_ROLLOVER_MINIMUM_AGE_UNITS, e.target.value); - validate(); - }} - options={[ - { value: 'd', text: 'days' }, - { value: 'h', text: 'hours' }, - ]} - /> - - - - ) : null} - - - - showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} - > - View node details - - ) : null - } - > - { - await setPhaseData(PHASE_NODE_ATTRS, e.target.value); - validate(); - }} - /> - - - - - - { - await setPhaseData(PHASE_REPLICA_COUNT, e.target.value); - validate(); - }} - min={0} - /> - - - - - { - await setPhaseData(PHASE_REPLICA_COUNT, hotPhaseReplicaCount); - validate(); - }} - > - Set to same as hot phase - - - - - - {hotPhasePrimaryShardCount > 1 ? ( - - - -

Shrink

-
- - - Shrink the index into a new index with fewer primary shards.{' '} - - - - - - - { - await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); - validate(); - }} - label="Shrink index" - /> - {phaseData[PHASE_SHRINK_ENABLED] ? ( - - - - - { - await setPhaseData(PHASE_PRIMARY_SHARD_COUNT, e.target.value); - validate(); - }} - min={1} - /> - - - - - { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - hotPhasePrimaryShardCount - ); - validate(); - }} - > - Set to same as hot phase - - - - - - - - ) : null} -
- ) : null} - - - - -

Force merge

-
- - - Reduce the number of segments in your shard by merging smaller files and clearing - deleted ones. - - - - - - { - await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); - validate(); - }} - /> - - - - {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( - - { - await setPhaseData(PHASE_FORCE_MERGE_SEGMENTS, e.target.value); - validate(); - }} - min={1} - /> - - ) : null} -
- ) : ( - -
- { - await setPhaseData(PHASE_ENABLED, true); - validate(); - }} - > - Activate warm phase - -
-
- )} -
-
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js deleted file mode 100644 index d8a885b0b32f3..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { PolicyConfiguration } from './policy_configuration.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js deleted file mode 100644 index 233e39776265a..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.container.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { connect } from 'react-redux'; -import { PolicyConfiguration as PresentationComponent } from './policy_configuration'; -import { - getSelectedPolicyName, - getAffectedIndexTemplates, - getSelectedIndexTemplateName, - getBootstrapEnabled, - getIndexName, - getAliasName, - getSelectedOriginalPolicyName, - getIsSelectedPolicySet, - getPolicies -} from '../../../../store/selectors'; -import { - setBootstrapEnabled, - setIndexName, - setAliasName, - setSelectedPolicyName, - unsetSelectedPolicy -} from '../../../../store/actions'; - -export const PolicyConfiguration = connect( - state => ({ - isSelectedPolicySet: getIsSelectedPolicySet(state), - selectedPolicyName: getSelectedPolicyName(state), - selectedIndexTemplateName: getSelectedIndexTemplateName(state), - affectedIndexTemplates: getAffectedIndexTemplates(state), - bootstrapEnabled: getBootstrapEnabled(state), - indexName: getIndexName(state), - aliasName: getAliasName(state), - originalPolicyName: getSelectedOriginalPolicyName(state), - hasExistingPolicies: getPolicies(state).length > 0 - }), - { - setBootstrapEnabled, - setIndexName, - setAliasName, - setSelectedPolicyName, - unsetSelectedPolicy - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js deleted file mode 100644 index df406b14c76d0..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_configuration/policy_configuration.js +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { toastNotifications } from 'ui/notify'; - -import { - EuiTitle, - EuiText, - EuiSpacer, - EuiHorizontalRule, - EuiButton, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; -import { HotPhase } from './components/hot_phase'; -import { WarmPhase } from './components/warm_phase'; -import { DeletePhase } from './components/delete_phase'; -import { ColdPhase } from './components/cold_phase'; -import { - PHASE_HOT, - PHASE_COLD, - PHASE_DELETE, - PHASE_WARM, -} from '../../../../store/constants'; -import { hasErrors } from '../../../../lib/find_errors'; -import { NodeAttrsDetails } from '../node_attrs_details'; -import { PolicySelection } from '../policy_selection/policy_selection.container'; - -export class PolicyConfiguration extends Component { - static propTypes = { - setSelectedPolicyName: PropTypes.func.isRequired, - setIndexName: PropTypes.func.isRequired, - setAliasName: PropTypes.func.isRequired, - setBootstrapEnabled: PropTypes.func.isRequired, - done: PropTypes.func.isRequired, - back: PropTypes.func.isRequired, - validate: PropTypes.func.isRequired, - selectedPolicyName: PropTypes.string.isRequired, - errors: PropTypes.object.isRequired, - bootstrapEnabled: PropTypes.bool.isRequired, - indexName: PropTypes.string.isRequired, - aliasName: PropTypes.string.isRequired, - originalPolicyName: PropTypes.string, - }; - - constructor(props) { - super(props); - this.state = { - isShowingErrors: false, - isShowingNodeDetailsFlyout: false, - selectedNodeAttrsForDetails: undefined, - }; - } - - validate = async () => { - await this.props.validate(); - const noErrors = !hasErrors(this.props.errors); - return noErrors; - }; - componentDidMount() { - window.scrollTo(0, 0); - } - submit = async () => { - this.setState({ isShowingErrors: true }); - if (await this.validate()) { - this.props.done(); - } else { - toastNotifications.addDanger('Please the fix errors on the page'); - } - }; - - showNodeDetailsFlyout = selectedNodeAttrsForDetails => { - this.setState({ isShowingNodeDetailsFlyout: true, selectedNodeAttrsForDetails }); - } - - render() { - const { - back, - - selectedPolicyName, - isSelectedPolicySet, - errors, - unsetSelectedPolicy, - hasExistingPolicies - } = this.props; - - const { isShowingErrors } = this.state; - - if (!isSelectedPolicySet) { - return ( - - ); - } - - return ( -
- - - - - -

- {!selectedPolicyName ? 'Create a lifecycle policy' : `Edit lifecycle policy ${selectedPolicyName}`} -

-
-
- {hasExistingPolicies ? ( - - Change lifecycle policy - - ) : null} -
- - -

Configure the phases of your data and when to transition between them.

-
- - - - - - - - - - - - Back - -    - - Continue - - - {this.state.isShowingNodeDetailsFlyout ? ( - this.setState({ isShowingNodeDetailsFlyout: false })} - /> - ) : null} -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js deleted file mode 100644 index 447bc384c368d..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { PolicySelection } from './policy_selection.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js deleted file mode 100644 index 59ce80fc1f0a4..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.container.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { PolicySelection as PresentationComponent } from './policy_selection'; -import { - getPolicies, getIsSelectedPolicySet, getSelectedPolicyName, -} from '../../../../store/selectors'; -import { - fetchPolicies, - setSelectedPolicy, -} from '../../../../store/actions'; - -export const PolicySelection = connect( - state => ({ - isSelectedPolicySet: getIsSelectedPolicySet(state), - policies: getPolicies(state), - selectedPolicyName: getSelectedPolicyName(state), - }), - { - fetchPolicies, - setSelectedPolicy, - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js deleted file mode 100644 index e153d9fd1ce03..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/policy_selection/policy_selection.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiButton, - EuiDescribedFormGroup, - EuiFormRow, - EuiSelect, -} from '@elastic/eui'; - -export class PolicySelection extends Component { - static propTypes = { - fetchPolicies: PropTypes.func.isRequired, - setSelectedPolicy: PropTypes.func.isRequired, - - selectedPolicyName: PropTypes.string.isRequired, - policies: PropTypes.array.isRequired, - }; - - componentDidMount() { - this.props.fetchPolicies(); - } - selectPolicy(policyName) { - const policy = this.props.policies.find(policy => policy.name === policyName); - this.props.setSelectedPolicy(policy); - } - - render() { - const { isSelectedPolicySet, policies, selectedPolicyName } = this.props; - const policiesExist = policies.length > 0; - if (isSelectedPolicySet) { - return null; - } - let existingPoliciesSelect; - if (policiesExist) { - const options = policies.map(item => ({ value: item.name, text: item.name })); - options.unshift({ value: '', text: 'Select an existing policy' }); - existingPoliciesSelect = ( - - - { - await this.selectPolicy(e.target.value); - }} - /> - - - ); - } - - return ( - Select or create a lifecycle policy} - titleSize="s" - description={`An index lifecycle policy is a - blueprint for transitioning your data over time. - You can create a new policy${policiesExist ? ' or edit an existing policy and save it with a new name.' : '.'}`} - fullWidth - > - - - {existingPoliciesSelect} - -

- OR -

-
- - this.selectPolicy()}>Create lifecycle policy - -
-
-
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js deleted file mode 100644 index a89f7e2412c59..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/diff_view.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiCodeEditor, - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, - EuiToolTip, - EuiCode, -} from '@elastic/eui'; -import ace from 'brace'; -import 'brace/mode/json'; -import { - mergeAndPreserveDuplicateKeys, - removePrefixes, -} from '../../../../lib/diff_tools'; -import { addDiffAddonsForAce, setCurrentJsonObject } from '../../../../lib/diff_ace_addons'; - -export class DiffView extends PureComponent { - static propTypes = { - templateDiff: PropTypes.shape({ - originalFullIndexTemplate: PropTypes.object.isRequired, - newFullIndexTemplate: PropTypes.object.isRequired, - }).isRequired, - }; - - scrollToKey = (key, value) => { - const editorDom = this.aceEditor.aceEditor.refEditor; - const editor = ace.edit(editorDom); - const escapedValue = value.replace(/\^/g, '\\^'); - const range = editor.find(new RegExp(`"${key}"\\s*:\\s*"*(${escapedValue})"*`), { regex: true }); - if (!range) { - return; - } - editor.gotoLine(range.start.row + 1, range.start.column); - } - - render() { - const { - templateDiff: { originalFullIndexTemplate, newFullIndexTemplate }, - } = this.props; - - const { result: mergedJson, changes } = mergeAndPreserveDuplicateKeys( - originalFullIndexTemplate, - newFullIndexTemplate - ); - - // Strip the ^ and $ characters - const mergedJsonAsString = removePrefixes( - JSON.stringify(mergedJson, null, 2) - ); - - setCurrentJsonObject(mergedJson); - addDiffAddonsForAce(); - - return ( - - -
    - {changes.map(({ key, original, updated }) => ( -
  • - - Changing the value of {key} from {JSON.stringify(original)} - to {JSON.stringify(updated)} - - ) : ( - Setting a value of {JSON.stringify(updated)} for {key} - )} - > - this.scrollToKey(key, updated)}> - {key} - - -
  • - ))} -
-
- - (this.aceEditor = aceEditor)} - mode="diff_json" - theme="github" - width="100%" - value={mergedJsonAsString} - setOptions={{ - useWorker: false, - readOnly: true, - }} - editorProps={{ - $blockScrolling: Infinity, - }} - /> - -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js deleted file mode 100644 index defebab0131ea..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { Review } from './review.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js deleted file mode 100644 index 044648dbbcabd..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.container.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import { Review as PresentationComponent } from './review'; -import { - getSelectedIndexTemplateName, - getAffectedIndexTemplates, - getTemplateDiff, - getLifecycle, - getSelectedPolicyName, - getSaveAsNewPolicy, - getSelectedOriginalPolicyName, - getAliasName, - getBootstrapEnabled, - getIndexName, - getPolicies, - getIsNewPolicy -} from '../../../../store/selectors'; -import { - setSelectedPolicyName, - setSaveAsNewPolicy, - setAliasName, - setIndexName, - setBootstrapEnabled, -} from '../../../../store/actions'; - -export const Review = connect( - state => ({ - selectedIndexTemplateName: getSelectedIndexTemplateName(state), - affectedIndexTemplates: getAffectedIndexTemplates(state), - policies: getPolicies(state), - templateDiff: getTemplateDiff(state), - lifecycle: getLifecycle(state), - bootstrapEnabled: getBootstrapEnabled(state), - aliasName: getAliasName(state), - selectedPolicyName: getSelectedPolicyName(state), - saveAsNewPolicy: getSaveAsNewPolicy(state), - originalPolicyName: getSelectedOriginalPolicyName(state), - isNewPolicy: getIsNewPolicy(state), - - /* start might go away */ - indexName: getIndexName(state), - /* end might go away */ - }), - { - setSelectedPolicyName, - setSaveAsNewPolicy, - setBootstrapEnabled, - setIndexName, - setAliasName, - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js deleted file mode 100644 index 20302c08de61f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.js +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, Component } from 'react'; -import PropTypes from 'prop-types'; -import { toastNotifications } from 'ui/notify'; - -import './review.less'; - -import 'brace/theme/github'; -import 'brace/mode/json'; -import 'brace/snippets/json'; -import 'brace/ext/language_tools'; - -import { - EuiTitle, - EuiCode, - EuiCallOut, - EuiSpacer, - EuiHorizontalRule, - EuiButton, - EuiFlexItem, - EuiText, - EuiButtonEmpty, - EuiFormRow, - EuiSwitch, - EuiFieldText, - EuiLoadingSpinner, - EuiFlexGroup, -} from '@elastic/eui'; -import { getAffectedIndices } from '../../../../api'; -import { DiffView } from './diff_view'; -import { ErrableFormRow } from '../../form_errors'; -import { STRUCTURE_POLICY_NAME } from '../../../../store/constants'; -import { hasErrors } from '../../../../lib/find_errors'; - -export class Review extends Component { - static propTypes = { - setSelectedPolicyName: PropTypes.func.isRequired, - setSaveAsNewPolicy: PropTypes.func.isRequired, - done: PropTypes.func.isRequired, - back: PropTypes.func.isRequired, - - selectedIndexTemplateName: PropTypes.string.isRequired, - affectedIndexTemplates: PropTypes.array.isRequired, - templateDiff: PropTypes.object.isRequired, - lifecycle: PropTypes.object.isRequired, - selectedPolicyName: PropTypes.string.isRequired, - saveAsNewPolicy: PropTypes.bool.isRequired, - originalPolicyName: PropTypes.string, - bootstrapEnabled: PropTypes.bool.isRequired, - aliasName: PropTypes.string.isRequired, - }; - - constructor(props) { - super(props); - this.state = { - selectedTab: 1, - affectedIndices: [], - isLoadingAffectedIndices: false, - }; - - this.currentFetchTimeout = null; - } - componentDidMount() { - window.scrollTo(0, 0); - } - fetchAffectedIndices = () => { - if (this.currentFetchTimeout) { - clearTimeout(this.currentFetchTimeout); - } - - this.setState({ isLoadingAffectedIndices: true }); - this.currentFetchTimeout = setTimeout(async () => { - const affectedIndices = await getAffectedIndices( - this.props.selectedIndexTemplateName, - this.props.selectedPolicyName - ); - this.setState({ affectedIndices, isLoadingAffectedIndices: false }); - }, 1000); - } - - async componentWillMount() { - this.fetchAffectedIndices(); - } - - async componentWillReceiveProps(nextProps) { - if (nextProps.selectedPolicyName !== this.props.selectedPolicyName) { - this.fetchAffectedIndices(); - } - } - - validate = async () => { - await this.props.validate(); - const noErrors = !hasErrors(this.props.errors); - return noErrors; - }; - - submit = async () => { - this.setState({ isShowingErrors: true }); - if (await this.validate()) { - this.props.done(); - } else { - toastNotifications.addDanger('Please fix the errors on the page'); - } - }; - - render() { - const { - done, - back, - setSelectedPolicyName, - setSaveAsNewPolicy, - validate, - - errors, - selectedPolicyName, - saveAsNewPolicy, - originalPolicyName, - selectedIndexTemplateName, - affectedIndexTemplates, - templateDiff, - lifecycle, - bootstrapEnabled, - aliasName, - policies, - isNewPolicy - } = this.props; - - const { affectedIndices, isLoadingAffectedIndices, isShowingErrors } = this.state; - - const showSaveChangedMessage = (originalPolicyName && !saveAsNewPolicy) - || (saveAsNewPolicy && !!policies.find(policy => policy.name === selectedPolicyName)); - - return ( -
- - -

Review your policy changes

-
- -

When you save a policy, your changes go into effect immediately.

-
- - - - -

{`${affectedIndexTemplates.length} Index ${affectedIndexTemplates.length === 1 ? 'template' : 'templates'}`}

-
    - {affectedIndexTemplates.map(template => ( -
  • {template}
  • - ))} -
-

{`${affectedIndices.length} ${affectedIndices.length === 1 ? 'Index' : 'Indices' }`}

- { isLoadingAffectedIndices ? ( - - ) : ( -
    - {affectedIndices.map(index =>
  • {index}
  • )} -
- ) } -
- - {bootstrapEnabled ? ( - - - -

You decided to bootstrap a new index. Point to this new alias going forward.

-

{aliasName} is your new alias

-
-
- ) : null} - - - {templateDiff.hasChanged ? ( - - -

- {selectedIndexTemplateName} index template changes -

-
- - - -
- ) : null} - - - - {originalPolicyName ? ( - - { showSaveChangedMessage ? ( - - -

Save changes to {selectedPolicyName} policy?

-
- -

- You are editing an existing policy. Any changes you make - will also change index templates that this policy is attached to. Alternately, you can save - these changes in a new policy and only change the index template you - selected. -

-
- -
- ) : null } - { isNewPolicy ? null : ( - - { - await setSaveAsNewPolicy(e.target.checked); - validate(); - }} - label={ - - Save this as a new policy - - } - /> - - )} - -
- ) : null} - {saveAsNewPolicy || isNewPolicy ? ( - - -

Save your work

-
- - - { - await setSelectedPolicyName(e.target.value); - validate(); - }} - /> - -
- ) : null} -
- - - - - Back - - - - - done(lifecycle)} - disabled={!selectedPolicyName} - > - Looks good, save changes - - - -    -
- ); - } -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less deleted file mode 100644 index e826e78a7bf7e..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/components/review/review.less +++ /dev/null @@ -1,7 +0,0 @@ -.ace_diff_addition { - background-color: #e6ffed; -} - -.ace_diff_removal { - background-color: #ffeef0; -} diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js deleted file mode 100644 index fc3c29c4beb0c..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/form_errors.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import React, { cloneElement, Children, Fragment } from 'react'; -import { EuiFormRow } from '@elastic/eui'; - -export const ErrableFormRow = ({ - errorKey, - isShowingErrors, - errors, - children, - ...rest -}) => { - return ( - 0 - } - error={errors[errorKey]} - {...rest} - > - - {Children.map(children, child => cloneElement(child, { - isInvalid: isShowingErrors && errors[errorKey].length > 0, - }))} - - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js deleted file mode 100644 index 3a6f61a7c0492..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { Wizard } from './wizard.container'; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js deleted file mode 100644 index 84c94f2c82387..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.container.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import { connect } from 'react-redux'; -import '../../less/main.less'; -import { Wizard as PresentationComponent } from './wizard'; -import { saveLifecycle, fetchIndexTemplates } from '../../store/actions'; - -import { - getIndexTemplates, - getIndexTemplatePatch, - getBootstrapEnabled, - getIndexName, - getAliasName, - validateLifecycle, -} from '../../store/selectors'; - -export const Wizard = connect( - state => ({ - indexTemplatePatch: getIndexTemplatePatch(state), - bootstrapEnabled: getBootstrapEnabled(state), - indexName: getIndexName(state), - aliasName: getAliasName(state), - validateLifecycle: () => validateLifecycle(state), - indexTemplates: getIndexTemplates(state), - }), - { - fetchIndexTemplates, - saveLifecycle - } -)(PresentationComponent); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js b/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js deleted file mode 100644 index 8d98a13616b99..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/sections/wizard/wizard.js +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { toastNotifications } from 'ui/notify'; -import { IndexTemplate } from './components/index_template'; -import { PolicyConfiguration } from './components/policy_configuration'; -import { Review } from './components/review'; -import { goToPolicyList } from '../../services/navigation'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiTitle, - EuiSpacer, - EuiStepsHorizontal, -} from '@elastic/eui'; -import { bootstrap } from '../../api'; -import { - STRUCTURE_INDEX_TEMPLATE, - STRUCTURE_POLICY_CONFIGURATION, - STRUCTURE_REVIEW, -} from '../../store/constants'; - -const STEP_INDEX_TEMPLATE = 1; -const STEP_POLICY_CONFIGURATION = 2; -const STEP_REVIEW = 3; -export class Wizard extends Component { - static propTypes = { - saveLifecycle: PropTypes.func.isRequired, - validateLifecycle: PropTypes.func.isRequired, - - indexTemplatePatch: PropTypes.object.isRequired, - bootstrapEnabled: PropTypes.bool.isRequired, - indexName: PropTypes.string.isRequired, - aliasName: PropTypes.string.isRequired, - fetchIndexTemplates: PropTypes.func.isRequired, - indexTemplates: PropTypes.array, - }; - - constructor(props) { - super(props); - - this.state = { - selectedStep: STEP_INDEX_TEMPLATE, - errors: this.getErrors(), - }; - } - componentDidMount() { - this.props.fetchIndexTemplates(); - } - onSelectedStepChanged = selectedStep => { - this.setState({ - selectedStep, - }); - }; - - getErrors = () => { - return this.props.validateLifecycle(); - }; - - validate = () => { - const errors = this.getErrors(); - this.setState({ errors }); - }; - - addLifecycle = async lifecycle => { - const lifecycleSuccess = await this.props.saveLifecycle(lifecycle, this.props.indexTemplatePatch); - if (!lifecycleSuccess) { - return; - } - const bootstrapSuccess = await this.bootstrap(); - if (bootstrapSuccess) { - goToPolicyList(); - } - }; - - bootstrap = async () => { - const { indexName, aliasName, bootstrapEnabled } = this.props; - if (!bootstrapEnabled) { - return true; - } - - const bootstrapSuccess = await bootstrap(indexName, aliasName); - if (bootstrapSuccess) { - toastNotifications.addSuccess( - 'Successfully bootstrapped an index and alias' - ); - // Bounce back to management - // this.onSelectedStepChanged(1); - // TODO: also clear state? - } else { - toastNotifications.addDanger('Unable to bootstrap an index and alias'); - } - return bootstrapSuccess; - }; - - renderContent() { - const { selectedStep, errors } = this.state; - - switch (selectedStep) { - case STEP_INDEX_TEMPLATE: - return ( - this.onSelectedStepChanged(selectedStep + 1)} - /> - ); - case STEP_POLICY_CONFIGURATION: - return ( - this.onSelectedStepChanged(selectedStep + 1)} - back={() => this.onSelectedStepChanged(selectedStep - 1)} - /> - ); - case STEP_REVIEW: - return ( - this.onSelectedStepChanged(selectedStep - 1)} - /> - ); - } - } - createStep(title, stepIndex) { - return { - title, - isSelected: this.state.selectedStep === stepIndex, - isComplete: this.state.selectedStep > stepIndex, - onClick: () => this.onSelectedStepChanged(stepIndex), - }; - } - render() { - const { indexTemplates } = this.props; - if (indexTemplates === null) { - // Loading... - return null; - } - - if (indexTemplates.length === 0) { - return ( -

No index templates found.

- ); - } - const steps = [ - this.createStep('Select an index template', STEP_INDEX_TEMPLATE), - this.createStep('Configure a lifecycle policy', STEP_POLICY_CONFIGURATION), - this.createStep('Review and save', STEP_REVIEW), - ]; - - return ( - - - - -

Index lifecycle management

-
- - - - - {this.renderContent()} -
-
-
- ); - } -} From 717bda897b299d808f3570bad1bd8cbd0114959b Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 25 Oct 2018 15:48:49 -0400 Subject: [PATCH 076/102] fixing issue with edit policy --- .../sections/edit_policy/edit_policy.container.js | 2 ++ .../public/sections/edit_policy/edit_policy.js | 7 ++++--- .../public/store/selectors/lifecycle.js | 13 +++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js index 2f3ab4a14b37a..a57280ed62dba 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.container.js @@ -14,6 +14,7 @@ import { getLifecycle, getPolicies, isPolicyListLoaded, + getIsNewPolicy } from '../../store/selectors'; import { setSelectedPolicy, @@ -32,6 +33,7 @@ export const EditPolicy = connect( lifecycle: getLifecycle(state), policies: getPolicies(state), isPolicyListLoaded: isPolicyListLoaded(state), + isNewPolicy: getIsNewPolicy(state), }), { setSelectedPolicy, diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js index 5cf7026f7bd55..4686efb39928d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js @@ -115,6 +115,7 @@ export class EditPolicy extends Component { setSaveAsNewPolicy, saveAsNewPolicy, setSelectedPolicyName, + isNewPolicy, } = this.props; const selectedPolicyName = selectedPolicy.name; const { isShowingErrors } = this.state; @@ -129,9 +130,9 @@ export class EditPolicy extends Component { >

- {!selectedPolicyName - ? 'Create a lifecycle policy' - : `Edit lifecycle policy ${selectedPolicyName}`} + {isNewPolicy + ? 'Create an index lifecycle policy' + : `Edit index lifecycle policy ${selectedPolicyName}`}

diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index cfd3d389a5628..a6c24a2d90596 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -30,6 +30,7 @@ import { getSelectedPrimaryShardCount, getSaveAsNewPolicy, getSelectedOriginalPolicyName, + getPolicies } from '.'; export const validatePhase = (type, phase, errors) => { @@ -106,12 +107,12 @@ export const validateLifecycle = state => { errors[STRUCTURE_POLICY_NAME].push('The policy name must be different'); } - // if (getSaveAsNewPolicy(state)) { - // const policyNames = getAllPolicyNamesFromTemplates(state); - // if (policyNames.includes(getSelectedPolicyName(state))) { - // errors[STRUCTURE_POLICY_NAME].push('That policy name is already used.'); - // } - // } + if (getSaveAsNewPolicy(state)) { + const policyNames = getPolicies(state).map(policy => policy.name); + if (policyNames.includes(getSelectedPolicyName(state))) { + errors[STRUCTURE_POLICY_NAME].push('That policy name is already used.'); + } + } const hotPhase = getPhase(state, PHASE_HOT); const warmPhase = getPhase(state, PHASE_WARM); From b34c2bd327fb25dec726de152b53512aed310f83 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 26 Oct 2018 09:41:56 -0400 Subject: [PATCH 077/102] fixing issue with closing delete confirmation modal --- .../policy_table/components/policy_table/confirm_delete.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js index 9b3e250d6f958..d3ad96e8b1b8c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js @@ -35,7 +35,7 @@ export class ConfirmDelete extends Component { cancelButtonText="Cancel" confirmButtonText="Delete" buttonColor="danger" - onClose={() => {}} + onClose={onCancel} >

This operation cannot be undone.

From 257401b61ef1ce5b3720066199505a87bf8a3c24 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 30 Oct 2018 08:17:12 -0400 Subject: [PATCH 078/102] making max age and max size not mutually exclusive --- .../public/store/selectors/policies.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 9cf603c071faf..6f87e542dd9f1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -236,7 +236,8 @@ export const phaseToES = (state, phase) => { esPhase.actions.rollover.max_age = `${phase[PHASE_ROLLOVER_MAX_AGE]}${ phase[PHASE_ROLLOVER_MAX_AGE_UNITS] }`; - } else if (isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED])) { + } + if (isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED])) { if (phase[PHASE_ROLLOVER_MAX_SIZE_STORED_UNITS] === MAX_SIZE_TYPE_DOCUMENT) { esPhase.actions.rollover.max_docs = phase[PHASE_ROLLOVER_MAX_SIZE_STORED]; } else { @@ -246,7 +247,6 @@ export const phaseToES = (state, phase) => { } } } - if (phase[PHASE_NODE_ATTRS]) { esPhase.actions.allocate = { include: {}, // TODO: this seems to be a constant, confirm? From 3c180940434d901f250bc6b0a4f91a45e286631a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 30 Oct 2018 08:22:06 -0400 Subject: [PATCH 079/102] removing names of covered indices from policy table --- .../policy_table/components/policy_table/policy_table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js index 1ed6d7ab44863..658a4710d885f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/policy_table.js @@ -124,7 +124,7 @@ export class PolicyTableUi extends Component { } else if (fieldName === 'coveredIndices' && value) { return ( - {value.length} ({value.join(', ')}) + {value.length} ); } else if (fieldName === 'modified_date' && value) { From b14de85bf571a450b2ee3de18b88f0c4cadb964c Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 31 Oct 2018 10:23:42 -0400 Subject: [PATCH 080/102] changing minimum_age to min_age --- .../public/store/selectors/lifecycle.js | 2 +- .../public/store/selectors/policies.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index a6c24a2d90596..f70452ef15873 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -149,7 +149,7 @@ export const getLifecycle = state => { // These seem to be constants // TODO: verify this assumption if (phaseName === PHASE_HOT) { - accum[phaseName].minimum_age = '0s'; + accum[phaseName].min_age = '0s'; } if (phaseName === PHASE_DELETE) { diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 6f87e542dd9f1..d41ef602b8430 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -136,12 +136,12 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { policy[PHASE_ENABLED] = true; policy[PHASE_ROLLOVER_ENABLED] = false; - if (phase.minimum_age) { - if (phaseName === PHASE_WARM && phase.minimum_age === '0ms') { + if (phase.min_age) { + if (phaseName === PHASE_WARM && phase.min_age === '0ms') { policy[WARM_PHASE_ON_ROLLOVER] = true; } else { const { size: minAge, units: minAgeUnits } = splitSizeAndUnits( - phase.minimum_age + phase.min_age ); policy[PHASE_ROLLOVER_MINIMUM_AGE] = minAge; policy[PHASE_ROLLOVER_MINIMUM_AGE_UNITS] = minAgeUnits; @@ -224,7 +224,7 @@ export const phaseToES = (state, phase) => { } if (isNumber(phase[PHASE_ROLLOVER_MINIMUM_AGE])) { - esPhase.minimum_age = `${phase[PHASE_ROLLOVER_MINIMUM_AGE]}${phase[PHASE_ROLLOVER_MINIMUM_AGE_UNITS]}`; + esPhase.min_age = `${phase[PHASE_ROLLOVER_MINIMUM_AGE]}${phase[PHASE_ROLLOVER_MINIMUM_AGE_UNITS]}`; } esPhase.actions = {}; From c71dd61eca243ba6f9c0e7ae98a7c8fa11e9bb35 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 1 Nov 2018 21:35:57 -0400 Subject: [PATCH 081/102] first pass at index lifecycle extensions --- .../index_lifecycle_management/index.js | 21 +++-- .../index_lifecycle_data.js | 22 +++++ .../components/index_lifecycle_summary.js | 94 +++++++++++++++++++ .../public/index.js | 1 + .../register_index_lifecycle_summary.js | 13 +++ .../lib/error_wrappers/wrap_es_error.js | 2 +- .../license_pre_routing_factory.js | 7 +- .../api/indices/register_bootstrap_route.js | 8 +- .../indices/register_get_affected_route.js | 16 ++-- .../api/lifecycle/register_create_route.js | 10 +- .../api/nodes/register_details_route.js | 8 +- .../routes/api/nodes/register_list_route.js | 8 +- .../api/policies/register_delete_route.js | 11 +-- .../api/policies/register_fetch_route.js | 10 +- .../api/templates/register_fetch_route.js | 8 +- .../api/templates/register_get_route.js | 8 +- x-pack/plugins/index_management/index.js | 3 +- .../index_management/index_management_data.js | 13 +++ .../plugins/index_management/public/index.js | 1 + .../public/index_management_extensions.js | 21 +++++ .../detail_panel/summary/summary.js | 58 ++++++++++-- .../routes/api/indices/register_list_route.js | 9 +- .../api/indices/register_reload_route.js | 8 +- 23 files changed, 290 insertions(+), 70 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/index_lifecycle_data.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js create mode 100644 x-pack/plugins/index_management/index_management_data.js create mode 100644 x-pack/plugins/index_management/public/index_management_extensions.js diff --git a/x-pack/plugins/index_lifecycle_management/index.js b/x-pack/plugins/index_lifecycle_management/index.js index 2824f8e345d50..f5bc1e4745852 100644 --- a/x-pack/plugins/index_lifecycle_management/index.js +++ b/x-pack/plugins/index_lifecycle_management/index.js @@ -4,9 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - - import { resolve } from 'path'; import { registerTemplatesRoutes } from './server/routes/api/templates'; import { registerNodesRoutes } from './server/routes/api/nodes'; @@ -15,16 +12,14 @@ import { registerLifecycleRoutes } from './server/routes/api/lifecycle'; import { registerIndicesRoutes } from './server/routes/api/indices'; import { registerLicenseChecker } from './server/lib/register_license_checker'; import { PLUGIN } from './common/constants'; - -export function indexLifecycleManagement(kibana) { +import { indexLifecycleDataEnricher } from './index_lifecycle_data'; +export function indexLifecycleManagement(kibana) { return new kibana.Plugin({ id: PLUGIN.ID, publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch', 'xpack_main'], + require: ['kibana', 'elasticsearch', 'xpack_main', 'index_management'], uiExports: { - managementSections: [ - 'plugins/index_lifecycle_management', - ] + managementSections: ['plugins/index_lifecycle_management'], }, init: function (server) { registerLicenseChecker(server); @@ -33,6 +28,12 @@ export function indexLifecycleManagement(kibana) { registerPoliciesRoutes(server); registerLifecycleRoutes(server); registerIndicesRoutes(server); - } + if ( + server.plugins.index_management && + server.plugins.index_management.addIndexManagementDataEnricher + ) { + server.plugins.index_management.addIndexManagementDataEnricher(indexLifecycleDataEnricher); + } + }, }); } diff --git a/x-pack/plugins/index_lifecycle_management/index_lifecycle_data.js b/x-pack/plugins/index_lifecycle_management/index_lifecycle_data.js new file mode 100644 index 0000000000000..57b90204ce224 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/index_lifecycle_data.js @@ -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. + */ + +export const indexLifecycleDataEnricher = async (indicesList, callWithRequest) => { + if (!indicesList || !indicesList.length) { + return; + } + const params = { + path: '/*/_ilm/explain', + method: 'GET', + }; + const { indices: ilmIndicesData } = await callWithRequest('transport.request', params); + return indicesList.map(index => { + return { + ...index, + ilm: { ...(ilmIndicesData[index.name] || {}) }, + }; + }); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js new file mode 100644 index 0000000000000..a64efb148c0de --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js @@ -0,0 +1,94 @@ +/* + * 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, { Component, Fragment } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiSpacer, + EuiTitle +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +const HEADERS = { + policy: i18n.translate('xpack.indexLifecycleMgmt.summary.headers.lifecyclePolicyHeader', { + defaultMessage: 'Lifecycle policy', + }), + phase: i18n.translate('xpack.indexLifecycleMgmt.summary.headers.currentPhaseHeader', { + defaultMessage: 'Current phase', + }), + action: i18n.translate('xpack.idxMgmt.indexLifecycleMgmtSummary.headers.nextActionHeader', { + defaultMessage: 'Next action', + }), + action_time: i18n.translate('xpack.idxMgmt.indexLifecycleMgmtSummary.headers.nextActionTimeHeader', { + defaultMessage: 'Next action time', + }), + failed_step: i18n.translate('xpack.idxMgmt.indexLifecycleMgmtSummary.headers.failedStepHeader', { + defaultMessage: 'Failed step', + }), + step_info: i18n.translate('xpack.idxMgmt.indexLifecycleMgmtSummary.headers.errorInfoHeader', { + defaultMessage: 'Error info', + }), +}; +export class IndexLifecycleSummary extends Component { + + buildRows() { + const { index: { ilm = {} } } = this.props; + const rows = { + left: [], + right: [] + }; + Object.keys(HEADERS).forEach((fieldName, arrayIndex) => { + const value = ilm[fieldName]; + const content = fieldName === 'step_info' ? ( + `${value.type}: ${value.reason}` + ) : value; + const cell = [ + + {HEADERS[fieldName]}: + , + + {content} + + ]; + if (arrayIndex % 2 === 0) { + rows.left.push(cell); + } else { + rows.right.push(cell); + } + }); + return rows; + } + + render() { + const { index: { ilm = {} } } = this.props; + if (!ilm.managed) { + return null; + } + const { left, right } = this.buildRows(); + return ( + +

Index lifecycle management

+ + + + + {left} + + + + + {right} + + + +
+ ); + } +} \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/index.js b/x-pack/plugins/index_lifecycle_management/public/index.js index ccde49edbdf5d..235137923b819 100644 --- a/x-pack/plugins/index_lifecycle_management/public/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/index.js @@ -6,3 +6,4 @@ import './register_management_section'; import './register_routes'; +import './register_index_lifecycle_summary'; diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js b/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js new file mode 100644 index 0000000000000..cd5a262b1635a --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js @@ -0,0 +1,13 @@ +/* + * 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 { addSummaryExtension } from '../../index_management/public/index_management_extensions'; +import { IndexLifecycleSummary } from './components/index_lifecycle_summary'; +addSummaryExtension((index) => { + return ; +}); + diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js index 6ac4d50c7e0fe..9f1328219bab2 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js @@ -20,7 +20,7 @@ export function wrapEsError(err, statusCodeToMessageMap = {}) { // If no custom message if specified for the error's status code, just // wrap the error as a Boom error response and return it if (!statusCodeToMessageMap[statusCode]) { - return Boom.wrap(err, err.statusCode); + return Boom.boomify(err, err.statusCode); } // Otherwise, use the custom message to create a Boom error response and diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js index b3720ab265393..abfc7fc24dcc3 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js @@ -12,16 +12,15 @@ export const licensePreRoutingFactory = once((server) => { const xpackMainPlugin = server.plugins.xpack_main; // License checking and enable/disable logic - function licensePreRouting(request, reply) { + function licensePreRouting() { const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); if (!licenseCheckResults.isAvailable) { const error = new Error(licenseCheckResults.message); const statusCode = 403; const wrappedError = wrapCustomError(error, statusCode); - reply(wrappedError); - } else { - reply(); + return wrappedError; } + return null; } return licensePreRouting; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js index dcd053e096c52..ad449101cb4f5 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js @@ -29,18 +29,18 @@ export function registerBootstrapRoute(server) { server.route({ path: '/api/index_lifecycle_management/indices/bootstrap', method: 'POST', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { const response = await bootstrap(callWithRequest, request.payload); - reply(response); + return response; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js index 8f60bffb94b5a..ed5b6b09db16b 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js @@ -72,7 +72,7 @@ export function registerGetAffectedRoute(server) { path: '/api/index_lifecycle_management/indices/affected/{indexTemplateName}', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { @@ -80,13 +80,13 @@ export function registerGetAffectedRoute(server) { callWithRequest, request.params.indexTemplateName, ); - reply(response); + return response; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { @@ -98,7 +98,7 @@ export function registerGetAffectedRoute(server) { path: '/api/index_lifecycle_management/indices/affected/{indexTemplateName}/{policyName}', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { @@ -107,13 +107,13 @@ export function registerGetAffectedRoute(server) { request.params.indexTemplateName, request.params.policyName ); - reply(response); + return response; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js index ca1a021c6100a..8aa93e3540243 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/lifecycle/register_create_route.js @@ -20,7 +20,7 @@ async function createLifecycle(callWithRequest, lifecycle) { }; const params = { method: 'PUT', - path: `/_ilm/${lifecycle.name}`, + path: `/_ilm/policy/${lifecycle.name}`, ignore: [ 404 ], body, }; @@ -35,18 +35,18 @@ export function registerCreateRoute(server) { server.route({ path: '/api/index_lifecycle_management/lifecycle', method: 'POST', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { const response = await createLifecycle(callWithRequest, request.payload.lifecycle); - reply(response); + return response; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js index 6cd882caf73a0..9547e66c73dc3 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.js @@ -43,19 +43,19 @@ export function registerDetailsRoute(server) { server.route({ path: '/api/index_lifecycle_management/nodes/{nodeAttrs}/details', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { const stats = await fetchNodeStats(callWithRequest); const response = findMatchingNodes(stats, request.params.nodeAttrs); - reply(response); + return response; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js index 8e92e6570edcf..40525b45f5566 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.js @@ -42,19 +42,19 @@ export function registerListRoute(server) { server.route({ path: '/api/index_lifecycle_management/nodes/list', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { const stats = await fetchNodeStats(callWithRequest); const response = convertStatsIntoList(stats); - reply(response); + return response; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js index bd69b3b93531c..1e06bf99f6db4 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.js @@ -9,11 +9,10 @@ import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; - async function deletePolicies(policyNames, callWithRequest) { const params = { method: 'DELETE', - path: `/_ilm/${policyNames}`, + path: `/_ilm/policy/${policyNames}`, // we allow 404 since they may have no policies ignore: [ 404 ] }; @@ -28,17 +27,17 @@ export function registerDeleteRoute(server) { server.route({ path: '/api/index_lifecycle_management/policies/{policyNames}', method: 'DELETE', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); const { policyNames } = request.params; try { await deletePolicies(policyNames, callWithRequest); - reply(); + return; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js index 22387ec11dde3..df2c0096cddff 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.js @@ -26,7 +26,7 @@ function formatPolicies(policiesMap) { async function fetchPolicies(callWithRequest) { const params = { method: 'GET', - path: '/_ilm', + path: '/_ilm/policy', // we allow 404 since they may have no policies ignore: [ 404 ] }; @@ -60,7 +60,7 @@ export function registerFetchRoute(server) { server.route({ path: '/api/index_lifecycle_management/policies', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); const { withIndices } = request.query; try { @@ -68,13 +68,13 @@ export function registerFetchRoute(server) { if (withIndices) { await addCoveredIndices(policiesMap, callWithRequest); } - reply(formatPolicies(policiesMap)); + return formatPolicies(policiesMap); } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js index 1a4c5529b8da8..d7fd8c83a2c98 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.js @@ -61,19 +61,19 @@ export function registerFetchRoute(server) { server.route({ path: '/api/index_lifecycle_management/templates', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); try { const hits = await fetchTemplates(callWithRequest); const templates = await formatTemplates(hits, callWithRequest); - reply(templates); + return templates; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js index 690960e953c8c..ad24160fe798f 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_get_route.js @@ -30,19 +30,19 @@ export function registerGetRoute(server) { server.route({ path: '/api/index_lifecycle_management/template/{templateName}', method: 'GET', - handler: async (request, reply) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); const templateName = request.params.templateName; try { const template = await fetchTemplate(callWithRequest, templateName); - reply(template[templateName]); + return template[templateName]; } catch (err) { if (isEsError(err)) { - return reply(wrapEsError(err)); + return wrapEsError(err); } - reply(wrapUnknownError(err)); + return wrapUnknownError(err); } }, config: { diff --git a/x-pack/plugins/index_management/index.js b/x-pack/plugins/index_management/index.js index 4408428364b83..c48eaa6ba15ca 100644 --- a/x-pack/plugins/index_management/index.js +++ b/x-pack/plugins/index_management/index.js @@ -11,7 +11,7 @@ import { registerSettingsRoutes } from './server/routes/api/settings'; import { registerStatsRoute } from './server/routes/api/stats'; import { registerLicenseChecker } from './server/lib/register_license_checker'; import { PLUGIN } from './common/constants'; - +import { addIndexManagementDataEnricher } from "./index_management_data"; export function indexManagement(kibana) { return new kibana.Plugin({ id: PLUGIN.ID, @@ -24,6 +24,7 @@ export function indexManagement(kibana) { ] }, init: function (server) { + server.expose('addIndexManagementDataEnricher', addIndexManagementDataEnricher); registerLicenseChecker(server); registerIndicesRoutes(server); registerSettingsRoutes(server); diff --git a/x-pack/plugins/index_management/index_management_data.js b/x-pack/plugins/index_management/index_management_data.js new file mode 100644 index 0000000000000..022ab9b6da5d4 --- /dev/null +++ b/x-pack/plugins/index_management/index_management_data.js @@ -0,0 +1,13 @@ +/* + * 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. + */ + +const indexManagementDataEnrichers = []; +export const addIndexManagementDataEnricher = (provider) => { + indexManagementDataEnrichers.push(provider); +}; +export const getIndexManagementDataEnrichers = () => { + return indexManagementDataEnrichers; +}; \ No newline at end of file diff --git a/x-pack/plugins/index_management/public/index.js b/x-pack/plugins/index_management/public/index.js index ccde49edbdf5d..d52bf02b82f65 100644 --- a/x-pack/plugins/index_management/public/index.js +++ b/x-pack/plugins/index_management/public/index.js @@ -6,3 +6,4 @@ import './register_management_section'; import './register_routes'; +import './index_management_extensions'; diff --git a/x-pack/plugins/index_management/public/index_management_extensions.js b/x-pack/plugins/index_management/public/index_management_extensions.js new file mode 100644 index 0000000000000..eb14abd7681bd --- /dev/null +++ b/x-pack/plugins/index_management/public/index_management_extensions.js @@ -0,0 +1,21 @@ +/* + * 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. + */ + +const summaryExtensions = []; +export const addSummaryExtension = (summaryExtension)=> { + summaryExtensions.push(summaryExtension); +}; +export const getSummaryExtensions = () => { + return summaryExtensions; +}; +const actionExtensions = []; +export const addActionExtension = (actionExtension)=> { + actionExtensions.push(actionExtension); +}; +export const getActionExtensions = () => { + return actionExtensions; +}; + diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js b/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js index 7346ab6650f65..b0cccd21fe829 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js @@ -4,16 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { healthToColor } from '../../../../../services'; import { + EuiFlexGroup, + EuiFlexItem, EuiHealth, EuiDescriptionList, + EuiHorizontalRule, EuiDescriptionListTitle, EuiDescriptionListDescription, + EuiSpacer, + EuiTitle } from '@elastic/eui'; - +import { getSummaryExtensions } from '../../../../../index_management_extensions'; const HEADERS = { health: i18n.translate('xpack.idxMgmt.summary.headers.healthHeader', { defaultMessage: 'Health', @@ -42,15 +47,32 @@ const HEADERS = { }; export class Summary extends React.PureComponent { + getAdditionalContent() { + const { index } = this.props; + const extensions = getSummaryExtensions(); + console.log(extensions); + return extensions.map((summaryExtension) => { + return ( + + + { summaryExtension(index) } + + ); + }); + } buildRows() { const { index } = this.props; - return Object.keys(HEADERS).map(fieldName => { + const rows = { + left: [], + right: [] + }; + Object.keys(HEADERS).forEach((fieldName, arrayIndex) => { const value = index[fieldName]; const content = fieldName === "health" ? ( {value} ) : value; - return [ + const cell = [ {HEADERS[fieldName]}: , @@ -58,14 +80,36 @@ export class Summary extends React.PureComponent { {content} ]; + if (arrayIndex % 2 === 0) { + rows.left.push(cell); + } else { + rows.right.push(cell); + } }); + return rows; } render() { + const { left, right } = this.buildRows(); + const additionalContent = this.getAdditionalContent(); return ( - - {this.buildRows()} - + +

General

+ + + + + {left} + + + + + {right} + + + + { additionalContent } +
); } } diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js index af2a03f5bc4aa..879424d9e7948 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js @@ -8,7 +8,7 @@ import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; - +import { getIndexManagementDataEnrichers } from '../../../../index_management_data'; function formatHits(hits) { return hits.map(hit => { return { @@ -46,7 +46,12 @@ export function registerListRoute(server) { try { const hits = await fetchIndices(callWithRequest); - const response = formatHits(hits); + let response = formatHits(hits); + const dataEnrichers = getIndexManagementDataEnrichers(); + for (let i = 0; i < dataEnrichers.length; i++) { + const dataEnricher = dataEnrichers[i]; + response = await dataEnricher(response, callWithRequest); + } return response; } catch (err) { if (isEsError(err)) { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.js b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.js index 8aa486be06f37..98916f40e6924 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.js +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.js @@ -8,6 +8,7 @@ import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { getIndexManagementDataEnrichers } from '../../../../index_management_data'; function getIndexNamesFromPayload(payload) { return payload.indexNames || []; @@ -52,7 +53,12 @@ export function registerReloadRoute(server) { try { const hits = await fetchIndices(callWithRequest, indexNames); - const response = formatHits(hits); + let response = formatHits(hits); + const dataEnrichers = getIndexManagementDataEnrichers(); + for (let i = 0; i < dataEnrichers.length; i++) { + const dataEnricher = dataEnrichers[i]; + response = await dataEnricher(response, callWithRequest); + } return response; } catch (err) { if (isEsError(err)) { From bfffab0c61fa0bac9fa0ac33a8e670ebaed9adc0 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 2 Nov 2018 08:31:58 -0400 Subject: [PATCH 082/102] adding retry button for ilm covered index that is in error --- .../index_lifecycle_management/index.js | 4 +- .../public/api/index.js | 25 +++++---- .../components/index_lifecycle_summary.js | 16 ++++-- .../public/index.js | 1 + .../register_index_lifecycle_actions.js | 31 +++++++++++ .../routes/api/{indices => index}/index.js | 2 +- .../register_bootstrap_route.js | 0 .../register_get_affected_route.js | 0 .../register_index_routes.js} | 4 +- .../routes/api/index/register_retry_route.js | 52 +++++++++++++++++++ .../public/index_management_extensions.js | 1 + .../index_actions_context_menu.container.js | 12 +++-- .../index_actions_context_menu.js | 19 +++++++ .../index_management/public/services/api.js | 3 ++ .../public/store/actions/extension_action.js | 20 +++++++ .../public/store/actions/index.js | 1 + .../public/store/selectors/index.js | 6 ++- 17 files changed, 176 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js rename x-pack/plugins/index_lifecycle_management/server/routes/api/{indices => index}/index.js (78%) rename x-pack/plugins/index_lifecycle_management/server/routes/api/{indices => index}/register_bootstrap_route.js (100%) rename x-pack/plugins/index_lifecycle_management/server/routes/api/{indices => index}/register_get_affected_route.js (100%) rename x-pack/plugins/index_lifecycle_management/server/routes/api/{indices/register_indices_routes.js => index/register_index_routes.js} (77%) create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.js create mode 100644 x-pack/plugins/index_management/public/store/actions/extension_action.js diff --git a/x-pack/plugins/index_lifecycle_management/index.js b/x-pack/plugins/index_lifecycle_management/index.js index f5bc1e4745852..4c666fffde158 100644 --- a/x-pack/plugins/index_lifecycle_management/index.js +++ b/x-pack/plugins/index_lifecycle_management/index.js @@ -9,7 +9,7 @@ import { registerTemplatesRoutes } from './server/routes/api/templates'; import { registerNodesRoutes } from './server/routes/api/nodes'; import { registerPoliciesRoutes } from './server/routes/api/policies'; import { registerLifecycleRoutes } from './server/routes/api/lifecycle'; -import { registerIndicesRoutes } from './server/routes/api/indices'; +import { registerIndexRoutes } from './server/routes/api/index'; import { registerLicenseChecker } from './server/lib/register_license_checker'; import { PLUGIN } from './common/constants'; import { indexLifecycleDataEnricher } from './index_lifecycle_data'; @@ -27,7 +27,7 @@ export function indexLifecycleManagement(kibana) { registerNodesRoutes(server); registerPoliciesRoutes(server); registerLifecycleRoutes(server); - registerIndicesRoutes(server); + registerIndexRoutes(server); if ( server.plugins.index_management && server.plugins.index_management.addIndexManagementDataEnricher diff --git a/x-pack/plugins/index_lifecycle_management/public/api/index.js b/x-pack/plugins/index_lifecycle_management/public/api/index.js index 542acfe8e987b..8db47a8e6380c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/api/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/api/index.js @@ -3,30 +3,33 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import chrome from 'ui/chrome'; + let httpClient; export const setHttpClient = (client) => { httpClient = client; }; +const getHttpClient = () => { + return httpClient; +}; const apiPrefix = chrome.addBasePath('/api/index_lifecycle_management'); -export async function loadNodes() { +export async function loadNodes(httpClient = getHttpClient()) { const response = await httpClient.get(`${apiPrefix}/nodes/list`); return response.data; } -export async function loadNodeDetails(selectedNodeAttrs) { +export async function loadNodeDetails(selectedNodeAttrs, httpClient = getHttpClient()) { const response = await httpClient.get(`${apiPrefix}/nodes/${selectedNodeAttrs}/details`); return response.data; } -export async function loadIndexTemplates() { +export async function loadIndexTemplates(httpClient = getHttpClient()) { const response = await httpClient.get(`${apiPrefix}/templates`); return response.data; } -export async function loadIndexTemplate(templateName) { +export async function loadIndexTemplate(templateName, httpClient = getHttpClient()) { if (!templateName) { return {}; } @@ -34,26 +37,30 @@ export async function loadIndexTemplate(templateName) { return response.data; } -export async function loadPolicies(withIndices) { +export async function loadPolicies(withIndices, httpClient = getHttpClient()) { const response = await httpClient.get(`${apiPrefix}/policies${ withIndices ? '?withIndices=true' : ''}`); return response.data; } -export async function deletePolicy(policyName) { +export async function deletePolicy(policyName, httpClient = getHttpClient()) { const response = await httpClient.delete(`${apiPrefix}/policies/${policyName}`); return response.data; } -export async function saveLifecycle(lifecycle) { +export async function saveLifecycle(lifecycle, httpClient = getHttpClient()) { const response = await httpClient.post(`${apiPrefix}/lifecycle`, { lifecycle }); return response.data; } -export async function getAffectedIndices(indexTemplateName, policyName) { +export async function getAffectedIndices(indexTemplateName, policyName, httpClient = getHttpClient()) { const path = policyName ? `${apiPrefix}/indices/affected/${indexTemplateName}/${encodeURIComponent(policyName)}` : `${apiPrefix}/indices/affected/${indexTemplateName}`; const response = await httpClient.get(path); return response.data; } +export const retryLifecycleForIndex = async (indexNames, httpClient = getHttpClient()) => { + const response = await httpClient.post(`${apiPrefix}/index/retry`, { indexNames }); + return response.data; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js index a64efb148c0de..4e38932dab42d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js +++ b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js @@ -5,7 +5,7 @@ */ import React, { Component, Fragment } from 'react'; - +import moment from 'moment-timezone'; import { EuiFlexGroup, EuiFlexItem, @@ -46,9 +46,17 @@ export class IndexLifecycleSummary extends Component { }; Object.keys(HEADERS).forEach((fieldName, arrayIndex) => { const value = ilm[fieldName]; - const content = fieldName === 'step_info' ? ( - `${value.type}: ${value.reason}` - ) : value; + let content; + if (fieldName === 'step_info') { + if (value) { + content = `${value.type}: ${value.reason}`; + } + } else if (fieldName === 'action_time') { + content = moment(value).format('YYYY-MM-DD HH:mm:ss'); + } else { + content = value; + } + content = content || '-'; const cell = [ {HEADERS[fieldName]}: diff --git a/x-pack/plugins/index_lifecycle_management/public/index.js b/x-pack/plugins/index_lifecycle_management/public/index.js index 235137923b819..867ee0e3ef913 100644 --- a/x-pack/plugins/index_lifecycle_management/public/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/index.js @@ -7,3 +7,4 @@ import './register_management_section'; import './register_routes'; import './register_index_lifecycle_summary'; +import './register_index_lifecycle_actions'; diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js b/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js new file mode 100644 index 0000000000000..f37e06f8a8c4e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js @@ -0,0 +1,31 @@ +/* + * 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 { every } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { addActionExtension } from '../../index_management/public/index_management_extensions'; +import { retryLifecycleForIndex } from './api'; +addActionExtension((indices) => { + const allHaveErrors = every(indices, (index) => { + return (index.ilm && index.ilm.failed_step); + }); + if (!allHaveErrors) { + return null; + } + const indexNames = indices.map(({ name }) => name); + return { + requestMethod: retryLifecycleForIndex, + indexNames: [indexNames], + buttonLabel: i18n.translate('xpack.idxMgmt.retryIndexLifecycleActionButtonLabel', { + defaultMessage: 'Retry lifecycle', + }), + successMessage: i18n.translate('xpack.idxMgmt.retryIndexLifecycleAction.successfullyRetriedLifecycleMessage', { + defaultMessage: 'Successfully called retry lifecycle for: [{indexNames}]', + values: { indexNames: indexNames.join(', ') } + }), + }; +}); + diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/index.js similarity index 78% rename from x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js rename to x-pack/plugins/index_lifecycle_management/server/routes/api/index/index.js index 915fb695bb468..82fb2e3b2a372 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/index.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { registerIndicesRoutes } from './register_indices_routes'; +export { registerIndexRoutes } from './register_index_routes'; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_bootstrap_route.js similarity index 100% rename from x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_bootstrap_route.js rename to x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_bootstrap_route.js diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_get_affected_route.js similarity index 100% rename from x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_get_affected_route.js rename to x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_get_affected_route.js diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_index_routes.js similarity index 77% rename from x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js rename to x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_index_routes.js index 1f94eb13d125e..12007e778a989 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/indices/register_indices_routes.js +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_index_routes.js @@ -6,8 +6,10 @@ import { registerBootstrapRoute } from './register_bootstrap_route'; import { registerGetAffectedRoute } from './register_get_affected_route'; +import { registerRetryRoute } from './register_retry_route'; -export function registerIndicesRoutes(server) { +export function registerIndexRoutes(server) { registerBootstrapRoute(server); registerGetAffectedRoute(server); + registerRetryRoute(server); } diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.js b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.js new file mode 100644 index 0000000000000..97d51022374a6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.js @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; +import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; +import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; + +async function retryLifecycle(callWithRequest, indexNames) { + const responses = []; + for (let i = 0; i < indexNames.length; i++) { + const indexName = indexNames[i]; + const params = { + method: 'POST', + path: `/${indexName}/_ilm/retry`, + ignore: [ 404 ], + }; + + responses.push(callWithRequest('transport.request', params)); + } + return Promise.all(responses); +} + +export function registerRetryRoute(server) { + const isEsError = isEsErrorFactory(server); + const licensePreRouting = licensePreRoutingFactory(server); + + server.route({ + path: '/api/index_lifecycle_management/index/retry', + method: 'POST', + handler: async (request) => { + const callWithRequest = callWithRequestFactory(server, request); + + try { + const response = await retryLifecycle(callWithRequest, request.payload.indexNames); + return response; + } catch (err) { + if (isEsError(err)) { + return wrapEsError(err); + } + + return wrapUnknownError(err); + } + }, + config: { + pre: [ licensePreRouting ] + } + }); +} diff --git a/x-pack/plugins/index_management/public/index_management_extensions.js b/x-pack/plugins/index_management/public/index_management_extensions.js index eb14abd7681bd..192b75d4f4446 100644 --- a/x-pack/plugins/index_management/public/index_management_extensions.js +++ b/x-pack/plugins/index_management/public/index_management_extensions.js @@ -19,3 +19,4 @@ export const getActionExtensions = () => { return actionExtensions; }; + diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.container.js b/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.container.js index bbf6a5d4a9cea..458c861dbfe05 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.container.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.container.js @@ -15,11 +15,13 @@ import { openIndices, editIndexSettings, refreshIndices, - openDetailPanel + openDetailPanel, + performExtensionAction } from '../../../../store/actions'; import { - getIndexStatusByIndexName + getIndexStatusByIndexName, + getIndicesByName } from '../../../../store/selectors'; const mapStateToProps = (state, ownProps) => { @@ -29,7 +31,8 @@ const mapStateToProps = (state, ownProps) => { indexStatusByName[indexName] = getIndexStatusByIndexName(state, indexName); }); return { - indexStatusByName + indexStatusByName, + indices: getIndicesByName(state, indexNames) }; }; @@ -73,6 +76,9 @@ const mapDispatchToProps = (dispatch, { indexNames }) => { }, deleteIndices: () => { dispatch(deleteIndices({ indexNames })); + }, + performExtensionAction: (requestMethod, successMessage) => { + dispatch(performExtensionAction({ requestMethod, successMessage, indexNames })); } }; }; diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.js b/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.js index 7c5339b53f75f..855c6ee7fe302 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/index_actions_context_menu/index_actions_context_menu.js @@ -22,6 +22,7 @@ import { } from '@elastic/eui'; import { flattenPanelTree } from '../../../../lib/flatten_panel_tree'; import { INDEX_OPEN } from '../../../../../common/constants'; +import { getActionExtensions } from '../../../../index_management_extensions'; class IndexActionsContextMenuUi extends Component { constructor(props) { @@ -46,6 +47,8 @@ class IndexActionsContextMenuUi extends Component { detailPanel, indexNames, indexStatusByName, + performExtensionAction, + indices, intl } = this.props; const allOpen = all(indexNames, indexName => { @@ -174,6 +177,22 @@ class IndexActionsContextMenuUi extends Component { this.openDeleteConfirmationModal(); } }); + getActionExtensions().forEach((actionExtension) => { + const actionExtensionDefinition = actionExtension(indices); + if (actionExtensionDefinition) { + const { buttonLabel, requestMethod, successMessage } = actionExtensionDefinition; + items.push({ + name: buttonLabel, + icon: , + onClick: () => { + this.closePopoverAndExecute(() => performExtensionAction(requestMethod, successMessage)); + } + } + + ); + } + performExtensionAction; + }); items.forEach(item => { item['data-test-subj'] = 'indexTableContextMenuButton'; }); diff --git a/x-pack/plugins/index_management/public/services/api.js b/x-pack/plugins/index_management/public/services/api.js index fdbf800ce180c..ec74e309847a0 100644 --- a/x-pack/plugins/index_management/public/services/api.js +++ b/x-pack/plugins/index_management/public/services/api.js @@ -9,6 +9,9 @@ let httpClient; export const setHttpClient = (client) => { httpClient = client; }; +export const getHttpClient = () => { + return httpClient; +}; const apiPrefix = chrome.addBasePath('/api/index_management'); export async function loadIndices() { diff --git a/x-pack/plugins/index_management/public/store/actions/extension_action.js b/x-pack/plugins/index_management/public/store/actions/extension_action.js new file mode 100644 index 0000000000000..98ed6170f1b14 --- /dev/null +++ b/x-pack/plugins/index_management/public/store/actions/extension_action.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { reloadIndices } from '../actions'; +import { toastNotifications } from 'ui/notify'; +import { getHttpClient } from '../../services/api'; + +export const performExtensionAction = ({ requestMethod, indexNames, successMessage }) => async (dispatch) => { + try { + await requestMethod(indexNames, getHttpClient()); + } catch (error) { + toastNotifications.addDanger(error.data.message); + return; + } + dispatch(reloadIndices(indexNames)); + toastNotifications.addSuccess(successMessage); +}; diff --git a/x-pack/plugins/index_management/public/store/actions/index.js b/x-pack/plugins/index_management/public/store/actions/index.js index 86c6280e12bbf..a50854015adea 100644 --- a/x-pack/plugins/index_management/public/store/actions/index.js +++ b/x-pack/plugins/index_management/public/store/actions/index.js @@ -19,4 +19,5 @@ export * from './table_state'; export * from './edit_index_settings'; export * from './update_index_settings'; export * from './detail_panel'; +export * from './extension_action'; diff --git a/x-pack/plugins/index_management/public/store/selectors/index.js b/x-pack/plugins/index_management/public/store/selectors/index.js index 19a02908992e2..77019819fd5af 100644 --- a/x-pack/plugins/index_management/public/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/store/selectors/index.js @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import { Pager } from '@elastic/eui'; import { createSelector } from 'reselect'; @@ -16,6 +15,11 @@ export const getDetailPanelType = (state) => state.detailPanel.panelType; export const isDetailPanelOpen = (state) => !!getDetailPanelType(state); export const getDetailPanelIndexName = (state) => state.detailPanel.indexName; export const getIndices = (state) => state.indices.byId; +export const getIndicesByName = (state, indexNames) => { + const indices = getIndices(state); + console.log(indices); + return indexNames.map((indexName) => indices[indexName]); +}; export const getIndexByIndexName = (state, name) => getIndices(state)[name]; export const getFilteredIds = (state) => state.indices.filteredIds; export const getRowStatuses = (state) => state.rowStatus; From 88b0c7f34a65366fe18d4fc5224af0ee0ff2482a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 2 Nov 2018 14:23:33 -0400 Subject: [PATCH 083/102] first pass at index lifecycle banner --- .../components/index_lifecycle_banner.js | 27 +++++++++++++++++++ .../public/index.js | 1 + .../public/register_index_lifecycle_banner.js | 20 ++++++++++++++ .../public/index_management_extensions.js | 7 +++++ .../components/index_table/index_table.js | 7 ++++- 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js new file mode 100644 index 0000000000000..c492d56571c7b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { + EuiCallOut, + EuiText +} from '@elastic/eui'; + +export const IndexLifecycleBanner = (props) => { + const numberOfErroredIndices = props.indices.length; + return ( + + + { numberOfErroredIndices } ind{numberOfErroredIndices > 1 ? 'icies have ' : 'ex has ' } index lifecycle errors. + + + ); +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/index.js b/x-pack/plugins/index_lifecycle_management/public/index.js index 867ee0e3ef913..272904e39a8a2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/index.js @@ -8,3 +8,4 @@ import './register_management_section'; import './register_routes'; import './register_index_lifecycle_summary'; import './register_index_lifecycle_actions'; +import './register_index_lifecycle_banner'; diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js b/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js new file mode 100644 index 0000000000000..0d9b37f516b3e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { addBannerExtension } from '../../index_management/public/index_management_extensions'; +import { IndexLifecycleBanner } from './components/index_lifecycle_banner'; +addBannerExtension((indices) =>{ + if (!indices.length) { + return null; + } + const indicesWithLifecycleErrors = indices.filter((index) => { + return index.ilm && index.ilm.failed_step; + }); + if (!indicesWithLifecycleErrors.length) { + return null; + } + return ; +}); \ No newline at end of file diff --git a/x-pack/plugins/index_management/public/index_management_extensions.js b/x-pack/plugins/index_management/public/index_management_extensions.js index 192b75d4f4446..25ebb5eb0445a 100644 --- a/x-pack/plugins/index_management/public/index_management_extensions.js +++ b/x-pack/plugins/index_management/public/index_management_extensions.js @@ -18,5 +18,12 @@ export const addActionExtension = (actionExtension)=> { export const getActionExtensions = () => { return actionExtensions; }; +const bannerExtensions = []; +export const addBannerExtension = (actionExtension)=> { + bannerExtensions.push(actionExtension); +}; +export const getBannerExtensions = () => { + return bannerExtensions; +}; diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js b/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js index df501ef98ff32..e61489bbd1d0e 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js @@ -39,6 +39,7 @@ import { } from '@elastic/eui'; import { IndexActionsContextMenu } from '../../components'; +import { getBannerExtensions } from '../../../../index_management_extensions'; const HEADERS = { name: i18n.translate('xpack.idxMgmt.indexTable.headers.nameHeader', { @@ -196,7 +197,10 @@ export class IndexTableUi extends Component { ); }); } - + renderBanners() { + const { indices = [] } = this.props; + return getBannerExtensions().map(bannerExtension => bannerExtension(indices)); + } buildRows() { const { indices = [], detailPanelIndexName } = this.props; return indices.map(index => { @@ -292,6 +296,7 @@ export class IndexTableUi extends Component { + { this.renderBanners()} {atLeastOneItemSelected ? ( From 94ef3e7296f00ecf4a54ca8ee1e8f8708ed58171 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 7 Sep 2018 09:53:03 -0400 Subject: [PATCH 084/102] i18n work --- .../public/components/learn_more_link.js | 12 +++++--- .../components/policy_table/confirm_delete.js | 30 ++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js b/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js index 17f6e5e2d1661..25ebeac229729 100644 --- a/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js +++ b/x-pack/plugins/index_lifecycle_management/public/components/learn_more_link.js @@ -7,11 +7,11 @@ import React from 'react'; import { EuiLink } from '@elastic/eui'; import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; - +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`; -export class LearnMoreLink extends React.PureComponent { +export class LearnMoreLinkUi extends React.PureComponent { render() { const { href, docPath } = this.props; let url; @@ -22,9 +22,13 @@ export class LearnMoreLink extends React.PureComponent { } return ( - Learn more + ); } -} \ No newline at end of file +} +export const LearnMoreLink = injectI18n(LearnMoreLinkUi); \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js index d3ad96e8b1b8c..596ccb6645ea1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/policy_table/components/policy_table/confirm_delete.js @@ -5,27 +5,39 @@ */ import React, { Component } from 'react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; import { toastNotifications } from 'ui/notify'; import { deletePolicy } from '../../../../api'; -export class ConfirmDelete extends Component { +export class ConfirmDeleteUi extends Component { deletePolicy = async () => { - const { policyToDelete, callback } = this.props; + const { intl, policyToDelete, callback } = this.props; const policyName = policyToDelete.name; try { await deletePolicy(policyName); - toastNotifications.addSuccess(`Deleted policy ${policyName}`); + const message = intl.formatMessage({ + id: 'xpack.indexLifecycleMgmt.confirmDelete.successMessage', + defaultMessage: 'Deleted policy {policyName}', + }, { policyName }); + toastNotifications.addSuccess(message); } catch (e) { - toastNotifications.addDanger(`Error deleting policy ${policyName}`); + const message = intl.formatMessage({ + id: 'xpack.indexLifecycleMgmt.confirmDelete.errorMessage', + defaultMessage: 'Error deleting policy}{policyName}', + }, { policyName }); + toastNotifications.addDanger(message); } if (callback) { callback(); } }; render() { - const { policyToDelete, onCancel } = this.props; - const title = `Delete policy '${policyToDelete.name}'`; + const { intl, policyToDelete, onCancel } = this.props; + const title = intl.formatMessage({ + id: 'xpack.indexLifecycleMgmt.confirmDelete.title', + defaultMessage: 'Delete {name}', + }, { name: policyToDelete.name }); return (
-

This operation cannot be undone.

+
); } } +export const ConfirmDelete = injectI18n(ConfirmDeleteUi); From c902b7018c4a42a2994dd78e345bc5c4123a8d1b Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Fri, 7 Sep 2018 12:54:25 -0400 Subject: [PATCH 085/102] more i18n --- .../node_attrs_details/node_attrs_details.js | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js index 7cb2977e706ab..e55a6df0e9013 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/node_attrs_details/node_attrs_details.js @@ -18,8 +18,10 @@ import { EuiCallOut, EuiPortal, } from '@elastic/eui'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; -export class NodeAttrsDetails extends PureComponent { + +export class NodeAttrsDetailsUi extends PureComponent { static propTypes = { fetchNodeDetails: PropTypes.func.isRequired, close: PropTypes.func.isRequired, @@ -34,14 +36,20 @@ export class NodeAttrsDetails extends PureComponent { } render() { - const { selectedNodeAttrs, allocationRules, details, close } = this.props; + const { selectedNodeAttrs, allocationRules, details, close, intl } = this.props; return ( -

Nodes that contain the attribute: `{selectedNodeAttrs}`

+

+ +

{allocationRules ? ( @@ -51,8 +59,10 @@ export class NodeAttrsDetails extends PureComponent { title="Heads up" color="warning" > - Be aware that this index template has existing allocation rules - which will affect the list of nodes these indices can be allocated to. + @@ -60,9 +70,18 @@ export class NodeAttrsDetails extends PureComponent { - Close +
@@ -78,3 +100,5 @@ export class NodeAttrsDetails extends PureComponent { ); } } +export const NodeAttrsDetails = injectI18n(NodeAttrsDetailsUi); + From b5e2f04a1c9b631df429f9b76d5a1ef385157bd3 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 5 Nov 2018 13:19:54 -0500 Subject: [PATCH 086/102] fixing issue with node attributes --- .../public/store/selectors/index_template.js | 7 ++++--- .../public/store/selectors/policies.js | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js index 339d5380613ff..0ba81b4595296 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/index_template.js @@ -124,7 +124,7 @@ export const getExistingAllocationRules = state => { const hasJSONChanged = (json1, json2) => JSON.stringify(json1) !== JSON.stringify(json2); export const getTemplateDiff = state => { const originalFullIndexTemplate = getFullSelectedIndexTemplate(state) || { settings: {} }; - const attributeName = getSelectedNodeAttrs(state); + const attributeNameAndValue = getSelectedNodeAttrs(state); const baseNewFullIndexTemplate = { settings: { index: { @@ -136,11 +136,12 @@ export const getTemplateDiff = state => { } } }; - if (attributeName) { + if (attributeNameAndValue) { + const [ name, value ] = attributeNameAndValue.split[':']; baseNewFullIndexTemplate.routing = { allocation: { include: { - sattr_name: attributeName, + [name]: value, } } }; diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index d41ef602b8430..5f4734626cc93 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -179,7 +179,9 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { if (actions.allocate) { const allocate = actions.allocate; if (allocate.require) { - policy[PHASE_NODE_ATTRS] = allocate.require._name; + Object.entries(allocate.require).forEach((entry) => { + policy[PHASE_NODE_ATTRS] = entry.join(':'); + }); } } @@ -248,11 +250,12 @@ export const phaseToES = (state, phase) => { } } if (phase[PHASE_NODE_ATTRS]) { + const [ name, value, ] = phase[PHASE_NODE_ATTRS].split(':'); esPhase.actions.allocate = { include: {}, // TODO: this seems to be a constant, confirm? exclude: {}, // TODO: this seems to be a constant, confirm? require: { - _name: phase[PHASE_NODE_ATTRS] + [name]: value } }; if (isNumber(phase[PHASE_REPLICA_COUNT])) { From 861a0e856935bbbf1f878931fa0591b669e37283 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 5 Nov 2018 13:21:31 -0500 Subject: [PATCH 087/102] removing console.log statements --- .../index_list/components/detail_panel/summary/summary.js | 1 - x-pack/plugins/index_management/public/store/selectors/index.js | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js b/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js index 44b5ca4c24930..9a33de20f9d6d 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/detail_panel/summary/summary.js @@ -53,7 +53,6 @@ export class Summary extends React.PureComponent { getAdditionalContent() { const { index } = this.props; const extensions = getSummaryExtensions(); - console.log(extensions); return extensions.map((summaryExtension) => { return ( diff --git a/x-pack/plugins/index_management/public/store/selectors/index.js b/x-pack/plugins/index_management/public/store/selectors/index.js index 77019819fd5af..ec08aa69d1f11 100644 --- a/x-pack/plugins/index_management/public/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/store/selectors/index.js @@ -17,7 +17,6 @@ export const getDetailPanelIndexName = (state) => state.detailPanel.indexName; export const getIndices = (state) => state.indices.byId; export const getIndicesByName = (state, indexNames) => { const indices = getIndices(state); - console.log(indices); return indexNames.map((indexName) => indices[indexName]); }; export const getIndexByIndexName = (state, name) => getIndices(state)[name]; From f1fc1f7460cce25a9d242aa1488e978302a2cbe2 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 5 Nov 2018 13:48:18 -0500 Subject: [PATCH 088/102] fixing issue with deserializing number_of_shards for edit policy --- .../public/store/selectors/policies.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js index 5f4734626cc93..be50ad382c87a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/policies.js @@ -182,6 +182,10 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { Object.entries(allocate.require).forEach((entry) => { policy[PHASE_NODE_ATTRS] = entry.join(':'); }); + // checking for null or undefined here + if (allocate.number_of_replicas != null) { + policy[PHASE_REPLICA_COUNT] = allocate.number_of_replicas; + } } } @@ -194,11 +198,6 @@ export const phaseFromES = (phase, phaseName, defaultPolicy) => { if (actions.shrink) { policy[PHASE_PRIMARY_SHARD_COUNT] = actions.shrink.number_of_shards; } - - if (actions.replicas) { - const replicas = actions.replicas; - policy[PHASE_REPLICA_COUNT] = replicas.number_of_replicas; - } } return policy; }; From 7bd290038cb55d0eaed2fcf2b5310f6a3524c708 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 5 Nov 2018 13:52:38 -0500 Subject: [PATCH 089/102] defaulting shrink to false and fixing ui spacing issue --- .../sections/edit_policy/components/warm_phase/warm_phase.js | 1 + .../public/store/defaults/warm_phase.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js index 4320e043489f0..fc80aad807281 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js @@ -255,6 +255,7 @@ export class WarmPhase extends PureComponent { /> {phaseData[PHASE_SHRINK_ENABLED] ? ( + Date: Mon, 5 Nov 2018 14:00:17 -0500 Subject: [PATCH 090/102] removing hot phase shard count from warm phase --- .../warm_phase/warm_phase.container.js | 4 --- .../components/warm_phase/warm_phase.js | 34 ------------------- 2 files changed, 38 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js index b6d1c579de58f..c21cc9e7bfbee 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.container.js @@ -12,8 +12,6 @@ import { WarmPhase as PresentationComponent } from './warm_phase'; import { getNodeOptions, getPhase, - getSelectedReplicaCount, - getSelectedPrimaryShardCount } from '../../../../store/selectors'; import { setPhaseData, fetchNodes } from '../../../../store/actions'; import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../store/constants'; @@ -21,8 +19,6 @@ import { PHASE_WARM, PHASE_HOT, PHASE_ROLLOVER_ENABLED } from '../../../../store export const WarmPhase = connect( state => ({ phaseData: getPhase(state, PHASE_WARM), - hotPhaseReplicaCount: Number(getSelectedReplicaCount(state)), - hotPhasePrimaryShardCount: Number(getSelectedPrimaryShardCount(state)), hotPhaseRolloverEnabled: getPhase(state, PHASE_HOT)[PHASE_ROLLOVER_ENABLED], nodeOptions: getNodeOptions(state) }), diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js index fc80aad807281..1723ff9d0deca 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js @@ -59,10 +59,6 @@ export class WarmPhase extends PureComponent { [PHASE_ROLLOVER_MINIMUM_AGE]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: PropTypes.string.isRequired, }).isRequired, - - hotPhaseReplicaCount: PropTypes.number.isRequired, - hotPhasePrimaryShardCount: PropTypes.number.isRequired, - nodeOptions: PropTypes.array.isRequired, }; @@ -74,10 +70,7 @@ export class WarmPhase extends PureComponent { const { setPhaseData, showNodeDetailsFlyout, - phaseData, - hotPhaseReplicaCount, - hotPhasePrimaryShardCount, nodeOptions, errors, isShowingErrors, @@ -217,18 +210,6 @@ export class WarmPhase extends PureComponent { /> - - - { - await setPhaseData(PHASE_REPLICA_COUNT, hotPhaseReplicaCount); - }} - > - Set to same as hot phase - - - @@ -273,21 +254,6 @@ export class WarmPhase extends PureComponent { />
- - - { - await setPhaseData( - PHASE_PRIMARY_SHARD_COUNT, - hotPhasePrimaryShardCount - ); - }} - > - Set to same as hot phase - - -
From 008d37c6374d802543811bffdbbc086b20681d50 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 5 Nov 2018 17:08:38 -0500 Subject: [PATCH 091/102] scrolling to first error when user submits form for edit policy --- .../public/lib/find_errors.js | 23 ++++++++++------ .../components/cold_phase/cold_phase.js | 4 +++ .../components/delete_phase/delete_phase.js | 2 ++ .../components/hot_phase/hot_phase.js | 7 ++++- .../components/warm_phase/warm_phase.js | 6 +++++ .../edit_policy/edit_policy.container.js | 26 ++++++++++++------- .../sections/edit_policy/edit_policy.js | 19 +++++++++----- 7 files changed, 61 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js index 39d260b05301e..6a95326ee85c9 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js @@ -4,19 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -export const hasErrors = (object, keysToIgnore = []) => { - let errors = false; - for (const [key, value] of Object.entries(object)) { - if (keysToIgnore.includes(key)) continue; +export const findFirstError = (object, topLevel = true) => { + console.log("ERRORS", object); + + let firstError; + const keys = topLevel ? [ 'policyName', 'hot', 'warm', 'cold', 'delete'] : Object.keys(object); + for (const key of keys) { + const value = object[key]; if (Array.isArray(value) && value.length > 0) { - errors = true; + firstError = key; + console.log(`Found error ${firstError}`); break; } else if (value) { - errors = hasErrors(value, keysToIgnore); - if (errors) { + firstError = findFirstError(value, false); + if (firstError) { + firstError = `${key}.${firstError}`; + console.log(`Found error ${firstError}`); break; } } } - return errors; + console.log(`Returning ${firstError}`); + return firstError; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js index 3e24bc18da1be..6ee756c142b61 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js @@ -22,6 +22,7 @@ import { EuiButton, } from '@elastic/eui'; import { + PHASE_COLD, PHASE_ENABLED, PHASE_ROLLOVER_ALIAS, PHASE_ROLLOVER_MINIMUM_AGE, @@ -118,6 +119,7 @@ export class ColdPhase extends PureComponent { @@ -173,6 +177,7 @@ export class HotPhase extends PureComponent { ({ - errors: validateLifecycle(state), - selectedPolicy: getSelectedPolicy(state), - affectedIndexTemplates: getAffectedIndexTemplates(state), - saveAsNewPolicy: getSaveAsNewPolicy(state), - lifecycle: getLifecycle(state), - policies: getPolicies(state), - isPolicyListLoaded: isPolicyListLoaded(state), - isNewPolicy: getIsNewPolicy(state), - }), + state => { + const errors = validateLifecycle(state); + const firstError = findFirstError(errors); + return { + firstError, + errors, + selectedPolicy: getSelectedPolicy(state), + affectedIndexTemplates: getAffectedIndexTemplates(state), + saveAsNewPolicy: getSaveAsNewPolicy(state), + lifecycle: getLifecycle(state), + policies: getPolicies(state), + isPolicyListLoaded: isPolicyListLoaded(state), + isNewPolicy: getIsNewPolicy(state), + }; + }, { setSelectedPolicy, setSelectedPolicyName, diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js index 4686efb39928d..9715953c53d3b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js @@ -34,7 +34,7 @@ import { PHASE_WARM, STRUCTURE_POLICY_NAME, } from '../../store/constants'; -import { hasErrors } from '../../lib/find_errors'; +import { findFirstError } from '../../lib/find_errors'; import { NodeAttrsDetails } from './components/node_attrs_details'; import { ErrableFormRow } from './form_errors'; @@ -87,13 +87,17 @@ export class EditPolicy extends Component { submit = async () => { this.setState({ isShowingErrors: true }); const { - errors, saveLifecyclePolicy, lifecycle, saveAsNewPolicy, + firstError } = this.props; - if (hasErrors(errors)) { + if (firstError) { toastNotifications.addDanger('Please the fix errors on the page'); + const element = document.getElementById(`${firstError}-row`); + if (element) { + element.scrollIntoView(); + } } else { const success = await saveLifecyclePolicy(lifecycle, saveAsNewPolicy); if (success) { @@ -176,6 +180,7 @@ export class EditPolicy extends Component { {saveAsNewPolicy || !policyName ? ( From 45d4e8c51d8569351a6c2548a7ee73429e066747 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 6 Nov 2018 09:22:52 -0500 Subject: [PATCH 092/102] disabling UI for index management when enabled is false in kibana.yml --- x-pack/plugins/index_management/index.js | 8 ++++- .../public/register_management_section.js | 17 ++++++----- .../public/register_routes.js | 30 ++++++++++--------- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/index_management/index.js b/x-pack/plugins/index_management/index.js index c48eaa6ba15ca..6e29b91882d48 100644 --- a/x-pack/plugins/index_management/index.js +++ b/x-pack/plugins/index_management/index.js @@ -21,7 +21,13 @@ export function indexManagement(kibana) { styleSheetPaths: `${__dirname}/public/index.scss`, managementSections: [ 'plugins/index_management', - ] + ], + injectDefaultVars(server) { + const config = server.config(); + return { + indexManagementUiEnabled: config.get(`${PLUGIN.ID}.enabled`) + }; + }, }, init: function (server) { server.expose('addIndexManagementDataEnricher', addIndexManagementDataEnricher); diff --git a/x-pack/plugins/index_management/public/register_management_section.js b/x-pack/plugins/index_management/public/register_management_section.js index 78dec3515a899..3f084da770e1d 100644 --- a/x-pack/plugins/index_management/public/register_management_section.js +++ b/x-pack/plugins/index_management/public/register_management_section.js @@ -7,12 +7,15 @@ import { management } from 'ui/management'; import { i18n } from '@kbn/i18n'; import { BASE_PATH } from '../common/constants'; +import chrome from 'ui/chrome'; +if (chrome.getInjected('indexManagementUiEnabled')) { + const esSection = management.getSection('elasticsearch'); + esSection.register('index_management', { + visible: true, + display: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), + order: 1, + url: `#${BASE_PATH}home` + }); +} -const esSection = management.getSection('elasticsearch'); -esSection.register('index_management', { - visible: true, - display: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), - order: 1, - url: `#${BASE_PATH}home` -}); diff --git a/x-pack/plugins/index_management/public/register_routes.js b/x-pack/plugins/index_management/public/register_routes.js index 02666e81a0b5c..acae726940b04 100644 --- a/x-pack/plugins/index_management/public/register_routes.js +++ b/x-pack/plugins/index_management/public/register_routes.js @@ -19,6 +19,7 @@ import routes from 'ui/routes'; import template from './main.html'; import { manageAngularLifecycle } from './lib/manage_angular_lifecycle'; import { indexManagementStore } from './store'; +import chrome from 'ui/chrome'; const renderReact = async (elem) => { render( @@ -32,21 +33,22 @@ const renderReact = async (elem) => { elem ); }; - -routes.when(`${BASE_PATH}:view?/:id?`, { - template: template, - controllerAs: 'indexManagement', - controller: class IndexManagementController { - constructor($scope, $route, $http) { +if (chrome.getInjected('indexManagementUiEnabled')) { + routes.when(`${BASE_PATH}:view?/:id?`, { + template: template, + controllerAs: 'indexManagement', + controller: class IndexManagementController { + constructor($scope, $route, $http) { // NOTE: We depend upon Angular's $http service because it's decorated with interceptors, // e.g. to check license status per request. - setHttpClient($http); + setHttpClient($http); - $scope.$$postDigest(() => { - const elem = document.getElementById('indexManagementReactRoot'); - renderReact(elem); - manageAngularLifecycle($scope, $route, elem); - }); + $scope.$$postDigest(() => { + const elem = document.getElementById('indexManagementReactRoot'); + renderReact(elem); + manageAngularLifecycle($scope, $route, elem); + }); + } } - } -}); + }); +} From e16aa554340cd68b64dae44ee6ba76f726acceaf Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Tue, 6 Nov 2018 09:35:19 -0500 Subject: [PATCH 093/102] disabling index lifecycle management when enabled is false in kibana.yml --- .../index_lifecycle_management/index.js | 6 +++ .../public/index.js | 4 +- .../index.js | 12 ++++++ .../register_index_lifecycle_actions.js | 4 +- .../register_index_lifecycle_banner.js | 4 +- .../register_index_lifecycle_summary.js | 4 +- .../public/register_management_section.js | 18 ++++---- .../public/register_routes.js | 43 ++++++++++--------- 8 files changed, 58 insertions(+), 37 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/index.js rename x-pack/plugins/index_lifecycle_management/public/{ => register_index_management_extensions}/register_index_lifecycle_actions.js (87%) rename x-pack/plugins/index_lifecycle_management/public/{ => register_index_management_extensions}/register_index_lifecycle_banner.js (77%) rename x-pack/plugins/index_lifecycle_management/public/{ => register_index_management_extensions}/register_index_lifecycle_summary.js (66%) diff --git a/x-pack/plugins/index_lifecycle_management/index.js b/x-pack/plugins/index_lifecycle_management/index.js index 4c666fffde158..ce483d9218864 100644 --- a/x-pack/plugins/index_lifecycle_management/index.js +++ b/x-pack/plugins/index_lifecycle_management/index.js @@ -20,6 +20,12 @@ export function indexLifecycleManagement(kibana) { require: ['kibana', 'elasticsearch', 'xpack_main', 'index_management'], uiExports: { managementSections: ['plugins/index_lifecycle_management'], + injectDefaultVars(server) { + const config = server.config(); + return { + indexLifecycleManagementUiEnabled: config.get(`${PLUGIN.ID}.enabled`) + }; + }, }, init: function (server) { registerLicenseChecker(server); diff --git a/x-pack/plugins/index_lifecycle_management/public/index.js b/x-pack/plugins/index_lifecycle_management/public/index.js index 272904e39a8a2..3747d4e89a7b1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/index.js +++ b/x-pack/plugins/index_lifecycle_management/public/index.js @@ -6,6 +6,4 @@ import './register_management_section'; import './register_routes'; -import './register_index_lifecycle_summary'; -import './register_index_lifecycle_actions'; -import './register_index_lifecycle_banner'; +import './register_index_management_extensions'; diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/index.js b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/index.js new file mode 100644 index 0000000000000..706894c14eacc --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/index.js @@ -0,0 +1,12 @@ +/* + * 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 chrome from 'ui/chrome'; +if (chrome.getInjected('indexLifecycleManagementUiEnabled')) { + require('./register_index_lifecycle_actions'); + require('./register_index_lifecycle_banner'); + require('./register_index_lifecycle_summary'); +} \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_actions.js similarity index 87% rename from x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js rename to x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_actions.js index f37e06f8a8c4e..9e265d82009c8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_actions.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_actions.js @@ -6,8 +6,8 @@ import { every } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { addActionExtension } from '../../index_management/public/index_management_extensions'; -import { retryLifecycleForIndex } from './api'; +import { addActionExtension } from '../../../index_management/public/index_management_extensions'; +import { retryLifecycleForIndex } from '../api'; addActionExtension((indices) => { const allHaveErrors = every(indices, (index) => { return (index.ilm && index.ilm.failed_step); diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js similarity index 77% rename from x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js rename to x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js index 0d9b37f516b3e..0fa78985fc407 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_banner.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { addBannerExtension } from '../../index_management/public/index_management_extensions'; -import { IndexLifecycleBanner } from './components/index_lifecycle_banner'; +import { addBannerExtension } from '../../../index_management/public/index_management_extensions'; +import { IndexLifecycleBanner } from '../components/index_lifecycle_banner'; addBannerExtension((indices) =>{ if (!indices.length) { return null; diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_summary.js similarity index 66% rename from x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js rename to x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_summary.js index cd5a262b1635a..e1a74f0dd6991 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_index_lifecycle_summary.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_summary.js @@ -5,8 +5,8 @@ */ import React from 'react'; -import { addSummaryExtension } from '../../index_management/public/index_management_extensions'; -import { IndexLifecycleSummary } from './components/index_lifecycle_summary'; +import { addSummaryExtension } from '../../../index_management/public/index_management_extensions'; +import { IndexLifecycleSummary } from '../components/index_lifecycle_summary'; addSummaryExtension((index) => { return ; }); diff --git a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js index 461f94c573c5f..ed6ecfd756028 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_management_section.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_management_section.js @@ -6,12 +6,14 @@ import { management } from 'ui/management'; import { BASE_PATH } from '../common/constants'; - -const esSection = management.getSection('elasticsearch'); -esSection.register('index_lifecycle_management', { - visible: true, - display: 'Index Lifecycle Management', - order: 1, - url: `#${BASE_PATH}policies` -}); +import chrome from 'ui/chrome'; +if (chrome.getInjected('indexLifecycleManagementUiEnabled')) { + const esSection = management.getSection('elasticsearch'); + esSection.register('index_lifecycle_management', { + visible: true, + display: 'Index Lifecycle Management', + order: 1, + url: `#${BASE_PATH}policies` + }); +} diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js index 6395abca92c3c..27a26b98f503b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_routes.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -19,6 +19,7 @@ import routes from 'ui/routes'; import template from './main.html'; import { manageAngularLifecycle } from './lib/manage_angular_lifecycle'; +import chrome from 'ui/chrome'; const renderReact = async (elem) => { render( @@ -30,24 +31,26 @@ const renderReact = async (elem) => { elem ); }; - -routes.when(`${BASE_PATH}:view?/:action?/:id?`, { - template: template, - controllerAs: 'indexManagement', - controller: class IndexManagementController { - constructor($scope, $route, $http, kbnUrl, $rootScope) { - setHttpClient($http); - setUrlService({ - change(url) { - kbnUrl.change(url); - $rootScope.$digest(); - } - }); - $scope.$$postDigest(() => { - const elem = document.getElementById('indexLifecycleManagementReactRoot'); - renderReact(elem); - manageAngularLifecycle($scope, $route, elem); - }); +if (chrome.getInjected('indexLifecycleManagementUiEnabled')) { + routes.when(`${BASE_PATH}:view?/:action?/:id?`, { + template: template, + controllerAs: 'indexLifecycleManagement', + controller: class IndexLifecycleManagementController { + constructor($scope, $route, $http, kbnUrl, $rootScope) { + console.log("ILM"); + setHttpClient($http); + setUrlService({ + change(url) { + kbnUrl.change(url); + $rootScope.$digest(); + } + }); + $scope.$$postDigest(() => { + const elem = document.getElementById('indexLifecycleManagementReactRoot'); + renderReact(elem); + manageAngularLifecycle($scope, $route, elem); + }); + } } - } -}); + }); +} From 71d4889ab9c0a7856b15d39055a42f0ddbf5a0d1 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 07:40:03 -0500 Subject: [PATCH 094/102] extending index management filter to allow for searching fields --- .../index_list/components/index_table/index_table.js | 3 --- .../index_management/public/services/filter_items.js | 8 ++++++-- .../index_management/public/store/selectors/index.js | 10 +++++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js b/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js index e61489bbd1d0e..0f7e90ef7eee3 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js @@ -63,9 +63,6 @@ const HEADERS = { size: i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', { defaultMessage: 'Storage size', }), - primary_size: i18n.translate('xpack.idxMgmt.indexTable.headers.primaryStorageSizeHeader', { - defaultMessage: 'Primary storage size', - }) }; export class IndexTableUi extends Component { diff --git a/x-pack/plugins/index_management/public/services/filter_items.js b/x-pack/plugins/index_management/public/services/filter_items.js index 6d2e3dae57f46..3ec88bf600246 100644 --- a/x-pack/plugins/index_management/public/services/filter_items.js +++ b/x-pack/plugins/index_management/public/services/filter_items.js @@ -3,13 +3,17 @@ * 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'; export const filterItems = (fields, filter = '', items = []) => { - const lowerFilter = filter.toLowerCase(); + const lowerFilter = filter.trim().toLowerCase(); return items.filter(item => { const actualFields = fields || Object.keys(item); const indexOfMatch = actualFields.findIndex(field => { - const normalizedField = String(item[field]).toLowerCase(); + console.log('GETTING', item, field, filter); + const normalizedField = String(get(item, field)).toLowerCase(); + console.log('N', normalizedField); + console.log("MATCH?", normalizedField.includes(lowerFilter)); return normalizedField.includes(lowerFilter); }); return indexOfMatch !== -1; diff --git a/x-pack/plugins/index_management/public/store/selectors/index.js b/x-pack/plugins/index_management/public/store/selectors/index.js index ec08aa69d1f11..fe0d1678c1b35 100644 --- a/x-pack/plugins/index_management/public/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/store/selectors/index.js @@ -30,6 +30,7 @@ export const getIndexStatusByIndexName = (state, indexName) => { const { status } = indices[indexName] || {}; return status; }; +const defaultFilterFields = ['name', 'uuid']; const getFilteredIndices = createSelector( getIndices, getRowStatuses, @@ -39,7 +40,14 @@ const getFilteredIndices = createSelector( const systemFilteredIndexes = tableState.showSystemIndices ? indexArray : indexArray.filter(index => !(index.name + '').startsWith('.')); - return filterItems(['name', 'uuid'], tableState.filter, systemFilteredIndexes); + let filter = tableState.filter; + let fields = defaultFilterFields; + if (filter.includes(':')) { + const splitFilter = filter.split(':'); + fields = [ splitFilter[0]]; + filter = splitFilter[1]; + } + return filterItems(fields, filter, systemFilteredIndexes); } ); export const getTotalItems = createSelector( From 013bf6a0f44b521c3ef14d64bddfc8f491c30100 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 08:59:57 -0500 Subject: [PATCH 095/102] add support for filtering to indices with errors for index lifecycle management banner --- .../components/index_lifecycle_banner.js | 27 ------ .../register_index_lifecycle_banner.js | 22 +++-- .../components/index_table/index_table.js | 84 ++++++++++++------- .../public/services/filter_items.js | 3 +- 4 files changed, 71 insertions(+), 65 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js deleted file mode 100644 index c492d56571c7b..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_banner.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { - EuiCallOut, - EuiText -} from '@elastic/eui'; - -export const IndexLifecycleBanner = (props) => { - const numberOfErroredIndices = props.indices.length; - return ( - - - { numberOfErroredIndices } ind{numberOfErroredIndices > 1 ? 'icies have ' : 'ex has ' } index lifecycle errors. - - - ); -}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js index 0fa78985fc407..32ef838e7b0e2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js @@ -3,18 +3,30 @@ * 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 { get } from 'lodash'; import { addBannerExtension } from '../../../index_management/public/index_management_extensions'; -import { IndexLifecycleBanner } from '../components/index_lifecycle_banner'; +const stepPath = 'ilm.step'; +import { i18n } from '@kbn/i18n'; + addBannerExtension((indices) =>{ if (!indices.length) { return null; } const indicesWithLifecycleErrors = indices.filter((index) => { - return index.ilm && index.ilm.failed_step; + return get(index, stepPath) === 'ERROR'; }); - if (!indicesWithLifecycleErrors.length) { + const numIndicesWithLifecycleErrors = indicesWithLifecycleErrors.length; + if (!numIndicesWithLifecycleErrors) { return null; } - return ; + return { + type: 'warning', + filter: `${stepPath}:ERROR`, + message: i18n.translate('xpack.idxLifecycleMgmt.indexMgmtBanner.errorMessage', { + defaultMessage: `{ numIndicesWithLifecycleErrors, number} + {numIndicesWithLifecycleErrors, plural, one {index has} other {indices have} } + lifecycle errors`, + values: { numIndicesWithLifecycleErrors } + }), + }; }); \ No newline at end of file diff --git a/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js b/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js index 0f7e90ef7eee3..4d9c6b5d95862 100644 --- a/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/sections/index_list/components/index_table/index_table.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; -import { i18n } from '@kbn/i18n'; +import React, { Component, Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { Route } from 'react-router-dom'; import { NoMatch } from '../../../no_match'; @@ -14,6 +14,8 @@ import { healthToColor } from '../../../../services'; import '../../../../styles/table.less'; import { + EuiButtonEmpty, + EuiCallOut, EuiHealth, EuiLink, EuiCheckbox, @@ -35,7 +37,7 @@ import { EuiTitle, EuiText, EuiPageBody, - EuiPageContent + EuiPageContent, } from '@elastic/eui'; import { IndexActionsContextMenu } from '../../components'; @@ -88,7 +90,7 @@ export class IndexTableUi extends Component { super(props); this.state = { - selectedIndicesMap: {} + selectedIndicesMap: {}, }; } @@ -110,7 +112,7 @@ export class IndexTableUi extends Component { selectedIndicesMap[name] = true; }); this.setState({ - selectedIndicesMap + selectedIndicesMap, }); }; @@ -123,7 +125,7 @@ export class IndexTableUi extends Component { newMap[name] = true; } return { - selectedIndicesMap: newMap + selectedIndicesMap: newMap, }; }); }; @@ -134,9 +136,7 @@ export class IndexTableUi extends Component { areAllItemsSelected = () => { const { indices } = this.props; - const indexOfUnselectedItem = indices.findIndex( - index => !this.isItemSelected(index.name) - ); + const indexOfUnselectedItem = indices.findIndex(index => !this.isItemSelected(index.name)); return indexOfUnselectedItem === -1; }; @@ -195,8 +195,34 @@ export class IndexTableUi extends Component { }); } renderBanners() { - const { indices = [] } = this.props; - return getBannerExtensions().map(bannerExtension => bannerExtension(indices)); + const { indices = [], filterChanged } = this.props; + return getBannerExtensions().map(bannerExtension => { + const bannerData = bannerExtension(indices); + console.log(bannerData); + if (!bannerData) { + return null; + } + return ( + + + {bannerData.message} {bannerData.filter ? ( + { + filterChanged(bannerData.filter); + }} + > + + + ) : null} + + + + + ); + }); } buildRows() { const { indices = [], detailPanelIndexName } = this.props; @@ -204,9 +230,7 @@ export class IndexTableUi extends Component { const { name } = index; return ( @@ -285,15 +309,17 @@ export class IndexTableUi extends Component { id="checkboxShowSystemIndices" checked={showSystemIndices} onChange={event => showSystemIndicesChanged(event.target.checked)} - label={} + label={ + + } /> - { this.renderBanners()} + {this.renderBanners()} {atLeastOneItemSelected ? ( @@ -318,18 +344,14 @@ export class IndexTableUi extends Component { filterChanged(event.target.value); }} data-test-subj="indexTableFilterInput" - placeholder={ - intl.formatMessage({ - id: 'xpack.idxMgmt.indexTable.systemIndicesSearchInputPlaceholder', - defaultMessage: 'Search', - }) - } - aria-label={ - intl.formatMessage({ - id: 'xpack.idxMgmt.indexTable.systemIndicesSearchIndicesAriaLabel', - defaultMessage: 'Search indices', - }) - } + placeholder={intl.formatMessage({ + id: 'xpack.idxMgmt.indexTable.systemIndicesSearchInputPlaceholder', + defaultMessage: 'Search', + })} + aria-label={intl.formatMessage({ + id: 'xpack.idxMgmt.indexTable.systemIndicesSearchIndicesAriaLabel', + defaultMessage: 'Search indices', + })} /> diff --git a/x-pack/plugins/index_management/public/services/filter_items.js b/x-pack/plugins/index_management/public/services/filter_items.js index 3ec88bf600246..5151f055136c0 100644 --- a/x-pack/plugins/index_management/public/services/filter_items.js +++ b/x-pack/plugins/index_management/public/services/filter_items.js @@ -10,10 +10,9 @@ export const filterItems = (fields, filter = '', items = []) => { return items.filter(item => { const actualFields = fields || Object.keys(item); const indexOfMatch = actualFields.findIndex(field => { - console.log('GETTING', item, field, filter); const normalizedField = String(get(item, field)).toLowerCase(); console.log('N', normalizedField); - console.log("MATCH?", normalizedField.includes(lowerFilter)); + console.log('N', normalizedField); return normalizedField.includes(lowerFilter); }); return indexOfMatch !== -1; From 0a09b0840381968a9de7264a5c5ec3635968f4ce Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 11:15:20 -0500 Subject: [PATCH 096/102] i18n work --- .../public/components/active_badge.js | 18 +++ .../public/components/index.js | 9 ++ .../components/index_lifecycle_summary.js | 11 +- .../public/components/phase_error_message.js | 22 +++ .../components/cold_phase/cold_phase.js | 74 ++++++---- .../components/delete_phase/delete_phase.js | 66 ++++++--- .../components/hot_phase/hot_phase.js | 77 ++++++---- .../components/warm_phase/warm_phase.js | 132 +++++++++++++----- .../sections/edit_policy/edit_policy.js | 57 ++++++-- .../public/store/selectors/lifecycle.js | 57 ++++---- 10 files changed, 379 insertions(+), 144 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/active_badge.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/index.js create mode 100644 x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js diff --git a/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js b/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js new file mode 100644 index 0000000000000..4696a9a96d5fb --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js @@ -0,0 +1,18 @@ +/* + * 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 { EuiBadge } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +export const ActiveBadge = () => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index.js b/x-pack/plugins/index_lifecycle_management/public/components/index.js new file mode 100644 index 0000000000000..9e77b35f5df41 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/index.js @@ -0,0 +1,9 @@ +/* + * 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 { ActiveBadge } from './active_badge'; +export { LearnMoreLink } from './learn_more_link'; +export { PhaseErrorMessage } from './phase_error_message'; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js index 4e38932dab42d..4ecd485c87be5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js +++ b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js @@ -15,7 +15,7 @@ import { EuiSpacer, EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { i18n, FormattedMessage } from '@kbn/i18n'; const HEADERS = { policy: i18n.translate('xpack.indexLifecycleMgmt.summary.headers.lifecyclePolicyHeader', { defaultMessage: 'Lifecycle policy', @@ -82,7 +82,14 @@ export class IndexLifecycleSummary extends Component { const { left, right } = this.buildRows(); return ( -

Index lifecycle management

+ +

+ +

+
diff --git a/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js b/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js new file mode 100644 index 0000000000000..c580443dd7ff6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js @@ -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 React from 'react'; +import { EuiText, EuiTextColor } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +export const PhaseErrorMessage = ({ isShowingErrors }) => { + return isShowingErrors ? ( + + +

+ +

+
+
+ ) : null; +}; \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js index 6ee756c142b61..850a5f51f2605 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js @@ -6,19 +6,17 @@ import React, { PureComponent, Fragment } from 'react'; import PropTypes from 'prop-types'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, - EuiText, - EuiTextColor, EuiFormRow, EuiFieldNumber, EuiSelect, EuiButtonEmpty, EuiDescribedFormGroup, - EuiBadge, EuiButton, } from '@elastic/eui'; import { @@ -31,8 +29,9 @@ import { PHASE_REPLICA_COUNT } from '../../../../store/constants'; import { ErrableFormRow } from '../../form_errors'; +import { ActiveBadge, PhaseErrorMessage } from '../../../../components'; -export class ColdPhase extends PureComponent { +class ColdPhaseUi extends PureComponent { static propTypes = { setPhaseData: PropTypes.func.isRequired, showNodeDetailsFlyout: PropTypes.func.isRequired, @@ -69,16 +68,22 @@ export class ColdPhase extends PureComponent { nodeOptions, warmPhaseReplicaCount, errors, - isShowingErrors + isShowingErrors, + intl } = this.props; return ( - Cold phase{' '} + + + {' '} {phaseData[PHASE_ENABLED] ? ( - Active + ) : null}
} @@ -86,16 +91,12 @@ export class ColdPhase extends PureComponent { description={

- A cold index is queried less frequently - and thus no longer needs to be on the most performant hardware. +

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} +
} fullWidth @@ -111,7 +112,10 @@ export class ColdPhase extends PureComponent { await setPhaseData(PHASE_ENABLED, false); }} > - Deactive cold phase +
@@ -120,7 +124,10 @@ export class ColdPhase extends PureComponent { @@ -154,7 +167,10 @@ export class ColdPhase extends PureComponent { showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > - View a list of nodes attached to this configuration + ) : null} > @@ -181,7 +200,10 @@ export class ColdPhase extends PureComponent { - Activate cold phase + )} @@ -226,3 +251,4 @@ export class ColdPhase extends PureComponent { ); } } +export const ColdPhase = injectI18n(ColdPhaseUi); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js index 7c4a179ea8935..d5c7da95483a8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js @@ -8,6 +8,7 @@ import React, { PureComponent, Fragment } from 'react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import PropTypes from 'prop-types'; import { @@ -15,13 +16,10 @@ import { EuiFlexItem, EuiTitle, EuiSpacer, - EuiText, - EuiTextColor, EuiFormRow, EuiFieldNumber, EuiSelect, EuiDescribedFormGroup, - EuiBadge, EuiButton, } from '@elastic/eui'; import { @@ -31,8 +29,9 @@ import { PHASE_ROLLOVER_MINIMUM_AGE_UNITS, } from '../../../../store/constants'; import { ErrableFormRow } from '../../form_errors'; +import { ActiveBadge, PhaseErrorMessage } from '../../../../components'; -export class DeletePhase extends PureComponent { +class DeletePhaseUi extends PureComponent { static propTypes = { setPhaseData: PropTypes.func.isRequired, isShowingErrors: PropTypes.bool.isRequired, @@ -52,16 +51,22 @@ export class DeletePhase extends PureComponent { setPhaseData, phaseData, errors, - isShowingErrors + isShowingErrors, + intl } = this.props; return ( - Delete phase{' '} + + + {' '} {phaseData[PHASE_ENABLED] ? ( - Active + ) : null} } @@ -69,15 +74,13 @@ export class DeletePhase extends PureComponent { description={

- Use this phase to define how long to retain your data. + +

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} +
} fullWidth @@ -93,20 +96,31 @@ export class DeletePhase extends PureComponent { await setPhaseData(PHASE_ENABLED, false); }} > - Deactive delete phase + -

Configuration

+

+ +

@@ -144,7 +164,10 @@ export class DeletePhase extends PureComponent { await setPhaseData(PHASE_ENABLED, true); }} > - Activate delete phase + )} @@ -152,3 +175,4 @@ export class DeletePhase extends PureComponent { ); } } +export const DeletePhase = injectI18n(DeletePhaseUi); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js index ef88d4cae15ab..64be10ef4fc09 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js @@ -5,22 +5,20 @@ */ import React, { Fragment, PureComponent } from 'react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, - EuiText, - EuiTextColor, EuiFieldNumber, EuiSelect, EuiSwitch, EuiFormRow, EuiDescribedFormGroup, - EuiBadge, } from '@elastic/eui'; -import { LearnMoreLink } from '../../../../components/learn_more_link'; +import { LearnMoreLink, ActiveBadge, PhaseErrorMessage } from '../../../../components'; import { PHASE_HOT, PHASE_ROLLOVER_ALIAS, @@ -34,7 +32,7 @@ import { import { ErrableFormRow } from '../../form_errors'; -export class HotPhase extends PureComponent { +class HotPhaseUi extends PureComponent { static propTypes = { setPhaseData: PropTypes.func.isRequired, @@ -61,30 +59,33 @@ export class HotPhase extends PureComponent { phaseData, isShowingErrors, errors, + intl } = this.props; return ( - Hot phase{' '} - Active + + + {' '} + } titleSize="s" description={

- This phase is required. A hot index is being queried and actively written to. - You can optimize this phase for write throughput. +

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} +
} fullWidth @@ -93,9 +94,13 @@ export class HotPhase extends PureComponent { hasEmptyLabelSpace helpText={

- If true, rollover the index when it gets too big or too old. The alias switches to the new index.{' '} + + {' '}

} @@ -105,7 +110,10 @@ export class HotPhase extends PureComponent { onChange={async e => { await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); }} - label="Enable rollover" + label={intl.formatMessage({ + id: 'xpack.idxLifecycleMgmt.hotPhase.enableRolloverLabel', + defaultMessage: 'Enable rollover' + })} /> {phaseData[PHASE_ROLLOVER_ENABLED] ? ( @@ -115,7 +123,10 @@ export class HotPhase extends PureComponent { @@ -161,7 +178,10 @@ export class HotPhase extends PureComponent { @@ -205,3 +231,4 @@ export class HotPhase extends PureComponent { ); } } +export const HotPhase = injectI18n(HotPhaseUi); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js index dc3768451330d..6ef2189f9be9f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js @@ -5,21 +5,20 @@ */ import React, { Fragment, PureComponent } from 'react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import PropTypes from 'prop-types'; import { + EuiTextColor, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, - EuiText, - EuiTextColor, EuiFormRow, EuiFieldNumber, EuiSelect, EuiSwitch, EuiButtonEmpty, EuiDescribedFormGroup, - EuiBadge, EuiButton, } from '@elastic/eui'; import { @@ -37,9 +36,9 @@ import { PHASE_SHRINK_ENABLED, } from '../../../../store/constants'; import { ErrableFormRow } from '../../form_errors'; -import { LearnMoreLink } from '../../../../components/learn_more_link'; +import { LearnMoreLink, ActiveBadge, PhaseErrorMessage } from '../../../../components'; -export class WarmPhase extends PureComponent { +class WarmPhaseUi extends PureComponent { static propTypes = { setPhaseData: PropTypes.func.isRequired, showNodeDetailsFlyout: PropTypes.func.isRequired, @@ -76,15 +75,21 @@ export class WarmPhase extends PureComponent { errors, isShowingErrors, hotPhaseRolloverEnabled, + intl } = this.props; return ( - Warm phase{' '} + + + {' '} {phaseData[PHASE_ENABLED] ? ( - Active + ) : null} } @@ -92,16 +97,12 @@ export class WarmPhase extends PureComponent { description={

- Your index becomes read-only when it enters the warm phase. You can optimize this - phase for search. +

- {isShowingErrors ? ( - - -

This phase contains errors

-
-
- ) : null} +
} fullWidth @@ -117,14 +118,25 @@ export class WarmPhase extends PureComponent { await setPhaseData(PHASE_ENABLED, false); }} > - Deactivate warm phase + {hotPhaseRolloverEnabled ? ( - + { await setPhaseData(WARM_PHASE_ON_ROLLOVER, e.target.checked); @@ -137,7 +149,10 @@ export class WarmPhase extends PureComponent { @@ -172,7 +193,10 @@ export class WarmPhase extends PureComponent { showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > - View node details + ) : null } @@ -200,7 +227,10 @@ export class WarmPhase extends PureComponent { -

Shrink

+

+ +

- Shrink the index into a new index with fewer primary shards.{' '} + {' '} @@ -236,7 +274,10 @@ export class WarmPhase extends PureComponent { onChange={async e => { await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); }} - label="Shrink index" + label={intl.formatMessage({ + id: 'xpack.idxLifecycleMgmt.warmPhase.shrinkIndexLabel', + defaultMessage: 'Shrink index' + })} /> {phaseData[PHASE_SHRINK_ENABLED] ? ( @@ -245,7 +286,10 @@ export class WarmPhase extends PureComponent { -

Force merge

+

+ +

- Reduce the number of segments in your shard by merging smaller files and clearing - deleted ones. + + {' '} { await setPhaseData(PHASE_FORCE_MERGE_ENABLED, e.target.checked); @@ -290,7 +345,10 @@ export class WarmPhase extends PureComponent { {phaseData[PHASE_FORCE_MERGE_ENABLED] ? ( - Activate warm phase +
@@ -323,3 +384,4 @@ export class WarmPhase extends PureComponent { ); } } +export const WarmPhase = injectI18n(WarmPhaseUi); diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js index 9715953c53d3b..1d3ddc29f2fd8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js @@ -8,7 +8,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { toastNotifications } from 'ui/notify'; import { goToPolicyList } from '../../services/navigation'; - +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { EuiPage, EuiPageBody, @@ -38,7 +38,7 @@ import { findFirstError } from '../../lib/find_errors'; import { NodeAttrsDetails } from './components/node_attrs_details'; import { ErrableFormRow } from './form_errors'; -export class EditPolicy extends Component { +class EditPolicyUi extends Component { static propTypes = { selectedPolicy: PropTypes.object.isRequired, errors: PropTypes.object.isRequired, @@ -85,6 +85,7 @@ export class EditPolicy extends Component { goToPolicyList(); } submit = async () => { + const { intl } = this.props; this.setState({ isShowingErrors: true }); const { saveLifecyclePolicy, @@ -93,7 +94,10 @@ export class EditPolicy extends Component { firstError } = this.props; if (firstError) { - toastNotifications.addDanger('Please the fix errors on the page'); + toastNotifications.addDanger(intl.formatMessage({ + id: 'xpack.idxLifecycleMgmt.editPolicy.formErrorsMessage', + defaultMessage: 'Please the fix errors on the page' + })); const element = document.getElementById(`${firstError}-row`); if (element) { element.scrollIntoView(); @@ -111,6 +115,7 @@ export class EditPolicy extends Component { }; render() { const { + intl, selectedPolicy, errors, match: { @@ -135,8 +140,14 @@ export class EditPolicy extends Component {

{isNewPolicy - ? 'Create an index lifecycle policy' - : `Edit index lifecycle policy ${selectedPolicyName}`} + ? intl.formatMessage({ + id: 'xpack.idxLifecycleMgmt.editPolicy.createPolicyMessage', + defaultMessage: 'Create an index lifecycle policy' + }) + : intl.formatMessage({ + id: 'xpack.idxLifecycleMgmt.editPolicy.editPolicyMessage', + defaultMessage: 'Edit index lifecycle policy {selectedPolicyName}', + }, { selectedPolicyName }) }

@@ -151,10 +162,17 @@ export class EditPolicy extends Component {

- You are editing an existing policy. Any changes you make - will also change index templates that this policy is attached to. - Alternately, you can save these changes in a new policy and only change - the index template you selected. + + + .{' '} +

@@ -169,7 +187,10 @@ export class EditPolicy extends Component { }} label={ - Save this as a new policy + } /> @@ -181,7 +202,10 @@ export class EditPolicy extends Component { - Cancel +    - Save your policy + {this.state.isShowingNodeDetailsFlyout ? ( { const phaseErrors = {}; @@ -46,10 +50,14 @@ export const validatePhase = (type, phase, errors) => { !isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED]) ) { phaseErrors[PHASE_ROLLOVER_MAX_AGE] = [ - 'A maximum age is required' + i18n.translate('xpack.idxLifecycleMgmt.editPolicy.maximumAgeMissingError', { + defaultMessage: 'A maximum age is required' + }) ]; phaseErrors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ - 'A maximum index size is required' + i18n.translate('xpack.idxLifecycleMgmt.editPolicy.maximumIndexSizeMissingError', { + defaultMessage: 'A maximum index size is required' + }) ]; } } @@ -61,32 +69,36 @@ export const validatePhase = (type, phase, errors) => { continue; } if (!isNumber(phase[numberedAttribute])) { - phaseErrors[numberedAttribute] = ['A number is required']; + phaseErrors[numberedAttribute] = [numberRequiredMessage]; } else if (phase[numberedAttribute] < 0) { - phaseErrors[numberedAttribute] = ['Only positive numbers are allowed']; + phaseErrors[numberedAttribute] = [positiveNumberRequiredMessage]; } else if (numberedAttribute === PHASE_PRIMARY_SHARD_COUNT && phase[numberedAttribute] < 1) { - phaseErrors[numberedAttribute] = ['Only positive numbers are allowed']; + phaseErrors[numberedAttribute] = [positiveNumberRequiredMessage]; } } } if (phase[PHASE_SHRINK_ENABLED]) { if (!isNumber(phase[PHASE_PRIMARY_SHARD_COUNT])) { - phaseErrors[PHASE_PRIMARY_SHARD_COUNT] = ['A number is required.']; + phaseErrors[PHASE_PRIMARY_SHARD_COUNT] = [numberRequiredMessage]; } else if (phase[PHASE_PRIMARY_SHARD_COUNT] < 1) { - phaseErrors[PHASE_PRIMARY_SHARD_COUNT] = ['Only positive numbers are allowed.']; + phaseErrors[PHASE_PRIMARY_SHARD_COUNT] = [positiveNumberRequiredMessage]; } } if (phase[PHASE_FORCE_MERGE_ENABLED]) { if (!isNumber(phase[PHASE_FORCE_MERGE_SEGMENTS])) { - phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = ['A number is required.']; + phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = [numberRequiredMessage]; } else if (phase[PHASE_FORCE_MERGE_SEGMENTS] < 1) { - phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = ['Only positive numbers above 0 are allowed.']; + phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = [ + i18n.translate('xpack.idxLifecycleMgmt.editPolicy.positiveNumberAboveZeroRequiredError', { + defaultMessage: 'Only positive numbers above 0 are allowed' + }) + ]; } } errors[type] = { @@ -100,17 +112,23 @@ export const validateLifecycle = state => { const errors = JSON.parse(JSON.stringify(ERROR_STRUCTURE)); if (!getSelectedPolicyName(state)) { - errors[STRUCTURE_POLICY_NAME].push('A policy name is required'); + errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.idxLifecycleMgmt.editPolicy.policyNameRequiredError', { + defaultMessage: 'A policy name is required' + })); } if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { - errors[STRUCTURE_POLICY_NAME].push('The policy name must be different'); + errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.idxLifecycleMgmt.editPolicy.differentPolicyNameRequiredError', { + defaultMessage: 'The policy name must be different' + })); } if (getSaveAsNewPolicy(state)) { const policyNames = getPolicies(state).map(policy => policy.name); if (policyNames.includes(getSelectedPolicyName(state))) { - errors[STRUCTURE_POLICY_NAME].push('That policy name is already used.'); + errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.idxLifecycleMgmt.editPolicy.policyNameAlreadyUsedError', { + defaultMessage: 'That policy name is already used' + })); } } @@ -123,15 +141,6 @@ export const validateLifecycle = state => { validatePhase(PHASE_WARM, warmPhase, errors); validatePhase(PHASE_COLD, coldPhase, errors); validatePhase(PHASE_DELETE, deletePhase, errors); - if (warmPhase[PHASE_SHRINK_ENABLED]) { - if (isNumber(warmPhase[PHASE_PRIMARY_SHARD_COUNT]) && warmPhase[PHASE_PRIMARY_SHARD_COUNT] > 0) { - if (getSelectedPrimaryShardCount(state) % warmPhase[PHASE_PRIMARY_SHARD_COUNT] !== 0) { - errors[PHASE_WARM][PHASE_PRIMARY_SHARD_COUNT].push( - 'The shard count needs to be a divisor of the hot phase shard count.' - ); - } - } - } return errors; }; From d3e50a708ece5d79e56538be5ec7c0709486c7c1 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 11:54:01 -0500 Subject: [PATCH 097/102] fixing error wrappers --- .../server/lib/error_wrappers/wrap_custom_error.js | 2 +- .../server/lib/error_wrappers/wrap_es_error.js | 4 ++-- .../server/lib/error_wrappers/wrap_unknown_error.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js index 890a366ac65c1..3295113d38ee5 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_custom_error.js @@ -14,5 +14,5 @@ import Boom from 'boom'; * @return Object Boom error response */ export function wrapCustomError(err, statusCode) { - return Boom.wrap(err, statusCode); + return Boom.boomify(err, { statusCode }); } diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js index 9f1328219bab2..2df2e4b802e1a 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_es_error.js @@ -20,11 +20,11 @@ export function wrapEsError(err, statusCodeToMessageMap = {}) { // If no custom message if specified for the error's status code, just // wrap the error as a Boom error response and return it if (!statusCodeToMessageMap[statusCode]) { - return Boom.boomify(err, err.statusCode); + return Boom.boomify(err, { statusCode }); } // Otherwise, use the custom message to create a Boom error response and // return it const message = statusCodeToMessageMap[statusCode]; - return Boom.create(statusCode, message); + return new Boom(message, { statusCode }); } diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js index b0cdced7adbef..4b865880ae20d 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/error_wrappers/wrap_unknown_error.js @@ -13,5 +13,5 @@ import Boom from 'boom'; * @return Object Boom error response */ export function wrapUnknownError(err) { - return Boom.wrap(err); -} + return Boom.boomify(err); +} \ No newline at end of file From 01256bdceeba8013caac0d8c4939bcc7ea602099 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 12:42:09 -0500 Subject: [PATCH 098/102] fixing tests --- .../__tests__/license_pre_routing_factory.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js index b72f8cc769731..359b3fb2ce6f4 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js @@ -40,14 +40,13 @@ describe('license_pre_routing_factory', () => { }; }); - it ('replies with 403', (done) => { + it ('replies with 403', () => { const licensePreRouting = licensePreRoutingFactory(mockServer); const stubRequest = {}; - licensePreRouting(stubRequest, (response) => { + expect(() => licensePreRouting(stubRequest)).to.throwException((response) => { expect(response).to.be.an(Error); expect(response.isBoom).to.be(true); expect(response.output.statusCode).to.be(403); - done(); }); }); }); @@ -59,13 +58,11 @@ describe('license_pre_routing_factory', () => { }; }); - it ('replies with nothing', (done) => { + it ('replies with nothing', () => { const licensePreRouting = licensePreRoutingFactory(mockServer); const stubRequest = {}; - licensePreRouting(stubRequest, (response) => { - expect(response).to.be(undefined); - done(); - }); + const response = licensePreRouting(stubRequest); + expect(response).to.be(null); }); }); }); From 25e0fc1bf8479a218a1ff2fd8b2bfdac51c61913 Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 13:24:10 -0500 Subject: [PATCH 099/102] adding view JSON for index lifecycle policy on edit screen --- .../public/components/active_badge.js | 2 +- .../components/index_lifecycle_summary.js | 2 +- .../public/components/phase_error_message.js | 2 +- .../register_index_lifecycle_banner.js | 2 +- .../components/cold_phase/cold_phase.js | 20 +++--- .../components/delete_phase/delete_phase.js | 16 ++--- .../components/hot_phase/hot_phase.js | 20 +++--- .../components/policy_json_flyout.js | 66 +++++++++++++++++++ .../components/warm_phase/warm_phase.js | 40 +++++------ .../sections/edit_policy/edit_policy.js | 37 ++++++++--- .../public/store/selectors/lifecycle.js | 16 ++--- 11 files changed, 154 insertions(+), 69 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/policy_json_flyout.js diff --git a/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js b/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js index 4696a9a96d5fb..4a270c9e002f0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js +++ b/x-pack/plugins/index_lifecycle_management/public/components/active_badge.js @@ -10,7 +10,7 @@ export const ActiveBadge = () => { return ( diff --git a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js index 4ecd485c87be5..77f87be1da824 100644 --- a/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js +++ b/x-pack/plugins/index_lifecycle_management/public/components/index_lifecycle_summary.js @@ -86,7 +86,7 @@ export class IndexLifecycleSummary extends Component {

diff --git a/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js b/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js index c580443dd7ff6..3466f09168b0a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js +++ b/x-pack/plugins/index_lifecycle_management/public/components/phase_error_message.js @@ -12,7 +12,7 @@ export const PhaseErrorMessage = ({ isShowingErrors }) => {

diff --git a/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js index 32ef838e7b0e2..519c9b78518b4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_index_management_extensions/register_index_lifecycle_banner.js @@ -22,7 +22,7 @@ addBannerExtension((indices) =>{ return { type: 'warning', filter: `${stepPath}:ERROR`, - message: i18n.translate('xpack.idxLifecycleMgmt.indexMgmtBanner.errorMessage', { + message: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtBanner.errorMessage', { defaultMessage: `{ numIndicesWithLifecycleErrors, number} {numIndicesWithLifecycleErrors, plural, one {index has} other {indices have} } lifecycle errors`, diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js index 850a5f51f2605..92973ad3a0e38 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/cold_phase/cold_phase.js @@ -78,7 +78,7 @@ class ColdPhaseUi extends PureComponent {
{' '} @@ -92,7 +92,7 @@ class ColdPhaseUi extends PureComponent {

@@ -113,7 +113,7 @@ class ColdPhaseUi extends PureComponent { }} > @@ -125,7 +125,7 @@ class ColdPhaseUi extends PureComponent { showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > @@ -201,7 +201,7 @@ class ColdPhaseUi extends PureComponent { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js index d5c7da95483a8..d15d794a277bc 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/delete_phase/delete_phase.js @@ -61,7 +61,7 @@ class DeletePhaseUi extends PureComponent {
{' '} @@ -75,7 +75,7 @@ class DeletePhaseUi extends PureComponent {

@@ -97,7 +97,7 @@ class DeletePhaseUi extends PureComponent { }} > @@ -107,7 +107,7 @@ class DeletePhaseUi extends PureComponent {

@@ -118,7 +118,7 @@ class DeletePhaseUi extends PureComponent { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js index 64be10ef4fc09..19fcb8507b8d5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/hot_phase/hot_phase.js @@ -68,7 +68,7 @@ class HotPhaseUi extends PureComponent {
{' '} @@ -80,7 +80,7 @@ class HotPhaseUi extends PureComponent {

@@ -95,7 +95,7 @@ class HotPhaseUi extends PureComponent { helpText={

{' '} @@ -111,7 +111,7 @@ class HotPhaseUi extends PureComponent { await setPhaseData(PHASE_ROLLOVER_ENABLED, e.target.checked); }} label={intl.formatMessage({ - id: 'xpack.idxLifecycleMgmt.hotPhase.enableRolloverLabel', + id: 'xpack.indexLifecycleMgmt.hotPhase.enableRolloverLabel', defaultMessage: 'Enable rollover' })} /> @@ -124,7 +124,7 @@ class HotPhaseUi extends PureComponent { + + + +

+ +

+ + + + + + + + + + + + ); + } +} +export const PolicyJsonFlyout = injectI18n(PolicyJsonFlyoutUi); + diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js index 6ef2189f9be9f..abc012ca590df 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/warm_phase/warm_phase.js @@ -84,7 +84,7 @@ class WarmPhaseUi extends PureComponent {
{' '} @@ -98,7 +98,7 @@ class WarmPhaseUi extends PureComponent {

@@ -119,7 +119,7 @@ class WarmPhaseUi extends PureComponent { }} > @@ -128,13 +128,13 @@ class WarmPhaseUi extends PureComponent { {hotPhaseRolloverEnabled ? ( showNodeDetailsFlyout(phaseData[PHASE_NODE_ATTRS])} > @@ -228,7 +228,7 @@ class WarmPhaseUi extends PureComponent {

@@ -260,7 +260,7 @@ class WarmPhaseUi extends PureComponent { {' '} @@ -275,7 +275,7 @@ class WarmPhaseUi extends PureComponent { await setPhaseData(PHASE_SHRINK_ENABLED, e.target.checked); }} label={intl.formatMessage({ - id: 'xpack.idxLifecycleMgmt.warmPhase.shrinkIndexLabel', + id: 'xpack.indexLifecycleMgmt.warmPhase.shrinkIndexLabel', defaultMessage: 'Shrink index' })} /> @@ -287,7 +287,7 @@ class WarmPhaseUi extends PureComponent {

@@ -320,7 +320,7 @@ class WarmPhaseUi extends PureComponent { {' '} @@ -331,7 +331,7 @@ class WarmPhaseUi extends PureComponent { diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js index 1d3ddc29f2fd8..d81f517fce816 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/edit_policy.js @@ -36,6 +36,7 @@ import { } from '../../store/constants'; import { findFirstError } from '../../lib/find_errors'; import { NodeAttrsDetails } from './components/node_attrs_details'; +import { PolicyJsonFlyout } from './components/policy_json_flyout'; import { ErrableFormRow } from './form_errors'; class EditPolicyUi extends Component { @@ -50,6 +51,7 @@ class EditPolicyUi extends Component { isShowingErrors: false, isShowingNodeDetailsFlyout: false, selectedNodeAttrsForDetails: undefined, + isShowingPolicyJsonFlyout: false }; } selectPolicy = policyName => { @@ -95,7 +97,7 @@ class EditPolicyUi extends Component { } = this.props; if (firstError) { toastNotifications.addDanger(intl.formatMessage({ - id: 'xpack.idxLifecycleMgmt.editPolicy.formErrorsMessage', + id: 'xpack.indexLifecycleMgmt.editPolicy.formErrorsMessage', defaultMessage: 'Please the fix errors on the page' })); const element = document.getElementById(`${firstError}-row`); @@ -113,6 +115,9 @@ class EditPolicyUi extends Component { showNodeDetailsFlyout = selectedNodeAttrsForDetails => { this.setState({ isShowingNodeDetailsFlyout: true, selectedNodeAttrsForDetails }); }; + showPolicyJsonFlyout = () => { + this.setState({ isShowingPolicyJsonFlyout: true }); + }; render() { const { intl, @@ -125,6 +130,7 @@ class EditPolicyUi extends Component { saveAsNewPolicy, setSelectedPolicyName, isNewPolicy, + lifecycle } = this.props; const selectedPolicyName = selectedPolicy.name; const { isShowingErrors } = this.state; @@ -141,11 +147,11 @@ class EditPolicyUi extends Component {

{isNewPolicy ? intl.formatMessage({ - id: 'xpack.idxLifecycleMgmt.editPolicy.createPolicyMessage', + id: 'xpack.indexLifecycleMgmt.editPolicy.createPolicyMessage', defaultMessage: 'Create an index lifecycle policy' }) : intl.formatMessage({ - id: 'xpack.idxLifecycleMgmt.editPolicy.editPolicyMessage', + id: 'xpack.indexLifecycleMgmt.editPolicy.editPolicyMessage', defaultMessage: 'Edit index lifecycle policy {selectedPolicyName}', }, { selectedPolicyName }) }

@@ -164,12 +170,12 @@ class EditPolicyUi extends Component {

.{' '} @@ -188,7 +194,7 @@ class EditPolicyUi extends Component { label={ @@ -203,7 +209,7 @@ class EditPolicyUi extends Component {    + + + {this.state.isShowingNodeDetailsFlyout ? ( this.setState({ isShowingNodeDetailsFlyout: false })} /> ) : null} + {this.state.isShowingPolicyJsonFlyout ? ( + this.setState({ isShowingPolicyJsonFlyout: false })} + /> + ) : null}

diff --git a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js index 782d2747c21ca..9d44cd4ae5b5e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js +++ b/x-pack/plugins/index_lifecycle_management/public/store/selectors/lifecycle.js @@ -31,10 +31,10 @@ import { getSelectedOriginalPolicyName, getPolicies } from '.'; -const numberRequiredMessage = i18n.translate('xpack.idxLifecycleMgmt.editPolicy.numberRequiredError', { +const numberRequiredMessage = i18n.translate('xpack.indexLifecycleMgmt.editPolicy.numberRequiredError', { defaultMessage: 'A number is required' }); -const positiveNumberRequiredMessage = i18n.translate('xpack.idxLifecycleMgmt.editPolicy.positiveNumberRequiredError', { +const positiveNumberRequiredMessage = i18n.translate('xpack.indexLifecycleMgmt.editPolicy.positiveNumberRequiredError', { defaultMessage: 'Only positive numbers are allowed' }); export const validatePhase = (type, phase, errors) => { @@ -50,12 +50,12 @@ export const validatePhase = (type, phase, errors) => { !isNumber(phase[PHASE_ROLLOVER_MAX_SIZE_STORED]) ) { phaseErrors[PHASE_ROLLOVER_MAX_AGE] = [ - i18n.translate('xpack.idxLifecycleMgmt.editPolicy.maximumAgeMissingError', { + i18n.translate('xpack.indexLifecycleMgmt.editPolicy.maximumAgeMissingError', { defaultMessage: 'A maximum age is required' }) ]; phaseErrors[PHASE_ROLLOVER_MAX_SIZE_STORED] = [ - i18n.translate('xpack.idxLifecycleMgmt.editPolicy.maximumIndexSizeMissingError', { + i18n.translate('xpack.indexLifecycleMgmt.editPolicy.maximumIndexSizeMissingError', { defaultMessage: 'A maximum index size is required' }) ]; @@ -95,7 +95,7 @@ export const validatePhase = (type, phase, errors) => { } else if (phase[PHASE_FORCE_MERGE_SEGMENTS] < 1) { phaseErrors[PHASE_FORCE_MERGE_SEGMENTS] = [ - i18n.translate('xpack.idxLifecycleMgmt.editPolicy.positiveNumberAboveZeroRequiredError', { + i18n.translate('xpack.indexLifecycleMgmt.editPolicy.positiveNumberAboveZeroRequiredError', { defaultMessage: 'Only positive numbers above 0 are allowed' }) ]; @@ -112,13 +112,13 @@ export const validateLifecycle = state => { const errors = JSON.parse(JSON.stringify(ERROR_STRUCTURE)); if (!getSelectedPolicyName(state)) { - errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.idxLifecycleMgmt.editPolicy.policyNameRequiredError', { + errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.indexLifecycleMgmt.editPolicy.policyNameRequiredError', { defaultMessage: 'A policy name is required' })); } if (getSaveAsNewPolicy(state) && getSelectedOriginalPolicyName(state) === getSelectedPolicyName(state)) { - errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.idxLifecycleMgmt.editPolicy.differentPolicyNameRequiredError', { + errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.indexLifecycleMgmt.editPolicy.differentPolicyNameRequiredError', { defaultMessage: 'The policy name must be different' })); } @@ -126,7 +126,7 @@ export const validateLifecycle = state => { if (getSaveAsNewPolicy(state)) { const policyNames = getPolicies(state).map(policy => policy.name); if (policyNames.includes(getSelectedPolicyName(state))) { - errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.idxLifecycleMgmt.editPolicy.policyNameAlreadyUsedError', { + errors[STRUCTURE_POLICY_NAME].push(i18n.translate('xpack.indexLifecycleMgmt.editPolicy.policyNameAlreadyUsedError', { defaultMessage: 'That policy name is already used' })); } From 562acc5fbdc11575a2cbc16a322298995365f90a Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 13:25:14 -0500 Subject: [PATCH 100/102] fixing label for i18n on policy JSON flyout --- .../sections/edit_policy/components/policy_json_flyout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/policy_json_flyout.js b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/policy_json_flyout.js index a61611db6cb06..cd8ddfd372551 100644 --- a/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/policy_json_flyout.js +++ b/x-pack/plugins/index_lifecycle_management/public/sections/edit_policy/components/policy_json_flyout.js @@ -52,7 +52,7 @@ export class PolicyJsonFlyoutUi extends PureComponent { From a40716af22ff2e3792fda09c188d1ed4f0685efa Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 13:27:01 -0500 Subject: [PATCH 101/102] removing console.log statements --- .../index_lifecycle_management/public/lib/find_errors.js | 5 ----- .../index_lifecycle_management/public/register_routes.js | 1 - 2 files changed, 6 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js index 6a95326ee85c9..7b77cf7a1301c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js +++ b/x-pack/plugins/index_lifecycle_management/public/lib/find_errors.js @@ -5,25 +5,20 @@ */ export const findFirstError = (object, topLevel = true) => { - console.log("ERRORS", object); - let firstError; const keys = topLevel ? [ 'policyName', 'hot', 'warm', 'cold', 'delete'] : Object.keys(object); for (const key of keys) { const value = object[key]; if (Array.isArray(value) && value.length > 0) { firstError = key; - console.log(`Found error ${firstError}`); break; } else if (value) { firstError = findFirstError(value, false); if (firstError) { firstError = `${key}.${firstError}`; - console.log(`Found error ${firstError}`); break; } } } - console.log(`Returning ${firstError}`); return firstError; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/register_routes.js b/x-pack/plugins/index_lifecycle_management/public/register_routes.js index 27a26b98f503b..ebb37c9b76a51 100644 --- a/x-pack/plugins/index_lifecycle_management/public/register_routes.js +++ b/x-pack/plugins/index_lifecycle_management/public/register_routes.js @@ -37,7 +37,6 @@ if (chrome.getInjected('indexLifecycleManagementUiEnabled')) { controllerAs: 'indexLifecycleManagement', controller: class IndexLifecycleManagementController { constructor($scope, $route, $http, kbnUrl, $rootScope) { - console.log("ILM"); setHttpClient($http); setUrlService({ change(url) { From 03c439738b1650ea838d0da720866db307a77aac Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Mon, 12 Nov 2018 13:33:41 -0500 Subject: [PATCH 102/102] fixing tests --- .../license_pre_routing_factory.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js index abfc7fc24dcc3..11e01304b6e5c 100644 --- a/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js +++ b/x-pack/plugins/index_lifecycle_management/server/lib/license_pre_routing_factory/license_pre_routing_factory.js @@ -17,9 +17,9 @@ export const licensePreRoutingFactory = once((server) => { if (!licenseCheckResults.isAvailable) { const error = new Error(licenseCheckResults.message); const statusCode = 403; - const wrappedError = wrapCustomError(error, statusCode); - return wrappedError; + throw wrapCustomError(error, statusCode); } + return null; }