From 4fd37141ed12146dd6e9eaee116e7393f9317c2c Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 3 Mar 2020 16:05:59 -0500 Subject: [PATCH 01/17] init commit: support for proxy mode --- .../remote_clusters/common/constants.ts | 3 + .../common/lib/cluster_serialization.ts | 56 ++++- .../remote_cluster_form.js | 216 +++++++++++++++--- .../detail_panel/detail_panel.js | 3 +- .../remote_cluster_table.js | 2 +- .../server/routes/api/add_route.ts | 40 +++- 6 files changed, 283 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 3521b7f662fc94..cbb9961e3aeb98 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -21,3 +21,6 @@ export const PLUGIN = { }; export const API_BASE_PATH = '/api/remote_clusters'; + +export const SNIFF_MODE = 'sniff'; +export const PROXY_MODE = 'proxy'; diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 07ea79d42b8006..37527849ce1373 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { PROXY_MODE } from '../constants'; + export function deserializeCluster(name: string, esClusterObject: any): any { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); @@ -11,22 +13,45 @@ export function deserializeCluster(name: string, esClusterObject: any): any { const { seeds, + mode, connected: isConnected, num_nodes_connected: connectedNodesCount, max_connections_per_cluster: maxConnectionsPerCluster, initial_connect_timeout: initialConnectTimeout, skip_unavailable: skipUnavailable, transport, + address: proxyAddress, + // TODO: finish + // node_connections: nodeConnections, + // node, + // proxy_socket_connections: proxySocketConnections, + // server_name: serverName, } = esClusterObject; + let connectionModeSettings; + if (mode === PROXY_MODE) { + connectionModeSettings = { + proxyAddress, + // proxySocketConnections, + // serverName, + }; + } else { + connectionModeSettings = { + seeds, + // nodeConnections, + // nodeAttribute: node?.attr ?? null, + }; + } + let deserializedClusterObject: any = { name, - seeds, + mode, isConnected, connectedNodesCount, maxConnectionsPerCluster, initialConnectTimeout, skipUnavailable, + ...connectionModeSettings, }; if (transport) { @@ -54,15 +79,40 @@ export function serializeCluster(deserializedClusterObject: any): any { throw new Error('Unable to serialize cluster'); } - const { name, seeds, skipUnavailable } = deserializedClusterObject; + const { + name, + seeds, + skipUnavailable, + mode, + nodeConnections, + proxyAddress, + proxySocketConnections, + serverName, + } = deserializedClusterObject; + + let connectionModeSettings; + + if (mode === PROXY_MODE) { + connectionModeSettings = { + proxy_address: proxyAddress ?? null, + proxy_socket_connections: proxySocketConnections ?? null, + server_name: serverName ?? null, + }; + } else { + connectionModeSettings = { + seeds: seeds ?? null, + node_connections: nodeConnections ?? null, + }; + } return { persistent: { cluster: { remote: { [name]: { - seeds: seeds ? seeds : null, skip_unavailable: skipUnavailable !== undefined ? skipUnavailable : null, + mode, + ...connectionModeSettings, }, }, }, diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 08cd01496a8b9e..a4d8bb4ea60b56 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -15,6 +15,7 @@ import { EuiCallOut, EuiComboBox, EuiDescribedFormGroup, + EuiFieldNumber, EuiFieldText, EuiFlexGroup, EuiFlexItem, @@ -39,10 +40,17 @@ import { RequestFlyout } from './request_flyout'; import { validateName, validateSeeds, validateSeed } from './validators'; +import { SNIFF_MODE, PROXY_MODE } from '../../../../../common/constants'; + const defaultFields = { name: '', seeds: [], skipUnavailable: false, + mode: SNIFF_MODE, + nodeConnections: 3, + proxyAddress: '', + proxySocketConnections: 18, + serverName: '', }; const ERROR_TITLE_ID = 'removeClustersErrorTitle'; @@ -88,10 +96,10 @@ export class RemoteClusterForm extends Component { }; getFieldsErrors(fields, seedInput = '') { - const { name, seeds } = fields; + const { name, seeds, mode } = fields; return { name: validateName(name), - seeds: validateSeeds(seeds, seedInput), + seeds: mode === SNIFF_MODE ? validateSeeds(seeds, seedInput) : null, }; } @@ -110,13 +118,38 @@ export class RemoteClusterForm extends Component { getAllFields() { const { - fields: { name, seeds, skipUnavailable }, + fields: { + name, + mode, + seeds, + nodeConnections, + proxyAddress, + proxySocketConnections, + serverName, + skipUnavailable, + }, } = this.state; + let modeSettings; + + if (mode === PROXY_MODE) { + modeSettings = { + proxyAddress, + proxySocketConnections, + serverName, + }; + } else { + modeSettings = { + seeds, + nodeConnections, + }; + } + return { name, - seeds, skipUnavailable, + mode, + ...modeSettings, }; } @@ -215,10 +248,10 @@ export class RemoteClusterForm extends Component { return hasErrors; }; - renderSeeds() { + renderSniffModeSettings() { const { areErrorsVisible, - fields: { seeds }, + fields: { seeds, nodeConnections }, fieldsErrors: { seeds: errorsSeeds }, localSeedErrors, } = this.state; @@ -231,26 +264,7 @@ export class RemoteClusterForm extends Component { const formattedSeeds = seeds.map(seed => ({ label: seed })); return ( - -

- -

- - } - description={ - - } - fullWidth - > + <> + + + } + helpText={ + + } + fullWidth + > + this.onFieldsChange({ nodeConnections: e.target.value })} + fullWidth + /> + + + ); + } + + renderProxyModeSettings() { + const { + fields: { proxyAddress, proxySocketConnections, serverName }, + } = this.state; + + return ( + <> + + } + helpText={ + + } + fullWidth + > + this.onFieldsChange({ proxyAddress: e.target.value })} + fullWidth + /> + + + + } + helpText={ + + } + fullWidth + > + this.onFieldsChange({ proxySocketConnections: e.target.value })} + fullWidth + /> + + + } + helpText={ + + } + fullWidth + > + this.onFieldsChange({ serverName: e.target.value })} + fullWidth + /> + + + ); + } + + renderMode() { + const { + fields: { mode }, + } = this.state; + + return ( + +

+ +

+ + } + description={ + <> + + + + } + checked={mode === PROXY_MODE} + onChange={e => + this.onFieldsChange({ mode: e.target.checked ? PROXY_MODE : SNIFF_MODE }) + } + /> + + } + fullWidth + > + {mode === PROXY_MODE ? this.renderProxyModeSettings() : this.renderSniffModeSettings()}
); } @@ -662,7 +822,7 @@ export class RemoteClusterForm extends Component {
- {this.renderSeeds()} + {this.renderMode()} {this.renderSkipUnavailable()} diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 1c8ba372aa745a..5cd51648e63ccd 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -177,7 +177,8 @@ export class DetailPanel extends Component { - {seeds.map(seed => ( + {/* TODO: Fix to support proxy mode */} + {(seeds || []).map(seed => ( {seed} ))} diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js index 62c417b19904ac..fd321a8626ac7b 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js @@ -129,7 +129,7 @@ export class RemoteClusterTable extends Component { defaultMessage: 'Seeds', }), truncateText: true, - render: seeds => seeds.join(', '), + render: seeds => (seeds ? seeds.join(', ') : ''), // TODO Determine better way to support proxy mode }, { field: 'isConnected', diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index e4ede01ca23ea4..9ae3433a701cbd 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -11,15 +11,47 @@ import { RequestHandler } from 'src/core/server'; import { serializeCluster } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; -import { API_BASE_PATH } from '../../../common/constants'; +import { API_BASE_PATH, PROXY_MODE, SNIFF_MODE } from '../../../common/constants'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; import { isEsError } from '../../lib/is_es_error'; import { RouteDependencies } from '../../types'; const bodyValidation = schema.object({ name: schema.string(), - seeds: schema.arrayOf(schema.string()), skipUnavailable: schema.boolean(), + mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), + // The following validation only applies to "sniff" mode + seeds: schema.conditional( + schema.siblingRef('mode'), + SNIFF_MODE, + schema.arrayOf(schema.string()), + schema.never() + ), + nodeConnections: schema.conditional( + schema.siblingRef('mode'), + SNIFF_MODE, + schema.maybe(schema.number()), + schema.never() + ), + // The following validation only applies to "proxy" mode + proxyAddress: schema.conditional( + schema.siblingRef('mode'), + PROXY_MODE, + schema.string(), + schema.never() + ), + proxySocketConnections: schema.conditional( + schema.siblingRef('mode'), + PROXY_MODE, + schema.maybe(schema.number()), + schema.never() + ), + serverName: schema.conditional( + schema.siblingRef('mode'), + PROXY_MODE, + schema.maybe(schema.string()), + schema.never() + ), }); type RouteBody = TypeOf; @@ -33,7 +65,7 @@ export const register = (deps: RouteDependencies): void => { try { const callAsCurrentUser = ctx.core.elasticsearch.dataClient.callAsCurrentUser; - const { name, seeds, skipUnavailable } = request.body; + const { name } = request.body; // Check if cluster already exists. const existingCluster = await doesClusterExist(callAsCurrentUser, name); @@ -50,7 +82,7 @@ export const register = (deps: RouteDependencies): void => { }); } - const addClusterPayload = serializeCluster({ name, seeds, skipUnavailable }); + const addClusterPayload = serializeCluster(request.body); const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body: addClusterPayload, }); From a876d52656a65304b7bc353e165036ca434ca402 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 4 Mar 2020 15:44:06 -0500 Subject: [PATCH 02/17] fix edit remote clusters --- .../common/lib/cluster_serialization.ts | 50 +++++-------------- .../remote_cluster_form.js | 6 ++- .../server/routes/api/update_route.ts | 40 +++++++++++++-- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 37527849ce1373..46a6bb7e11ca73 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PROXY_MODE } from '../constants'; - export function deserializeCluster(name: string, esClusterObject: any): any { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); @@ -21,28 +19,12 @@ export function deserializeCluster(name: string, esClusterObject: any): any { skip_unavailable: skipUnavailable, transport, address: proxyAddress, - // TODO: finish - // node_connections: nodeConnections, - // node, - // proxy_socket_connections: proxySocketConnections, + max_socket_connections: proxySocketConnections, + num_sockets_connected: connectedSocketsCount, + // TODO: Need to support serverName somehow // server_name: serverName, } = esClusterObject; - let connectionModeSettings; - if (mode === PROXY_MODE) { - connectionModeSettings = { - proxyAddress, - // proxySocketConnections, - // serverName, - }; - } else { - connectionModeSettings = { - seeds, - // nodeConnections, - // nodeAttribute: node?.attr ?? null, - }; - } - let deserializedClusterObject: any = { name, mode, @@ -51,7 +33,10 @@ export function deserializeCluster(name: string, esClusterObject: any): any { maxConnectionsPerCluster, initialConnectTimeout, skipUnavailable, - ...connectionModeSettings, + seeds, + proxyAddress, + proxySocketConnections, + connectedSocketsCount, }; if (transport) { @@ -90,21 +75,6 @@ export function serializeCluster(deserializedClusterObject: any): any { serverName, } = deserializedClusterObject; - let connectionModeSettings; - - if (mode === PROXY_MODE) { - connectionModeSettings = { - proxy_address: proxyAddress ?? null, - proxy_socket_connections: proxySocketConnections ?? null, - server_name: serverName ?? null, - }; - } else { - connectionModeSettings = { - seeds: seeds ?? null, - node_connections: nodeConnections ?? null, - }; - } - return { persistent: { cluster: { @@ -112,7 +82,11 @@ export function serializeCluster(deserializedClusterObject: any): any { [name]: { skip_unavailable: skipUnavailable !== undefined ? skipUnavailable : null, mode, - ...connectionModeSettings, + proxy_address: proxyAddress ?? null, + proxy_socket_connections: proxySocketConnections ?? null, + server_name: serverName ?? null, + seeds: seeds ?? null, + node_connections: nodeConnections ?? null, }, }, }, diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index a4d8bb4ea60b56..26baa737039560 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -329,7 +329,7 @@ export class RemoteClusterForm extends Component { > this.onFieldsChange({ nodeConnections: e.target.value })} + onChange={e => this.onFieldsChange({ nodeConnections: Number(e.target.value) || null })} fullWidth /> @@ -385,7 +385,9 @@ export class RemoteClusterForm extends Component { > this.onFieldsChange({ proxySocketConnections: e.target.value })} + onChange={e => + this.onFieldsChange({ proxySocketConnections: Number(e.target.value) || null }) + } fullWidth /> diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index ed584307d84c11..37154045ce6a42 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -9,7 +9,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { RequestHandler } from 'src/core/server'; -import { API_BASE_PATH } from '../../../common/constants'; +import { API_BASE_PATH, SNIFF_MODE, PROXY_MODE } from '../../../common/constants'; import { serializeCluster, deserializeCluster } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; import { RouteDependencies } from '../../types'; @@ -17,8 +17,40 @@ import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory' import { isEsError } from '../../lib/is_es_error'; const bodyValidation = schema.object({ - seeds: schema.arrayOf(schema.string()), skipUnavailable: schema.boolean(), + mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), + // The following validation only applies to "sniff" mode + seeds: schema.conditional( + schema.siblingRef('mode'), + SNIFF_MODE, + schema.arrayOf(schema.string()), + schema.never() + ), + nodeConnections: schema.conditional( + schema.siblingRef('mode'), + SNIFF_MODE, + schema.maybe(schema.number()), + schema.never() + ), + // The following validation only applies to "proxy" mode + proxyAddress: schema.conditional( + schema.siblingRef('mode'), + PROXY_MODE, + schema.string(), + schema.never() + ), + proxySocketConnections: schema.conditional( + schema.siblingRef('mode'), + PROXY_MODE, + schema.maybe(schema.number()), + schema.never() + ), + serverName: schema.conditional( + schema.siblingRef('mode'), + PROXY_MODE, + schema.maybe(schema.string()), + schema.never() + ), }); const paramsValidation = schema.object({ @@ -39,7 +71,6 @@ export const register = (deps: RouteDependencies): void => { const callAsCurrentUser = ctx.core.elasticsearch.dataClient.callAsCurrentUser; const { name } = request.params; - const { seeds, skipUnavailable } = request.body; // Check if cluster does exist. const existingCluster = await doesClusterExist(callAsCurrentUser, name); @@ -57,7 +88,8 @@ export const register = (deps: RouteDependencies): void => { } // Update cluster as new settings - const updateClusterPayload = serializeCluster({ name, seeds, skipUnavailable }); + const updateClusterPayload = serializeCluster({ ...request.body, name }); + const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body: updateClusterPayload, }); From 884731fa8612b7705952c4e5d15ff19a1c670ad6 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 4 Mar 2020 16:15:12 -0500 Subject: [PATCH 03/17] add proxy support in details panel --- .../detail_panel/detail_panel.js | 143 ++++++++++++------ 1 file changed, 99 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 5cd51648e63ccd..4a2a766a08f669 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -28,6 +28,7 @@ import { } from '@elastic/eui'; import { CRUD_APP_BASE_PATH } from '../../../constants'; +import { PROXY_MODE } from '../../../../../common/constants'; import { getRouterLinkProps } from '../../../services'; import { ConfiguredByNodeWarning } from '../../components'; import { ConnectionStatus, RemoveClusterButtonProvider } from '../components'; @@ -113,6 +114,10 @@ export class DetailPanel extends Component { seeds, maxConnectionsPerCluster, initialConnectTimeout, + mode, + proxyAddress, + proxySocketConnections, + connectedSocketsCount, }) { return (
- - - - - - - - - {connectedNodesCount} - - + {mode === PROXY_MODE ? ( + + + + + + + + + {connectedSocketsCount} + + + ) : ( + + + + + + + + + {connectedNodesCount} + + + )} - - - - - - + {mode === PROXY_MODE ? ( + + + + + + + + + {proxyAddress} + + + ) : ( + + + + + + - - {/* TODO: Fix to support proxy mode */} - {(seeds || []).map(seed => ( - {seed} - ))} - - + + {seeds.map(seed => ( + {seed} + ))} + + + )} @@ -203,20 +241,37 @@ export class DetailPanel extends Component { - - - - - - - - - {maxConnectionsPerCluster} - - + {mode === PROXY_MODE ? ( + + + + + + + + + {proxySocketConnections} + + + ) : ( + + + + + + + + + {maxConnectionsPerCluster} + + + )} From df590e3ea96edaf19f659632211c1d8ed153b142 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Thu, 5 Mar 2020 21:21:51 -0500 Subject: [PATCH 04/17] update table --- .../connection_status/connection_status.js | 13 +++-- .../detail_panel/detail_panel.js | 2 +- .../remote_cluster_table.js | 53 ++++++++++++++----- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js index d6d3272c2abe4d..f032636af0bc35 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js @@ -10,7 +10,9 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiIconTip, EuiText } from '@elastic/eui'; -export function ConnectionStatus({ isConnected }) { +import { SNIFF_MODE, PROXY_MODE } from '../../../../../../common/constants'; + +export function ConnectionStatus({ isConnected, mode }) { let icon; let message; @@ -47,13 +49,16 @@ export function ConnectionStatus({ isConnected }) { - - - + {!isConnected && mode === SNIFF_MODE && ( + + + + )} ); } ConnectionStatus.propTypes = { isConnected: PropTypes.bool, + mode: PropTypes.oneOf([SNIFF_MODE, PROXY_MODE]), }; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 4a2a766a08f669..06720d2804a707 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -148,7 +148,7 @@ export class DetailPanel extends Component { - + diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js index fd321a8626ac7b..cd31f4a797bfbc 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { CRUD_APP_BASE_PATH, UIM_SHOW_DETAILS_CLICK } from '../../../constants'; +import { PROXY_MODE } from '../../../../../common/constants'; import { getRouterLinkProps, trackUiMetric, METRIC_TYPE } from '../../../services'; import { ConnectionStatus, RemoveClusterButtonProvider } from '../components'; @@ -123,33 +124,59 @@ export class RemoteClusterTable extends Component { return link; }, }, - { - field: 'seeds', - name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.seedsColumnTitle', { - defaultMessage: 'Seeds', - }), - truncateText: true, - render: seeds => (seeds ? seeds.join(', ') : ''), // TODO Determine better way to support proxy mode - }, { field: 'isConnected', name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.connectedColumnTitle', { - defaultMessage: 'Connection', + defaultMessage: 'Status', }), sortable: true, - render: isConnected => , + render: (isConnected, { mode }) => ( + + ), width: '240px', }, { - field: 'connectedNodesCount', + field: 'mode', + name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.modeColumnTitle', { + defaultMessage: 'Mode', + }), + sortable: true, + render: mode => + mode === PROXY_MODE + ? mode + : i18n.translate('xpack.remoteClusters.remoteClusterList.table.sniffModeDescription', { + defaultMessage: 'default', + }), + }, + { + field: 'mode', + name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.addressesColumnTitle', { + defaultMessage: 'Addresses', + }), + truncateText: true, + render: (mode, { seeds, proxyAddress }) => { + if (mode === PROXY_MODE) { + return proxyAddress; + } + return seeds.join(', '); + }, + }, + { + field: 'mode', name: i18n.translate( - 'xpack.remoteClusters.remoteClusterList.table.connectedNodesColumnTitle', + 'xpack.remoteClusters.remoteClusterList.table.connectionsColumnTitle', { - defaultMessage: 'Connected nodes', + defaultMessage: 'Connections', } ), sortable: true, width: '160px', + render: (mode, { connectedNodesCount, connectedSocketsCount }) => { + if (mode === PROXY_MODE) { + return connectedSocketsCount; + } + return connectedNodesCount; + }, }, { name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.actionsColumnTitle', { From 41a4397ce930c8d8259e72ac486328f183112b64 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 6 Mar 2020 12:54:38 -0500 Subject: [PATCH 05/17] fix validation --- .../remote_cluster_form.js | 35 ++++++++++++--- .../remote_cluster_form/validators/index.js | 3 +- .../validators/validate_proxy.js | 44 +++++++++++++++++++ .../validators/validate_seed.js | 8 ++-- .../public/application/services/index.js | 2 +- ...idate_seed_node.js => validate_address.js} | 4 +- ..._node.test.js => validate_address.test.js} | 34 +++++++------- .../server/routes/api/add_route.ts | 37 +++------------- .../server/routes/api/update_route.ts | 37 +++------------- 9 files changed, 108 insertions(+), 96 deletions(-) create mode 100644 x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js rename x-pack/plugins/remote_clusters/public/application/services/{validate_seed_node.js => validate_address.js} (91%) rename x-pack/plugins/remote_clusters/public/application/services/{validate_seed_node.test.js => validate_address.test.js} (55%) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 26baa737039560..6b6d2b591adff9 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -38,7 +38,7 @@ import { skippingDisconnectedClustersUrl, transportPortUrl } from '../../../serv import { RequestFlyout } from './request_flyout'; -import { validateName, validateSeeds, validateSeed } from './validators'; +import { validateName, validateSeeds, validateProxy, validateSeed } from './validators'; import { SNIFF_MODE, PROXY_MODE } from '../../../../../common/constants'; @@ -96,10 +96,12 @@ export class RemoteClusterForm extends Component { }; getFieldsErrors(fields, seedInput = '') { - const { name, seeds, mode } = fields; + const { name, seeds, mode, proxyAddress } = fields; + return { name: validateName(name), seeds: mode === SNIFF_MODE ? validateSeeds(seeds, seedInput) : null, + proxyAddress: mode === PROXY_MODE ? validateProxy(proxyAddress) : null, }; } @@ -328,7 +330,7 @@ export class RemoteClusterForm extends Component { fullWidth > this.onFieldsChange({ nodeConnections: Number(e.target.value) || null })} fullWidth /> @@ -339,7 +341,9 @@ export class RemoteClusterForm extends Component { renderProxyModeSettings() { const { + areErrorsVisible, fields: { proxyAddress, proxySocketConnections, serverName }, + fieldsErrors: { proxyAddress: errorProxyAddress }, } = this.state; return ( @@ -358,11 +362,20 @@ export class RemoteClusterForm extends Component { defaultMessage="The address used for all remote connections." /> } + isInvalid={Boolean(areErrorsVisible && errorProxyAddress)} + error={errorProxyAddress} fullWidth > this.onFieldsChange({ proxyAddress: e.target.value })} + isInvalid={Boolean(areErrorsVisible && errorProxyAddress)} fullWidth /> @@ -384,7 +397,7 @@ export class RemoteClusterForm extends Component { fullWidth > this.onFieldsChange({ proxySocketConnections: Number(e.target.value) || null }) } @@ -438,7 +451,7 @@ export class RemoteClusterForm extends Component { <> { const { areErrorsVisible, - fieldsErrors: { name: errorClusterName, seeds: errorsSeeds }, + fieldsErrors: { name: errorClusterName, seeds: errorsSeeds, proxyAddress: errorProxyAddress }, localSeedErrors, } = this.state; @@ -726,6 +739,16 @@ export class RemoteClusterForm extends Component { }); } + if (errorProxyAddress) { + errorExplanations.push({ + key: 'seedsExplanation', + field: i18n.translate('xpack.remoteClusters.remoteClusterForm.inputProxyErrorMessage', { + defaultMessage: 'The "Proxy address" field is invalid.', + }), + error: errorProxyAddress, + }); + } + const messagesToBeRendered = errorExplanations.length && (
diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js index 66a1016c7fcc84..ec5f0b1166ce5d 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js @@ -5,5 +5,6 @@ */ export { validateName } from './validate_name'; -export { validateSeed } from './validate_seed'; +export { validateProxy } from './validate_proxy'; export { validateSeeds } from './validate_seeds'; +export { validateSeed } from './validate_seed'; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js new file mode 100644 index 00000000000000..07ccccbd5910c6 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.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 { FormattedMessage } from '@kbn/i18n/react'; + +import { isAddressValid, isPortValid } from '../../../../services'; + +export function validateProxy(proxy) { + if (!proxy) { + return ( + + ); + } + + const isValid = isAddressValid(proxy); + + if (!isValid) { + return ( + + ); + } + + if (!isPortValid(proxy)) { + return ( + + ); + } + + return null; +} diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js index e2260504cc033f..e312e77972fbb7 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; -import { isSeedNodeValid, isSeedNodePortValid } from '../../../../services'; +import { isAddressValid, isPortValid } from '../../../../services'; export function validateSeed(seed) { const errors = []; @@ -15,7 +15,7 @@ export function validateSeed(seed) { return errors; } - const isValid = isSeedNodeValid(seed); + const isValid = isAddressValid(seed); if (!isValid) { errors.push( @@ -30,9 +30,7 @@ export function validateSeed(seed) { ); } - const isPortValid = isSeedNodePortValid(seed); - - if (!isPortValid) { + if (!isPortValid(seed)) { errors.push( i18n.translate('xpack.remoteClusters.remoteClusterForm.localSeedError.invalidPortMessage', { defaultMessage: 'A port is required.', diff --git a/x-pack/plugins/remote_clusters/public/application/services/index.js b/x-pack/plugins/remote_clusters/public/application/services/index.js index 031770d9500ed2..387a04b6e5d8c3 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/index.js +++ b/x-pack/plugins/remote_clusters/public/application/services/index.js @@ -10,7 +10,7 @@ export { showApiError, showApiWarning } from './api_errors'; export { initRedirect, redirect } from './redirect'; -export { isSeedNodeValid, isSeedNodePortValid } from './validate_seed_node'; +export { isAddressValid, isPortValid } from './validate_address'; export { extractQueryParams } from './query_params'; diff --git a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js b/x-pack/plugins/remote_clusters/public/application/services/validate_address.js similarity index 91% rename from x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js rename to x-pack/plugins/remote_clusters/public/application/services/validate_address.js index 714b5cf44de23f..7e12b9c06595d2 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js +++ b/x-pack/plugins/remote_clusters/public/application/services/validate_address.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export function isSeedNodeValid(seedNode) { +export function isAddressValid(seedNode) { if (!seedNode) { return false; } @@ -23,7 +23,7 @@ export function isSeedNodeValid(seedNode) { return !containsInvalidCharacters; } -export function isSeedNodePortValid(seedNode) { +export function isPortValid(seedNode) { if (!seedNode) { return false; } diff --git a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js b/x-pack/plugins/remote_clusters/public/application/services/validate_address.test.js similarity index 55% rename from x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js rename to x-pack/plugins/remote_clusters/public/application/services/validate_address.test.js index 36e989a41b066c..2551f4fac7908b 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js +++ b/x-pack/plugins/remote_clusters/public/application/services/validate_address.test.js @@ -4,75 +4,75 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isSeedNodeValid, isSeedNodePortValid } from './validate_seed_node'; +import { isAddressValid, isPortValid } from './validate_address'; -describe('Validate seed node', () => { +describe('Validate address', () => { describe('isSeedNodeValid', () => { describe('rejects', () => { it('adjacent periods', () => { - expect(isSeedNodeValid('a..b')).toBe(false); + expect(isAddressValid('a..b')).toBe(false); }); it('underscores', () => { - expect(isSeedNodeValid('____')).toBe(false); + expect(isAddressValid('____')).toBe(false); }); ['/', '\\', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '=', '+', '?'].forEach(char => { it(char, () => { - expect(isSeedNodeValid(char)).toBe(false); + expect(isAddressValid(char)).toBe(false); }); }); }); describe('accepts', () => { it('uppercase letters', () => { - expect(isSeedNodeValid('A.B.C.D')).toBe(true); + expect(isAddressValid('A.B.C.D')).toBe(true); }); it('lowercase letters', () => { - expect(isSeedNodeValid('a')).toBe(true); + expect(isAddressValid('a')).toBe(true); }); it('numbers', () => { - expect(isSeedNodeValid('56546354')).toBe(true); + expect(isAddressValid('56546354')).toBe(true); }); it('dashes', () => { - expect(isSeedNodeValid('----')).toBe(true); + expect(isAddressValid('----')).toBe(true); }); it('many parts', () => { - expect(isSeedNodeValid('abcd.efgh.ijkl.mnop.qrst.uvwx.yz')).toBe(true); + expect(isAddressValid('abcd.efgh.ijkl.mnop.qrst.uvwx.yz')).toBe(true); }); }); }); - describe('isSeedNodePortValid', () => { + describe('isPortValid', () => { describe('rejects', () => { it('missing port', () => { - expect(isSeedNodePortValid('abcd')).toBe(false); + expect(isPortValid('abcd')).toBe(false); }); it('empty port', () => { - expect(isSeedNodePortValid('abcd:')).toBe(false); + expect(isPortValid('abcd:')).toBe(false); }); it('letters', () => { - expect(isSeedNodePortValid('ab:cd')).toBe(false); + expect(isPortValid('ab:cd')).toBe(false); }); it('non-numbers', () => { - expect(isSeedNodePortValid('ab:5 0')).toBe(false); + expect(isPortValid('ab:5 0')).toBe(false); }); it('multiple ports', () => { - expect(isSeedNodePortValid('ab:cd:9000')).toBe(false); + expect(isPortValid('ab:cd:9000')).toBe(false); }); }); describe('accepts', () => { it('a single numeric port, even beyond the standard port range', () => { - expect(isSeedNodePortValid('abcd:100000000')).toBe(true); + expect(isPortValid('abcd:100000000')).toBe(true); }); }); }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index 9ae3433a701cbd..32642030b9d29d 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -20,38 +20,11 @@ const bodyValidation = schema.object({ name: schema.string(), skipUnavailable: schema.boolean(), mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), - // The following validation only applies to "sniff" mode - seeds: schema.conditional( - schema.siblingRef('mode'), - SNIFF_MODE, - schema.arrayOf(schema.string()), - schema.never() - ), - nodeConnections: schema.conditional( - schema.siblingRef('mode'), - SNIFF_MODE, - schema.maybe(schema.number()), - schema.never() - ), - // The following validation only applies to "proxy" mode - proxyAddress: schema.conditional( - schema.siblingRef('mode'), - PROXY_MODE, - schema.string(), - schema.never() - ), - proxySocketConnections: schema.conditional( - schema.siblingRef('mode'), - PROXY_MODE, - schema.maybe(schema.number()), - schema.never() - ), - serverName: schema.conditional( - schema.siblingRef('mode'), - PROXY_MODE, - schema.maybe(schema.string()), - schema.never() - ), + seeds: schema.nullable(schema.arrayOf(schema.string())), + nodeConnections: schema.nullable(schema.number()), + proxyAddress: schema.nullable(schema.string()), + proxySocketConnections: schema.nullable(schema.number()), + serverName: schema.nullable(schema.string()), }); type RouteBody = TypeOf; diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index 37154045ce6a42..5167c27bfa335b 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -19,38 +19,11 @@ import { isEsError } from '../../lib/is_es_error'; const bodyValidation = schema.object({ skipUnavailable: schema.boolean(), mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), - // The following validation only applies to "sniff" mode - seeds: schema.conditional( - schema.siblingRef('mode'), - SNIFF_MODE, - schema.arrayOf(schema.string()), - schema.never() - ), - nodeConnections: schema.conditional( - schema.siblingRef('mode'), - SNIFF_MODE, - schema.maybe(schema.number()), - schema.never() - ), - // The following validation only applies to "proxy" mode - proxyAddress: schema.conditional( - schema.siblingRef('mode'), - PROXY_MODE, - schema.string(), - schema.never() - ), - proxySocketConnections: schema.conditional( - schema.siblingRef('mode'), - PROXY_MODE, - schema.maybe(schema.number()), - schema.never() - ), - serverName: schema.conditional( - schema.siblingRef('mode'), - PROXY_MODE, - schema.maybe(schema.string()), - schema.never() - ), + seeds: schema.nullable(schema.arrayOf(schema.string())), + nodeConnections: schema.nullable(schema.number()), + proxyAddress: schema.nullable(schema.string()), + proxySocketConnections: schema.nullable(schema.number()), + serverName: schema.nullable(schema.string()), }); const paramsValidation = schema.object({ From d84337dc9c6731cf572a1aab70cab95ffc073cd0 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 6 Mar 2020 14:17:41 -0500 Subject: [PATCH 06/17] cleanup: update tests, remove unused translations, add proxy badge --- .../remote_clusters_list.test.js | 15 +- .../fixtures/remote_cluster.js | 8 + .../remote_cluster_form.test.js.snap | 1483 ++++++++++++++++- .../remote_cluster_form.js | 1 + .../remote_cluster_form.test.js | 10 + .../__snapshots__/validate_proxy.test.js.snap | 25 + .../validators/validate_proxy.test.js | 25 + .../detail_panel/detail_panel.js | 27 +- .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - 10 files changed, 1579 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap create mode 100644 x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js index 1b7c600218cee2..b359c952a96f02 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js @@ -15,6 +15,8 @@ import { import { getRouter } from '../../public/application/services'; import { getRemoteClusterMock } from '../../fixtures/remote_cluster'; +import { PROXY_MODE } from '../../common/constants'; + jest.mock('ui/new_platform'); const { setup } = pageHelpers.remoteClustersList; @@ -84,9 +86,10 @@ describe('', () => { const remoteCluster2 = getRemoteClusterMock({ name: `b${getRandomString()}`, isConnected: false, - connectedNodesCount: 0, - seeds: ['localhost:9500'], + connectedSocketsCount: 0, + proxyAddress: 'localhost:9500', isConfiguredByNode: true, + mode: PROXY_MODE, }); const remoteClusters = [remoteCluster1, remoteCluster2]; @@ -118,17 +121,19 @@ describe('', () => { [ '', // Empty because the first column is the checkbox to select the row remoteCluster1.name, - remoteCluster1.seeds.join(', '), 'Connected', + 'default', + remoteCluster1.seeds.join(', '), remoteCluster1.connectedNodesCount.toString(), '', // Empty because the last column is for the "actions" on the resource ], [ '', remoteCluster2.name, - remoteCluster2.seeds.join(', '), 'Not connected', - remoteCluster2.connectedNodesCount.toString(), + PROXY_MODE, + remoteCluster2.proxyAddress, + remoteCluster2.connectedSocketsCount.toString(), '', ], ]); diff --git a/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js index e3e087548cf001..d32be9fc19c458 100644 --- a/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js +++ b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js @@ -5,12 +5,17 @@ */ import { getRandomString } from '../../../test_utils'; +import { SNIFF_MODE } from '../common/constants'; + export const getRemoteClusterMock = ({ name = getRandomString(), isConnected = true, connectedNodesCount = 1, + connectedSocketsCount, seeds = ['localhost:9400'], isConfiguredByNode = false, + mode = SNIFF_MODE, + proxyAddress, } = {}) => ({ name, seeds, @@ -20,4 +25,7 @@ export const getRemoteClusterMock = ({ maxConnectionsPerCluster: 3, initialConnectTimeout: '30s', skipUnavailable: false, + mode, + connectedSocketsCount, + proxyAddress, }); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 590ea27617adfe..045de4d79ce336 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -1,5 +1,1389 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RemoteClusterForm proxy mode renders correct connection settings when user enables proxy mode 1`] = ` + + +
+ + } + fullWidth={true} + title={ + +

+ +

+
+ } + > + +
+ + + Name + + + +
+ +
+ + + + +
+ +
+ + A unique name for the remote cluster. + +
+
+
+
+
+
+ +
+ + } + fullWidth={true} + hasChildLabel={true} + hasEmptyLabelSpace={false} + helpText={ + + } + isInvalid={false} + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + Name can only contain letters, numbers, underscores, and dashes. + +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + } + onChange={[Function]} + /> + + } + fullWidth={true} + title={ + +

+ +

+
+ } + > + +
+ + + Connection mode settings + + + +
+ +
+ + + + +
+ +
+ + Remote cluster connections work by configuring a remote cluster and connecting only to a limited number of nodes in that remote cluster. + + +
+ + + } + onChange={[Function]} + > +
+ + + + Use proxy mode + + +
+
+
+ +
+ +
+ + +
+ + } + fullWidth={true} + hasChildLabel={true} + hasEmptyLabelSpace={false} + helpText={ + + } + isInvalid={false} + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + The address used for all remote connections. + +
+
+
+
+
+ + } + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + The number of socket connections to open per remote cluster. + +
+
+
+
+
+ + } + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + An optional hostname string which will be sent in the server_name field of the TLS Server Name Indication extension if TLS is enabled. + +
+
+
+
+
+
+
+
+ +
+
+
+ +

+ + + , + "optionName": + + , + } + } + /> +

+ + } + fullWidth={true} + title={ + +

+ +

+
+ } + > + +
+ + + Make remote cluster optional + + + +
+ +
+ + + + +
+ +
+

+ + + , + "optionName": + + , + } + } + > + By default, a request fails if any of the queried remote clusters are unavailable. To continue sending a request to other remote clusters if this cluster is unavailable, enable + + + Skip if unavailable + + + . + + + + +

+
+
+
+
+
+
+ +
+ +
+
+ +
+ + + Skip if unavailable + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+ + + +
+
+
+
+
+
+ +
+ + + +
+
+
+
+ +`; + exports[`RemoteClusterForm renders untouched state 1`] = ` Array [ @@ -195,6 +1633,47 @@ Array [
+
+
+ +
+
+
+
+ +
+
+
+ The number of gateway nodes to connect to. +
+
+
diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 6b6d2b591adff9..3fd535ec1393de 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -462,6 +462,7 @@ export class RemoteClusterForm extends Component { /> } checked={mode === PROXY_MODE} + data-test-subj="remoteClusterFormConnectionModeToggle" onChange={e => this.onFieldsChange({ mode: e.target.checked ? PROXY_MODE : SNIFF_MODE }) } diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js index 799bf1f4fd0519..907fd2183265f9 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js @@ -21,6 +21,16 @@ describe('RemoteClusterForm', () => { expect(component).toMatchSnapshot(); }); + describe('proxy mode', () => { + test('renders correct connection settings when user enables proxy mode', () => { + const component = mountWithIntl( {}} />); + + findTestSubject(component, 'remoteClusterFormConnectionModeToggle').simulate('click'); + + expect(component).toMatchSnapshot(); + }); + }); + describe('validation', () => { test('renders invalid state and a global form error when the user tries to submit an invalid form', () => { const component = mountWithIntl( {}} />); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap new file mode 100644 index 00000000000000..487a09962753eb --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validateProxy rejects proxy address when the address is invalid 1`] = ` + +`; + +exports[`validateProxy rejects proxy address when the port is invalid 1`] = ` + +`; + +exports[`validateProxy rejects proxy address when there's no input 1`] = ` + +`; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js new file mode 100644 index 00000000000000..e6e69849e13aaa --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.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 { validateProxy } from './validate_proxy'; + +describe('validateProxy', () => { + test(`rejects proxy address when there's no input`, () => { + expect(validateProxy(undefined)).toMatchSnapshot(); + }); + + test(`rejects proxy address when the address is invalid`, () => { + expect(validateProxy('___')).toMatchSnapshot(); + }); + + test(`rejects proxy address when the port is invalid`, () => { + expect(validateProxy('noport')).toMatchSnapshot(); + }); + + test(`accepts valid proxy address`, () => { + expect(validateProxy('localhost:3000')).toBe(null); + }); +}); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 06720d2804a707..99df849a979e1d 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -9,6 +9,7 @@ import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; import { + EuiBadge, EuiButton, EuiButtonEmpty, EuiDescriptionList, @@ -371,7 +372,7 @@ export class DetailPanel extends Component { } render() { - const { isOpen, closeDetailPanel, clusterName } = this.props; + const { isOpen, closeDetailPanel, clusterName, cluster } = this.props; if (!isOpen) { return null; @@ -386,13 +387,23 @@ export class DetailPanel extends Component { maxWidth={400} > - -

{clusterName}

-
+ + + +

{clusterName}

+
+
+ {cluster && cluster.mode === PROXY_MODE ? ( + + {' '} + {cluster.mode} + + ) : null} +
{this.renderFlyoutBody()} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 195b75f84a8c0c..dd4b7e0166c5da 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10094,10 +10094,7 @@ "xpack.remoteClusters.remoteClusterForm.saveButtonLabel": "保存", "xpack.remoteClusters.remoteClusterForm.sectionNameDescription": "リモートクラスターの固有の名前です。", "xpack.remoteClusters.remoteClusterForm.sectionNameTitle": "名前", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsDescription1": "クラスターステータスのクエリを実行するリモートクラスターノードのリストです。1 つのノードが利用できない場合にディスカバリが失敗しないよう、複数シードノードを指定してください。", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText": "リモートクラスターの {transportPort} の前にくる IP アドレスまたはホスト名です。", "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText.transportPortLinkText": "トランスポートポート", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsTitle": "クラスターディスカバリのシードノード", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription": "デフォルトで、リクエストのリモートクラスターのどれかが利用できないと、リクエストは失敗となります。このクラスターが利用できない場合にリクエストを他のリモートクラスターに送信し続けるには、{optionName} を有効にします。{learnMoreLink}", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.learnMoreLinkLabel": "詳細", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.optionNameLabel": "利用不可の場合スキップ", @@ -10119,11 +10116,9 @@ "xpack.remoteClusters.remoteClusterList.table.actionEditDescription": "リモートクラスターを編集します", "xpack.remoteClusters.remoteClusterList.table.actionsColumnTitle": "アクション", "xpack.remoteClusters.remoteClusterList.table.connectedColumnTitle": "接続", - "xpack.remoteClusters.remoteClusterList.table.connectedNodesColumnTitle": "接続済みのノード", "xpack.remoteClusters.remoteClusterList.table.isConfiguredByNodeMessage": "elasticsearch.yml で定義されています", "xpack.remoteClusters.remoteClusterList.table.nameColumnTitle": "名前", "xpack.remoteClusters.remoteClusterList.table.removeButtonLabel": "{count, plural, one {リモートクラスター} other {{count}リモートクラスター}}を削除", - "xpack.remoteClusters.remoteClusterList.table.seedsColumnTitle": "シード", "xpack.remoteClusters.remoteClusterListTitle": "リモートクラスター", "xpack.remoteClusters.removeAction.errorMultipleNotificationTitle": "「{count}」リモートクラスターの削除中にエラーが発生", "xpack.remoteClusters.removeAction.errorSingleNotificationTitle": "リモートクラスター「{name}」の削除中にエラーが発生", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9add5c6bcdbc32..574f0881ee9135 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10094,10 +10094,7 @@ "xpack.remoteClusters.remoteClusterForm.saveButtonLabel": "保存", "xpack.remoteClusters.remoteClusterForm.sectionNameDescription": "远程集群的唯一名称。", "xpack.remoteClusters.remoteClusterForm.sectionNameTitle": "名称", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsDescription1": "要查询集群状态的远程集群节点的列表。指定多个种子节点,以便在节点不可用时发现不会失败。", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText": "IP 地址或主机名,后跟远程集群的 {transportPort}。", "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText.transportPortLinkText": "传输端口", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsTitle": "用于集群发现的种子节点", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription": "默认情况下,如果任何查询的远程集群不可用,请求将失败。要在此集群不可用时继续向其他远程集群发送请求,请启用 {optionName}。{learnMoreLink}", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.learnMoreLinkLabel": "了解详情。", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.optionNameLabel": "如果不可用,则跳过", @@ -10119,11 +10116,9 @@ "xpack.remoteClusters.remoteClusterList.table.actionEditDescription": "编辑远程集群", "xpack.remoteClusters.remoteClusterList.table.actionsColumnTitle": "操作", "xpack.remoteClusters.remoteClusterList.table.connectedColumnTitle": "连接", - "xpack.remoteClusters.remoteClusterList.table.connectedNodesColumnTitle": "已连接节点", "xpack.remoteClusters.remoteClusterList.table.isConfiguredByNodeMessage": "在 elasticsearch.yml 中定义", "xpack.remoteClusters.remoteClusterList.table.nameColumnTitle": "名称", "xpack.remoteClusters.remoteClusterList.table.removeButtonLabel": "删除 {count, plural, one { 个远程集群} other {{count} 个远程集群}}", - "xpack.remoteClusters.remoteClusterList.table.seedsColumnTitle": "种子", "xpack.remoteClusters.remoteClusterListTitle": "远程集群", "xpack.remoteClusters.removeAction.errorMultipleNotificationTitle": "删除 “{count}” 个远程集群时出错", "xpack.remoteClusters.removeAction.errorSingleNotificationTitle": "删除远程集群 “{name}” 时出错", From 56671cac7bcf27beeac0708f10924117eb7dc925 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 9 Mar 2020 08:44:46 -0400 Subject: [PATCH 07/17] fix ccr test --- .../cross_cluster_replication/remote_clusters.helpers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js b/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js index d8cee1db9a2bcf..4462fcf75d5d85 100644 --- a/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js +++ b/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js @@ -25,7 +25,8 @@ export const registerHelpers = supertest => { .post(`${REMOTE_CLUSTERS_API_BASE_PATH}`) .set('kbn-xsrf', 'xxx') .send({ - name: name, + name, + mode: 'sniff', seeds: [`localhost:${esTransportPort}`], skipUnavailable: true, }); From ecf78ff60fcbd03f4a2f1d52d63b7c61dd92196b Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 9 Mar 2020 11:09:07 -0400 Subject: [PATCH 08/17] update api tests --- .../common/lib/cluster_serialization.ts | 2 +- .../server/routes/api/add_route.test.ts | 91 ++++++++++++++++++- .../server/routes/api/delete_route.test.ts | 24 ++++- .../server/routes/api/get_route.test.ts | 3 + .../server/routes/api/update_route.test.ts | 28 +++++- 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 46a6bb7e11ca73..f5f9f76985a7db 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -81,7 +81,7 @@ export function serializeCluster(deserializedClusterObject: any): any { remote: { [name]: { skip_unavailable: skipUnavailable !== undefined ? skipUnavailable : null, - mode, + mode: mode ?? null, proxy_address: proxyAddress ?? null, proxy_socket_connections: proxySocketConnections ?? null, server_name: serverName ?? null, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts index a6edd15995d728..34d741aa4b7da9 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts @@ -80,7 +80,7 @@ describe('ADD remote clusters', () => { }; describe('success', () => { - addRemoteClustersTest('adds remote cluster', { + addRemoteClustersTest(`adds remote cluster with "sniff" mode`, { apiResponses: [ async () => ({}), async () => ({ @@ -106,6 +106,7 @@ describe('ADD remote clusters', () => { payload: { name: 'test', seeds: ['127.0.0.1:9300'], + mode: 'sniff', skipUnavailable: false, }, asserts: { @@ -117,7 +118,79 @@ describe('ADD remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, + }, + }, + }, + }, + ], + ], + statusCode: 200, + result: { + acknowledged: true, + }, + }, + }); + addRemoteClustersTest(`adds remote cluster with "proxy" mode`, { + apiResponses: [ + async () => ({}), + async () => ({ + acknowledged: true, + persistent: { + cluster: { + remote: { + test: { + connected: true, + mode: 'proxy', + seeds: ['127.0.0.1:9300'], + num_sockets_connected: 1, + max_socket_connections: 18, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, + }, + }, + }, + transient: {}, + }), + ], + payload: { + name: 'test', + proxyAddress: '127.0.0.1:9300', + mode: 'proxy', + skipUnavailable: false, + serverName: 'foobar', + }, + asserts: { + apiArguments: [ + ['cluster.remoteInfo'], + [ + 'cluster.putSettings', + { + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: null, + skip_unavailable: false, + mode: 'proxy', + node_connections: null, + proxy_address: '127.0.0.1:9300', + proxy_socket_connections: null, + server_name: 'foobar', + }, + }, }, }, }, @@ -151,6 +224,7 @@ describe('ADD remote clusters', () => { name: 'test', seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, asserts: { apiArguments: [['cluster.remoteInfo']], @@ -167,6 +241,7 @@ describe('ADD remote clusters', () => { name: 'test', seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, asserts: { apiArguments: [ @@ -177,7 +252,17 @@ describe('ADD remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts index 04deb62d2c2d26..cf14f8a67054ec 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts @@ -113,7 +113,17 @@ describe('DELETE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: null, skip_unavailable: null } }, + remote: { + test: { + seeds: null, + skip_unavailable: null, + mode: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + node_connections: null, + }, + }, }, }, }, @@ -211,7 +221,17 @@ describe('DELETE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: null, skip_unavailable: null } }, + remote: { + test: { + seeds: null, + skip_unavailable: null, + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts index 90955be85859d4..d81b50f1148de4 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts @@ -89,6 +89,7 @@ describe('GET remote clusters', () => { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false, + mode: 'sniff', }, }, }, @@ -120,6 +121,7 @@ describe('GET remote clusters', () => { initialConnectTimeout: '30s', skipUnavailable: false, isConfiguredByNode: false, + mode: 'sniff', }, ], }, @@ -170,6 +172,7 @@ describe('GET remote clusters', () => { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false, + mode: 'sniff', }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts index 9ba239c3ff6616..84ba9587ddfa62 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts @@ -129,6 +129,7 @@ describe('UPDATE remote clusters', () => { payload: { seeds: ['127.0.0.1:9300'], skipUnavailable: true, + mode: 'sniff', }, asserts: { apiArguments: [ @@ -139,7 +140,17 @@ describe('UPDATE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: true } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: true, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, @@ -156,6 +167,7 @@ describe('UPDATE remote clusters', () => { name: 'test', seeds: ['127.0.0.1:9300'], skipUnavailable: true, + mode: 'sniff', }, }, }); @@ -167,6 +179,7 @@ describe('UPDATE remote clusters', () => { payload: { seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, params: { name: 'test', @@ -198,6 +211,7 @@ describe('UPDATE remote clusters', () => { payload: { seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, params: { name: 'test', @@ -211,7 +225,17 @@ describe('UPDATE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, From cbde9d1c0a69a02175127be0f61cbbe29b9ddbec Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 11 Mar 2020 15:15:55 -0400 Subject: [PATCH 09/17] add support for deprecated setting; add support for server_name --- .../common/lib/cluster_serialization.ts | 23 +++++++++-- .../remote_cluster_edit.js | 40 ++++++++++++++----- .../remote_cluster_table.js | 26 +++++++++++- .../server/routes/api/get_route.ts | 16 +++++++- 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index f5f9f76985a7db..029fec0989e44d 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -export function deserializeCluster(name: string, esClusterObject: any): any { +import { PROXY_MODE } from '../constants'; + +export function deserializeCluster( + name: string, + esClusterObject: any, + deprecatedProxyAddress: string | undefined +): any { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); } @@ -21,8 +27,6 @@ export function deserializeCluster(name: string, esClusterObject: any): any { address: proxyAddress, max_socket_connections: proxySocketConnections, num_sockets_connected: connectedSocketsCount, - // TODO: Need to support serverName somehow - // server_name: serverName, } = esClusterObject; let deserializedClusterObject: any = { @@ -49,6 +53,18 @@ export function deserializeCluster(name: string, esClusterObject: any): any { }; } + // If a user has a remote cluster with the deprecated proxy setting, + // we transform the data to support the new implementation and also flag the deprecation + if (deprecatedProxyAddress) { + deserializedClusterObject = { + ...deserializedClusterObject, + proxyAddress: deprecatedProxyAddress, + seeds: undefined, + hasDeprecatedProxySetting: true, + mode: PROXY_MODE, + }; + } + // It's unnecessary to send undefined values back to the client, so we can remove them. Object.keys(deserializedClusterObject).forEach(key => { if (deserializedClusterObject[key] === undefined) { @@ -76,6 +92,7 @@ export function serializeCluster(deserializedClusterObject: any): any { } = deserializedClusterObject; return { + // Background on why we only save as persistent settings detailed here: https://github.com/elastic/kibana/pull/26067#issuecomment-441848124 persistent: { cluster: { remote: { diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js index f48d854da7255d..2c0936b319d09a 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js @@ -158,7 +158,7 @@ export class RemoteClusterEdit extends Component { ); } - const { isConfiguredByNode } = cluster; + const { isConfiguredByNode, hasDeprecatedProxySetting } = cluster; if (isConfiguredByNode) { return ( @@ -178,14 +178,36 @@ export class RemoteClusterEdit extends Component { } return ( - + <> + {hasDeprecatedProxySetting ? ( + <> + + } + color="warning" + iconType="help" + > + + + + + ) : null} + + ); } diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js index cd31f4a797bfbc..65368acbf298fd 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js @@ -84,7 +84,7 @@ export class RemoteClusterTable extends Component { }), sortable: true, truncateText: false, - render: (name, { isConfiguredByNode }) => { + render: (name, { isConfiguredByNode, hasDeprecatedProxySetting }) => { const link = ( + {link} + + + + } + /> + + + ); + } + return link; }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts index 44b6284109ac54..4a9fdc7d3502c9 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts @@ -33,13 +33,27 @@ export const register = (deps: RouteDependencies): void => { const cluster = clustersByName[clusterName]; const isTransient = transientClusterNames.includes(clusterName); const isPersistent = persistentClusterNames.includes(clusterName); + // If the cluster hasn't been stored in the cluster state, then it's defined by the // node's config file. const isConfiguredByNode = !isTransient && !isPersistent; + // Pre-7.6, ES supported an undocumented "proxy" field + // ES does not handle migrating this to the new implementation, so we need to surface it in the UI + const deprecatedProxyAddress = isPersistent + ? get(clusterSettings, `persistent.cluster.remote[${clusterName}].proxy`, undefined) + : undefined; + + // server_name is not available via the GET /_remote/info API, so we get it from the cluster settings + // Per https://github.com/elastic/kibana/pull/26067#issuecomment-441848124, we only look at persistent settings + const serverName = isPersistent + ? get(clusterSettings, `persistent.cluster.remote[${clusterName}].server_name`, undefined) + : undefined; + return { - ...deserializeCluster(clusterName, cluster), + ...deserializeCluster(clusterName, cluster, deprecatedProxyAddress), isConfiguredByNode, + serverName, }; }); From de6393be25ad07296d52b8a67916823e188c5b71 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 11 Mar 2020 15:34:01 -0400 Subject: [PATCH 10/17] add documentation link --- .../remote_cluster_form.js | 48 ++++++++++++++----- .../application/services/documentation.ts | 2 + 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 3fd535ec1393de..8f068bbb4940f8 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -34,7 +34,11 @@ import { htmlIdGenerator, } from '@elastic/eui'; -import { skippingDisconnectedClustersUrl, transportPortUrl } from '../../../services/documentation'; +import { + skippingDisconnectedClustersUrl, + transportPortUrl, + proxyModeUrl, +} from '../../../services/documentation'; import { RequestFlyout } from './request_flyout'; @@ -453,20 +457,40 @@ export class RemoteClusterForm extends Component { id="xpack.remoteClusters.remoteClusterForm.sectionModeDescription" defaultMessage="Remote cluster connections work by configuring a remote cluster and connecting only to a limited number of nodes in that remote cluster." /> - - + + + ), + }} /> } - checked={mode === PROXY_MODE} - data-test-subj="remoteClusterFormConnectionModeToggle" - onChange={e => - this.onFieldsChange({ mode: e.target.checked ? PROXY_MODE : SNIFF_MODE }) - } - /> + > + + } + checked={mode === PROXY_MODE} + data-test-subj="remoteClusterFormConnectionModeToggle" + onChange={e => + this.onFieldsChange({ mode: e.target.checked ? PROXY_MODE : SNIFF_MODE }) + } + /> + } fullWidth diff --git a/x-pack/plugins/remote_clusters/public/application/services/documentation.ts b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts index 38cf2223a313bc..f6f5dc987c2eb7 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/documentation.ts +++ b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts @@ -9,6 +9,7 @@ import { DocLinksStart } from 'kibana/public'; export let skippingDisconnectedClustersUrl: string; export let remoteClustersUrl: string; export let transportPortUrl: string; +export let proxyModeUrl: string; export function init(docLinks: DocLinksStart): void { const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; @@ -17,4 +18,5 @@ export function init(docLinks: DocLinksStart): void { skippingDisconnectedClustersUrl = `${esDocBasePath}/modules-cross-cluster-search.html#_skipping_disconnected_clusters`; remoteClustersUrl = `${esDocBasePath}/modules-remote-clusters.html`; transportPortUrl = `${esDocBasePath}/modules-transport.html`; + proxyModeUrl = `${esDocBasePath}/modules-remote-clusters.html#proxy-mode`; } From 5a260c6288197448c9fc4f5f90ed48a6b93e4d0c Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Thu, 12 Mar 2020 15:49:15 -0400 Subject: [PATCH 11/17] update tests --- .../remote_clusters_add.test.js | 50 ++ .../remote_clusters_list.test.js | 38 +- .../common/lib/cluster_serialization.test.ts | 43 ++ .../common/lib/cluster_serialization.ts | 10 +- .../fixtures/remote_cluster.js | 2 + .../remote_cluster_form.test.js.snap | 461 ++++++++++-------- .../remote_cluster_form.js | 1 + .../validators/validate_proxy.js | 4 +- .../remote_cluster_table.js | 7 +- 9 files changed, 400 insertions(+), 216 deletions(-) diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js index 8f34e7d84a08be..78482198b1a5d4 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js @@ -53,6 +53,17 @@ describe('Create Remote cluster', () => { expect(find('remoteClusterFormSkipUnavailableFormToggle').props()['aria-checked']).toBe(true); }); + test('should have a toggle to enable "proxy" mode for a remote cluster', () => { + expect(exists('remoteClusterFormConnectionModeToggle')).toBe(true); + + // By default it should be set to "false" + expect(find('remoteClusterFormConnectionModeToggle').props()['aria-checked']).toBe(false); + + form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle'); + + expect(find('remoteClusterFormConnectionModeToggle').props()['aria-checked']).toBe(true); + }); + test('should display errors and disable the save button when clicking "save" without filling the form', () => { expect(exists('remoteClusterFormGlobalError')).toBe(false); expect(find('remoteClusterFormSaveButton').props().disabled).toBe(false); @@ -144,5 +155,44 @@ describe('Create Remote cluster', () => { expect(form.getErrorsMessages()).toContain('A port is required.'); }); }); + + describe('proxy address', () => { + let actions; + let form; + + beforeEach(async () => { + ({ form, actions } = setup()); + + // Enable "proxy" mode + form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle'); + }); + + test('should only allow alpha-numeric characters and "-" (dash) in the proxy address "host" part', () => { + actions.clickSaveForm(); // display form errors + + const notInArray = array => value => array.indexOf(value) < 0; + + const expectInvalidChar = char => { + form.setInputValue('remoteClusterFormProxyAddressInput', `192.16${char}:3000`); + expect(form.getErrorsMessages()).toContain( + 'Address must use host:port format. Example: 127.0.0.1:9400, localhost:9400. Hosts can only consist of letters, numbers, and dashes.' + ); + }; + + [...NON_ALPHA_NUMERIC_CHARS, ...ACCENTED_CHARS] + .filter(notInArray(['-', '_', ':'])) + .forEach(expectInvalidChar); + }); + + test('should require a numeric "port" to be set', () => { + actions.clickSaveForm(); + + form.setInputValue('remoteClusterFormProxyAddressInput', '192.168.1.1'); + expect(form.getErrorsMessages()).toContain('A port is required.'); + + form.setInputValue('remoteClusterFormProxyAddressInput', '192.168.1.1:abc'); + expect(form.getErrorsMessages()).toContain('A port is required.'); + }); + }); }); }); diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js index b359c952a96f02..954deb8b98d3e1 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js @@ -90,9 +90,22 @@ describe('', () => { proxyAddress: 'localhost:9500', isConfiguredByNode: true, mode: PROXY_MODE, + seeds: null, + connectedNodesCount: null, + }); + const remoteCluster3 = getRemoteClusterMock({ + name: `c${getRandomString()}`, + isConnected: false, + connectedSocketsCount: 0, + proxyAddress: 'localhost:9500', + isConfiguredByNode: false, + mode: PROXY_MODE, + hasDeprecatedProxySetting: true, + seeds: null, + connectedNodesCount: null, }); - const remoteClusters = [remoteCluster1, remoteCluster2]; + const remoteClusters = [remoteCluster1, remoteCluster2, remoteCluster3]; beforeEach(async () => { httpRequestsMockHelpers.setLoadRemoteClustersResponse(remoteClusters); @@ -136,6 +149,15 @@ describe('', () => { remoteCluster2.connectedSocketsCount.toString(), '', ], + [ + '', + remoteCluster3.name, + 'Not connected', + PROXY_MODE, + remoteCluster2.proxyAddress, + remoteCluster2.connectedSocketsCount.toString(), + '', + ], ]); }); @@ -146,6 +168,14 @@ describe('', () => { ).toBe(1); }); + test('should have a tooltip to indicate that the cluster has a deprecated setting', () => { + const secondRow = rows[2].reactWrapper; // The third cluster has been defined with deprecated setting + expect( + findTestSubject(secondRow, 'remoteClustersTableListClusterWithDeprecatedSettingTooltip') + .length + ).toBe(1); + }); + describe('bulk delete button', () => { test('should be visible when a remote cluster is selected', () => { expect(exists('remoteClusterBulkDeleteButton')).toBe(false); @@ -204,8 +234,8 @@ describe('', () => { errors: [], }); - // Make sure that we have our 2 remote clusters in the table - expect(rows.length).toBe(2); + // Make sure that we have our 3 remote clusters in the table + expect(rows.length).toBe(3); actions.selectRemoteClusterAt(0); actions.clickBulkDeleteButton(); @@ -216,7 +246,7 @@ describe('', () => { ({ rows } = table.getMetaData('remoteClusterListTable')); - expect(rows.length).toBe(1); + expect(rows.length).toBe(2); expect(rows[0].columns[1].value).toEqual(remoteCluster2.name); }); }); diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts index 476fbee7fb6a06..3e4c8548f50e9e 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts @@ -60,6 +60,39 @@ describe('cluster_serialization', () => { }); }); + it('should deserialize a cluster that contains a deprecated proxy address', () => { + expect( + deserializeCluster( + 'test_cluster', + { + seeds: ['localhost:9300'], + connected: true, + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + transport: { + ping_schedule: '-1', + compress: false, + }, + }, + 'localhost:9300' + ) + ).toEqual({ + name: 'test_cluster', + proxyAddress: 'localhost:9300', + mode: 'proxy', + hasDeprecatedProxySetting: true, + isConnected: true, + connectedNodesCount: 1, + maxConnectionsPerCluster: 3, + initialConnectTimeout: '30s', + skipUnavailable: false, + transportPingSchedule: '-1', + transportCompress: false, + }); + }); + it('should deserialize a cluster object with arbitrary missing properties', () => { expect( deserializeCluster('test_cluster', { @@ -105,8 +138,13 @@ describe('cluster_serialization', () => { cluster: { remote: { test_cluster: { + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, seeds: ['localhost:9300'], skip_unavailable: false, + server_name: null, }, }, }, @@ -125,8 +163,13 @@ describe('cluster_serialization', () => { cluster: { remote: { test_cluster: { + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, seeds: ['localhost:9300'], skip_unavailable: null, + server_name: null, }, }, }, diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 029fec0989e44d..025e4242b8742d 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -6,10 +6,18 @@ import { PROXY_MODE } from '../constants'; +// interface ClusterEs { +// seeds?: string[]; +// } + +// interface Cluster {} + +// interface ClusterPayload {} + export function deserializeCluster( name: string, esClusterObject: any, - deprecatedProxyAddress: string | undefined + deprecatedProxyAddress?: string | undefined ): any { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); diff --git a/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js index d32be9fc19c458..6a3bcba21d772d 100644 --- a/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js +++ b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js @@ -16,6 +16,7 @@ export const getRemoteClusterMock = ({ isConfiguredByNode = false, mode = SNIFF_MODE, proxyAddress, + hasDeprecatedProxySetting = false, } = {}) => ({ name, seeds, @@ -28,4 +29,5 @@ export const getRemoteClusterMock = ({ mode, connectedSocketsCount, proxyAddress, + hasDeprecatedProxySetting, }); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 88dfe16362ab66..f815a7b1e3d537 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -368,21 +368,46 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u id="xpack.remoteClusters.remoteClusterForm.sectionModeDescription" values={Object {}} /> - - + + , + } + } /> } - onChange={[Function]} - /> + labelType="label" + > + + } + onChange={[Function]} + /> + } fullWidth={true} @@ -469,115 +494,169 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u > Remote cluster connections work by configuring a remote cluster and connecting only to a limited number of nodes in that remote cluster. - -
- - + + , + } + } /> } - onChange={[Function]} + labelType="label" >
- + - - - - - - - - - + +
+
+ - Use proxy mode - - +
+ + + , + } + } + > + Configure a remote cluster with a single proxy address. + + + + +
+
+
-
+ @@ -1178,50 +1257,22 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u size="m" type="cross" > - - - +
- - - +
@@ -1306,25 +1357,12 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u size="m" type="check" > - - - +
diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 8f068bbb4940f8..80413b00fecdd1 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -380,6 +380,7 @@ export class RemoteClusterForm extends Component { )} onChange={e => this.onFieldsChange({ proxyAddress: e.target.value })} isInvalid={Boolean(areErrorsVisible && errorProxyAddress)} + data-test-subj="remoteClusterFormProxyAddressInput" fullWidth /> diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js index 07ccccbd5910c6..9648bd36c1a4ec 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js @@ -25,8 +25,8 @@ export function validateProxy(proxy) { return ( ); } diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js index 65368acbf298fd..ec20805ccd9192 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js @@ -124,7 +124,12 @@ export class RemoteClusterTable extends Component { if (hasDeprecatedProxySetting) { return ( - {link} + + {link} + Date: Fri, 13 Mar 2020 11:32:29 -0400 Subject: [PATCH 12/17] add callout to details panel for deprecated proxy setting --- .../detail_panel/detail_panel.js | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 99df849a979e1d..79da52f712af76 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -7,11 +7,13 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { EuiBadge, EuiButton, EuiButtonEmpty, + EuiCallOut, EuiDescriptionList, EuiDescriptionListDescription, EuiDescriptionListTitle, @@ -22,6 +24,7 @@ import { EuiFlyoutFooter, EuiFlyoutHeader, EuiIcon, + EuiLink, EuiSpacer, EuiText, EuiTextColor, @@ -108,6 +111,45 @@ export class DetailPanel extends Component { ); } + renderClusterWithDeprecatedSettingWarning({ hasDeprecatedProxySetting }, clusterName) { + if (!hasDeprecatedProxySetting) { + return null; + } + return ( + <> + + } + color="warning" + iconType="help" + > + + + + ), + }} + /> + + + + ); + } + renderCluster({ isConnected, connectedNodesCount, @@ -165,7 +207,7 @@ export class DetailPanel extends Component { - {connectedSocketsCount} + {connectedSocketsCount ? connectedSocketsCount : '-'} ) : ( @@ -254,7 +296,7 @@ export class DetailPanel extends Component { - {proxySocketConnections} + {proxySocketConnections ? proxySocketConnections : '-'} ) : ( @@ -295,7 +337,7 @@ export class DetailPanel extends Component { } renderFlyoutBody() { - const { cluster } = this.props; + const { cluster, clusterName } = this.props; return ( @@ -303,6 +345,7 @@ export class DetailPanel extends Component { {cluster && ( {this.renderClusterConfiguredByNodeWarning(cluster)} + {this.renderClusterWithDeprecatedSettingWarning(cluster, clusterName)} {this.renderCluster(cluster)} )} @@ -384,7 +427,7 @@ export class DetailPanel extends Component { onClose={closeDetailPanel} aria-labelledby="remoteClusterDetailsFlyoutTitle" size="m" - maxWidth={400} + maxWidth={550} > @@ -400,7 +443,14 @@ export class DetailPanel extends Component { {cluster && cluster.mode === PROXY_MODE ? ( {' '} - {cluster.mode} + + {cluster.mode} + ) : null} From 0c582ab77276eef4629a41de0a36e0c471000287 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 13 Mar 2020 14:47:29 -0400 Subject: [PATCH 13/17] cleanup and test fixes --- .../common/lib/cluster_serialization.test.ts | 2 + .../common/lib/cluster_serialization.ts | 69 +++++++++++++++---- .../remote_clusters/common/lib/index.ts | 2 +- .../remote_cluster_form.test.js.snap | 2 + .../detail_panel/detail_panel.js | 22 ++++-- .../server/routes/api/add_route.ts | 4 +- .../server/routes/api/get_route.ts | 1 + .../server/routes/api/update_route.ts | 6 +- .../remote_clusters/remote_clusters.js | 7 ++ 9 files changed, 92 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts index 3e4c8548f50e9e..5be6ed8828e6fc 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts @@ -9,6 +9,7 @@ import { deserializeCluster, serializeCluster } from './cluster_serialization'; describe('cluster_serialization', () => { describe('deserializeCluster()', () => { it('should throw an error for invalid arguments', () => { + // @ts-ignore expect(() => deserializeCluster('foo', 'bar')).toThrowError(); }); @@ -117,6 +118,7 @@ describe('cluster_serialization', () => { describe('serializeCluster()', () => { it('should throw an error for invalid arguments', () => { + // @ts-ignore expect(() => serializeCluster('foo')).toThrowError(); }); diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 025e4242b8742d..53dc72eb1695a5 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -6,19 +6,64 @@ import { PROXY_MODE } from '../constants'; -// interface ClusterEs { -// seeds?: string[]; -// } - -// interface Cluster {} +export interface ClusterEs { + seeds?: string[]; + mode?: 'proxy' | 'sniff'; + connected?: boolean; + num_nodes_connected?: number; + max_connections_per_cluster?: number; + initial_connect_timeout?: string; + skip_unavailable?: boolean; + transport?: { + ping_schedule?: string; + compress?: boolean; + }; + address?: string; + max_socket_connections?: number; + num_sockets_connected?: number; +} -// interface ClusterPayload {} +export interface Cluster { + name: string; + seeds?: string[]; + skipUnavailable?: boolean; + nodeConnections?: number; + proxyAddress?: string; + proxySocketConnections?: number; + serverName?: string; + mode?: 'proxy' | 'sniff'; + isConnected?: boolean; + transportPingSchedule?: string; + transportCompress?: boolean; + connectedNodesCount?: number; + maxConnectionsPerCluster?: number; + initialConnectTimeout?: string; + connectedSocketsCount?: number; + hasDeprecatedProxySetting?: boolean; +} +export interface ClusterPayload { + persistent: { + cluster: { + remote: { + [key: string]: { + skip_unavailable?: boolean | null; + mode?: 'sniff' | 'proxy' | null; + proxy_address?: string | null; + proxy_socket_connections?: number | null; + server_name?: string | null; + seeds?: string[] | null; + node_connections?: number | null; + }; + }; + }; + }; +} export function deserializeCluster( name: string, - esClusterObject: any, + esClusterObject: ClusterEs, deprecatedProxyAddress?: string | undefined -): any { +): Cluster { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); } @@ -37,7 +82,7 @@ export function deserializeCluster( num_sockets_connected: connectedSocketsCount, } = esClusterObject; - let deserializedClusterObject: any = { + let deserializedClusterObject: Cluster = { name, mode, isConnected, @@ -75,15 +120,15 @@ export function deserializeCluster( // It's unnecessary to send undefined values back to the client, so we can remove them. Object.keys(deserializedClusterObject).forEach(key => { - if (deserializedClusterObject[key] === undefined) { - delete deserializedClusterObject[key]; + if (deserializedClusterObject[key as keyof Cluster] === undefined) { + delete deserializedClusterObject[key as keyof Cluster]; } }); return deserializedClusterObject; } -export function serializeCluster(deserializedClusterObject: any): any { +export function serializeCluster(deserializedClusterObject: Cluster): ClusterPayload { if (!deserializedClusterObject || typeof deserializedClusterObject !== 'object') { throw new Error('Unable to serialize cluster'); } diff --git a/x-pack/plugins/remote_clusters/common/lib/index.ts b/x-pack/plugins/remote_clusters/common/lib/index.ts index bc67bf21af0384..52a0536bfd55b9 100644 --- a/x-pack/plugins/remote_clusters/common/lib/index.ts +++ b/x-pack/plugins/remote_clusters/common/lib/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { deserializeCluster, serializeCluster } from './cluster_serialization'; +export { deserializeCluster, serializeCluster, Cluster, ClusterEs } from './cluster_serialization'; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index f815a7b1e3d537..59026a73a6ac15 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -736,6 +736,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u > + + + ) : ( diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index 32642030b9d29d..5e0fce82376e0a 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -9,7 +9,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { RequestHandler } from 'src/core/server'; -import { serializeCluster } from '../../../common/lib'; +import { serializeCluster, Cluster } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; import { API_BASE_PATH, PROXY_MODE, SNIFF_MODE } from '../../../common/constants'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; @@ -55,7 +55,7 @@ export const register = (deps: RouteDependencies): void => { }); } - const addClusterPayload = serializeCluster(request.body); + const addClusterPayload = serializeCluster(request.body as Cluster); const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body: addClusterPayload, }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts index 4a9fdc7d3502c9..abd44977d8e46f 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts @@ -40,6 +40,7 @@ export const register = (deps: RouteDependencies): void => { // Pre-7.6, ES supported an undocumented "proxy" field // ES does not handle migrating this to the new implementation, so we need to surface it in the UI + // This value is not available via the GET /_remote/info API, so we get it from the cluster settings const deprecatedProxyAddress = isPersistent ? get(clusterSettings, `persistent.cluster.remote[${clusterName}].proxy`, undefined) : undefined; diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index 5167c27bfa335b..14b161b6f26b5a 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { RequestHandler } from 'src/core/server'; import { API_BASE_PATH, SNIFF_MODE, PROXY_MODE } from '../../../common/constants'; -import { serializeCluster, deserializeCluster } from '../../../common/lib'; +import { serializeCluster, deserializeCluster, Cluster, ClusterEs } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; import { RouteDependencies } from '../../types'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; @@ -61,14 +61,14 @@ export const register = (deps: RouteDependencies): void => { } // Update cluster as new settings - const updateClusterPayload = serializeCluster({ ...request.body, name }); + const updateClusterPayload = serializeCluster({ ...request.body, name } as Cluster); const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body: updateClusterPayload, }); const acknowledged = get(updateClusterResponse, 'acknowledged'); - const cluster = get(updateClusterResponse, `persistent.cluster.remote.${name}`); + const cluster = get(updateClusterResponse, `persistent.cluster.remote.${name}`) as ClusterEs; if (acknowledged && cluster) { const body = { diff --git a/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js b/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js index 677d22ff749847..7921186000e193 100644 --- a/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js +++ b/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js @@ -40,6 +40,7 @@ export default function({ getService }) { name: 'test_cluster', seeds: [NODE_SEED], skipUnavailable: true, + mode: 'sniff', }) .expect(200); @@ -58,6 +59,7 @@ export default function({ getService }) { name: 'test_cluster', seeds: [NODE_SEED], skipUnavailable: false, + mode: 'sniff', }) .expect(409); @@ -79,6 +81,7 @@ export default function({ getService }) { .send({ skipUnavailable: false, seeds: [NODE_SEED], + mode: 'sniff', }) .expect(200); @@ -87,6 +90,7 @@ export default function({ getService }) { skipUnavailable: 'false', // ES issue #35671 seeds: [NODE_SEED], isConfiguredByNode: false, + mode: 'sniff', }); }); }); @@ -109,6 +113,7 @@ export default function({ getService }) { initialConnectTimeout: '30s', skipUnavailable: false, isConfiguredByNode: false, + mode: 'sniff', }, ]); }); @@ -139,6 +144,7 @@ export default function({ getService }) { name: 'test_cluster1', seeds: [NODE_SEED], skipUnavailable: true, + mode: 'sniff', }) .expect(200); @@ -149,6 +155,7 @@ export default function({ getService }) { name: 'test_cluster2', seeds: [NODE_SEED], skipUnavailable: true, + mode: 'sniff', }) .expect(200); From 1bb3b6290f7a0a78501b3968e4416b90b71e4083 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 13 Mar 2020 15:55:07 -0400 Subject: [PATCH 14/17] cleanup detail panel logic --- .../detail_panel/detail_panel.js | 376 +++++++++++------- 1 file changed, 222 insertions(+), 154 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 01e929a4907b74..89a48927f68338 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -162,7 +162,7 @@ export class DetailPanel extends Component { ); } - renderCluster({ + renderSniffModeDescriptionList({ isConnected, connectedNodesCount, skipUnavailable, @@ -170,180 +170,248 @@ export class DetailPanel extends Component { maxConnectionsPerCluster, initialConnectTimeout, mode, + }) { + return ( + + + + + + + + + + + + + + + + + + + + + + + {connectedNodesCount} + + + + + + + + + + + + + + + + {seeds.map(seed => ( + {seed} + ))} + + + + + + + + + + + + {this.renderSkipUnavailableValue(skipUnavailable)} + + + + + + + + + + + + + + + + {maxConnectionsPerCluster} + + + + + + + + + + + + {initialConnectTimeout} + + + + + ); + } + + renderProxyModeDescriptionList({ + isConnected, + skipUnavailable, + initialConnectTimeout, proxyAddress, proxySocketConnections, connectedSocketsCount, + mode, }) { return ( -
- -

- -

-
- - + + + + + + + + - - - - - - - - + + + + - - - - + + + + + + - {mode === PROXY_MODE ? ( - - - - - - + + {connectedSocketsCount ? connectedSocketsCount : '-'} + + + - - {connectedSocketsCount ? connectedSocketsCount : '-'} - - - ) : ( - - - - - - + - - {connectedNodesCount} - - - )} - + + + + + + + - + + {proxyAddress} + + - - {mode === PROXY_MODE ? ( - - - - - - + + + + + + - - {proxyAddress} - - - ) : ( - - - - - - - - - {seeds.map(seed => ( - {seed} - ))} - - - )} + + {this.renderSkipUnavailableValue(skipUnavailable)} + + + - - - - - - + - - {this.renderSkipUnavailableValue(skipUnavailable)} - - - + + + + + + + - + + {proxySocketConnections ? proxySocketConnections : '-'} + + - - {mode === PROXY_MODE ? ( - - - - - - + + + + + + - - {proxySocketConnections ? proxySocketConnections : '-'} - - - ) : ( - - - - - - + + {initialConnectTimeout} + + + + + ); + } - - {maxConnectionsPerCluster} - - - )} + renderCluster(cluster) { + return ( +
+ +

+ +

+
- - - - - - + - - {initialConnectTimeout} - - - - + {cluster.mode === PROXY_MODE + ? this.renderProxyModeDescriptionList(cluster) + : this.renderSniffModeDescriptionList(cluster)}
); } From 4fe853f0f27d579a9b4b506850543540d081497b Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 13 Mar 2020 16:36:42 -0400 Subject: [PATCH 15/17] update jest snapshot --- .../validators/__snapshots__/validate_proxy.test.js.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap index 487a09962753eb..646b0b509f4e46 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap @@ -2,7 +2,7 @@ exports[`validateProxy rejects proxy address when the address is invalid 1`] = ` From 91cd33c88b157fc288aac54aabe7e9ce87030b28 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 16 Mar 2020 12:05:36 -0400 Subject: [PATCH 16/17] address review feedback --- .../components/remote_cluster_form/remote_cluster_form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 80413b00fecdd1..358ffc03da783b 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -282,7 +282,7 @@ export class RemoteClusterForm extends Component { helpText={ @@ -447,7 +447,7 @@ export class RemoteClusterForm extends Component {

From ca41b5f525e418e16bb29771d2f0f546d9de4321 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 16 Mar 2020 13:37:48 -0400 Subject: [PATCH 17/17] update jest snapshots --- .../__snapshots__/remote_cluster_form.test.js.snap | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 59026a73a6ac15..88b869b1d1d8fc 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -417,7 +417,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u >

@@ -433,7 +433,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u - Connection mode settings + Connection mode - Connection mode settings + Connection mode

@@ -1525,7 +1525,7 @@ Array [ aria-hidden="true" class="euiTitle euiTitle--small euiTitle euiTitle--xsmall euiDescribedFormGroup__title" > - Connection mode settings + Connection mode
transport port - of the remote cluster. + of the remote cluster. Specify multiple seed nodes so discovery doesn't fail if a node is unavailable.
@@ -2016,7 +2016,7 @@ Array [ > transport port - of the remote cluster. + of the remote cluster. Specify multiple seed nodes so discovery doesn't fail if a node is unavailable. ,