Skip to content

Commit

Permalink
Validate max number of node connections (#196908)
Browse files Browse the repository at this point in the history
#Closes [#110155](#110155)

## Summary
Currently Elastic Search has a limitation of 2^31-1 (2147483647) maximum
node connections. This PR adds this hardcoded value and is used to
validate that the input does not exceed this value and, therefore, the
user does not have to wait for the server to return the error to know
that they have entered a number that is too high. ES does not have an
API to query the number, that's why it is hardcoded.

<img width="1500" alt="Screenshot 2024-10-18 at 17 13 14"
src="https://github.com/user-attachments/assets/c18a4756-df76-4e0e-ba31-5118208f686d">
  • Loading branch information
SoniaSanzV authored Oct 21, 2024
1 parent 46eda4c commit 860e445
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { act } from 'react-dom/test-utils';
import { setupEnvironment, RemoteClustersActions } from '../helpers';
import { setup } from './remote_clusters_add.helpers';
import { NON_ALPHA_NUMERIC_CHARS, ACCENTED_CHARS } from './special_characters';
import { MAX_NODE_CONNECTIONS } from '../../../common/constants';

const notInArray = (array: string[]) => (value: string) => array.indexOf(value) < 0;

Expand Down Expand Up @@ -276,6 +277,17 @@ describe('Create Remote cluster', () => {
});
});

describe('node connections', () => {
test('should require a valid number of node connections', async () => {
await actions.saveButton.click();

actions.nodeConnectionsInput.setValue(String(MAX_NODE_CONNECTIONS + 1));
expect(actions.getErrorMessages()).toContain(
`This number must be equal or less than ${MAX_NODE_CONNECTIONS}.`
);
});
});

test('server name is optional (proxy connection)', () => {
actions.connectionModeSwitch.toggle();
actions.saveButton.click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export interface RemoteClustersActions {
setValue: (seed: string) => void;
getValue: () => string;
};
nodeConnectionsInput: {
setValue: (connections: string) => void;
};
proxyAddressInput: {
setValue: (proxyAddress: string) => void;
exists: () => boolean;
Expand Down Expand Up @@ -154,6 +157,16 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct
};
};

const createNodeConnectionsInputActions = () => {
const nodeConnectionsInputSelector = 'remoteClusterFormNodeConnectionsInput';
return {
nodeConnectionsInput: {
setValue: (connections: string) =>
form.setInputValue(nodeConnectionsInputSelector, connections),
},
};
};

const createProxyAddressActions = () => {
const proxyAddressSelector = 'remoteClusterFormProxyAddressInput';
return {
Expand Down Expand Up @@ -266,6 +279,7 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct
...createConnectionModeActions(),
...createCloudAdvancedOptionsSwitchActions(),
...createSeedsInputActions(),
...createNodeConnectionsInputActions(),
...createCloudRemoteAddressInputActions(),
...createProxyAddressActions(),
...createServerNameActions(),
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/remote_clusters/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ export const getSecurityModel = (type: string) => {

return type;
};

// Hardcoded limit of maximum node connections allowed
export const MAX_NODE_CONNECTIONS = 2 ** 31 - 1; // 2147483647
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@ export const SniffConnection: FunctionComponent<Props> = ({
}) => {
const [localSeedErrors, setLocalSeedErrors] = useState<JSX.Element[]>([]);
const { seeds = [], nodeConnections } = fields;
const { seeds: seedsError } = fieldsErrors;
const { seeds: seedsError, nodeConnections: nodeError } = fieldsErrors;
// Show errors if there is a general form error or local errors.
const areFormErrorsVisible = Boolean(areErrorsVisible && seedsError);
const showErrors = areFormErrorsVisible || localSeedErrors.length !== 0;
const errors =
const showLocalSeedErrors = areFormErrorsVisible || localSeedErrors.length !== 0;
const errorsInLocalSeeds =
areFormErrorsVisible && seedsError ? localSeedErrors.concat(seedsError) : localSeedErrors;
const formattedSeeds: EuiComboBoxOptionOption[] = seeds.map((seed: string) => ({ label: seed }));

const showNodeConnectionErrors = Boolean(nodeError);

const onCreateSeed = (newSeed?: string) => {
// If the user just hit enter without typing anything, treat it as a no-op.
if (!newSeed) {
Expand Down Expand Up @@ -107,8 +109,8 @@ export const SniffConnection: FunctionComponent<Props> = ({
}}
/>
}
isInvalid={showErrors}
error={errors}
isInvalid={showLocalSeedErrors}
error={errorsInLocalSeeds}
fullWidth
>
<EuiComboBox
Expand All @@ -125,7 +127,7 @@ export const SniffConnection: FunctionComponent<Props> = ({
onFieldsChange({ seeds: options.map(({ label }) => label) })
}
onSearchChange={onSeedsInputChange}
isInvalid={showErrors}
isInvalid={showLocalSeedErrors}
fullWidth
data-test-subj="remoteClusterFormSeedsInput"
/>
Expand All @@ -146,11 +148,15 @@ export const SniffConnection: FunctionComponent<Props> = ({
/>
}
fullWidth
isInvalid={showNodeConnectionErrors}
error={nodeError}
>
<EuiFieldNumber
value={nodeConnections || ''}
onChange={(e) => onFieldsChange({ nodeConnections: Number(e.target.value) })}
isInvalid={showNodeConnectionErrors}
fullWidth
data-test-subj="remoteClusterFormNodeConnectionsInput"
/>
</EuiFormRow>
</>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export {
validateCloudRemoteAddress,
convertCloudRemoteAddressToProxyConnection,
} from './validate_cloud_url';
export { validateNodeConnections } from './validate_node_connections';
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { validateSeeds } from './validate_seeds';
import { validateProxy } from './validate_proxy';
import { validateCloudRemoteAddress } from './validate_cloud_url';
import { FormFields } from '../remote_cluster_form';
import { validateNodeConnections } from './validate_node_connections';

type ClusterError = JSX.Element | null;

Expand All @@ -19,14 +20,16 @@ export interface ClusterErrors {
seeds?: ClusterError;
proxyAddress?: ClusterError;
cloudRemoteAddress?: ClusterError;
nodeConnections?: ClusterError;
}
export const validateCluster = (fields: FormFields, isCloudEnabled: boolean): ClusterErrors => {
const { name, seeds = [], mode, proxyAddress, cloudRemoteAddress } = fields;
const { name, seeds = [], mode, proxyAddress, cloudRemoteAddress, nodeConnections } = fields;

return {
name: validateName(name),
seeds: mode === SNIFF_MODE ? validateSeeds(seeds) : null,
proxyAddress: mode === PROXY_MODE ? validateProxy(proxyAddress) : null,
cloudRemoteAddress: isCloudEnabled ? validateCloudRemoteAddress(cloudRemoteAddress) : null,
nodeConnections: mode === SNIFF_MODE ? validateNodeConnections(nodeConnections) : null,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { MAX_NODE_CONNECTIONS } from '../../../../../../common/constants';
import { validateNodeConnections } from './validate_node_connections';

describe('validateNodeConnections', () => {
test('rejects numbers larger than MaxValue', () => {
expect(validateNodeConnections(MAX_NODE_CONNECTIONS + 1)).toMatchSnapshot();
});

test('accepts numbers equal than MaxValue', () => {
expect(validateNodeConnections(MAX_NODE_CONNECTIONS)).toBe(null);
});

test('accepts numbers smaller than MaxValue', () => {
expect(validateNodeConnections(MAX_NODE_CONNECTIONS - 1)).toBe(null);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { MAX_NODE_CONNECTIONS } from '../../../../../../common/constants';

export function validateNodeConnections(connections?: number | null): null | JSX.Element {
if (connections && connections > MAX_NODE_CONNECTIONS) {
return (
<FormattedMessage
id="xpack.remoteClusters.form.errors.maxValue"
defaultMessage="This number must be equal or less than {maxValue}."
values={{
maxValue: MAX_NODE_CONNECTIONS,
}}
/>
);
}

return null;
}
2 changes: 1 addition & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -47476,4 +47476,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "Ce champ est requis.",
"xpack.watcher.watcherDescription": "Détectez les modifications survenant dans vos données en créant, gérant et monitorant des alertes."
}
}
}
2 changes: 1 addition & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -47214,4 +47214,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。",
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
}
}
}
2 changes: 1 addition & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -47267,4 +47267,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。",
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
}
}
}

0 comments on commit 860e445

Please sign in to comment.