[Remote clusters] Clean values sent to ES API (#209538)

Fixes https://github.com/elastic/kibana/issues/156354
This commit is contained in:
Sonia Sanz Vivas 2025-02-06 10:44:17 +01:00 committed by GitHub
parent ca390dadc0
commit a1619b3994
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 187 additions and 35 deletions

View file

@ -252,8 +252,6 @@ describe('cluster_serialization', () => {
skip_unavailable: false,
server_name: 'localhost',
proxy: null,
seeds: null,
node_connections: null,
},
},
},
@ -282,11 +280,8 @@ describe('cluster_serialization', () => {
test_cluster: {
mode: 'sniff',
node_connections: null,
proxy_address: null,
proxy_socket_connections: null,
seeds: ['localhost:9300'],
skip_unavailable: false,
server_name: null,
},
},
},
@ -307,10 +302,69 @@ describe('cluster_serialization', () => {
test_cluster: {
mode: null,
node_connections: null,
proxy_address: null,
proxy_socket_connections: null,
seeds: ['localhost:9300'],
skip_unavailable: null,
},
},
},
},
});
});
it('should serialize a cluster object that will be deleted', () => {
expect(
serializeCluster(
{
name: 'test_cluster',
seeds: ['localhost:9300'],
},
undefined,
true
)
).toEqual({
persistent: {
cluster: {
remote: {
test_cluster: {
mode: null,
node_connections: null,
seeds: ['localhost:9300'],
skip_unavailable: null,
proxy_address: null,
proxy_socket_connections: null,
server_name: null,
},
},
},
},
});
});
it('should serialize a cluster object that has modified mode', () => {
expect(
serializeCluster(
{
name: 'test_cluster',
seeds: ['localhost:9300'],
isConnected: true,
connectedNodesCount: 1,
maxConnectionsPerCluster: 3,
mode: 'sniff',
nodeConnections: 18,
},
'proxy'
)
).toEqual({
persistent: {
cluster: {
remote: {
test_cluster: {
mode: 'sniff',
node_connections: 18,
seeds: ['localhost:9300'],
skip_unavailable: null,
proxy_address: null,
proxy_socket_connections: null,
server_name: null,
},
},

View file

@ -153,7 +153,9 @@ export function deserializeCluster(
}
export function serializeCluster(
deserializedClusterObject: ClusterPayload
deserializedClusterObject: ClusterPayload,
previousClusterMode?: 'proxy' | 'sniff',
isDelete?: boolean
): ClusterSettingsPayloadEs {
if (!deserializedClusterObject || typeof deserializedClusterObject !== 'object') {
throw new Error('Unable to serialize cluster');
@ -174,13 +176,28 @@ export function serializeCluster(
const clusterData: ClusterPayloadEs = {
skip_unavailable: typeof skipUnavailable === 'boolean' ? skipUnavailable : null,
mode: mode || null,
proxy_address: proxyAddress || null,
proxy_socket_connections: proxySocketConnections || null,
server_name: serverName || null,
seeds: seeds || null,
node_connections: nodeConnections || null,
...(mode === PROXY_MODE
? {
proxy_address: proxyAddress || null,
proxy_socket_connections: proxySocketConnections || null,
server_name: serverName || null,
}
: {
seeds: seeds || null,
node_connections: nodeConnections || null,
}),
};
// If the cluster is been deleted, we need to set all values to null
// If the cluster is been edited and the mode has changed, we need to set to null the previous mode settings values
if (isDelete || (previousClusterMode && previousClusterMode !== mode)) {
clusterData.proxy_address = proxyAddress || null;
clusterData.proxy_socket_connections = proxySocketConnections || null;
clusterData.server_name = serverName || null;
clusterData.seeds = seeds || null;
clusterData.node_connections = nodeConnections || null;
}
// This is only applicable in edit mode
// In order to "upgrade" an existing remote cluster to use the new proxy mode settings, we need to set the old proxy setting to null
if (hasDeprecatedProxySetting) {

View file

@ -6,6 +6,7 @@
*/
import React, { ReactNode, useCallback, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { PROXY_MODE, SNIFF_MODE } from '../../../../../../common/constants';
import {
EuiButton,
EuiButtonEmpty,
@ -26,6 +27,7 @@ interface Props {
cluster?: ClusterPayload;
nextButtonTestSubj: string;
backButtonTestSubj?: string;
previousClusterMode?: typeof PROXY_MODE | typeof SNIFF_MODE;
}
export const ActionButtons: React.FC<Props> = ({
@ -39,6 +41,7 @@ export const ActionButtons: React.FC<Props> = ({
cluster,
nextButtonTestSubj,
backButtonTestSubj,
previousClusterMode,
}) => {
const [isRequestVisible, setIsRequestVisible] = useState(false);
const toggleRequest = useCallback(() => {
@ -92,7 +95,11 @@ export const ActionButtons: React.FC<Props> = ({
)}
</EuiFlexItem>
{isRequestVisible && cluster ? (
<RequestFlyout cluster={cluster} close={() => setIsRequestVisible(false)} />
<RequestFlyout
cluster={cluster}
close={() => setIsRequestVisible(false)}
previousClusterMode={previousClusterMode}
/>
) : null}
</EuiFlexGroup>
);

View file

@ -21,10 +21,12 @@ import {
} from '@elastic/eui';
import { ClusterPayload, serializeCluster } from '../../../../../../common/lib';
import { SNIFF_MODE, PROXY_MODE } from '../../../../../../common/constants';
interface Props {
close: () => void;
cluster: ClusterPayload;
previousClusterMode?: typeof PROXY_MODE | typeof SNIFF_MODE;
}
export class RequestFlyout extends PureComponent<Props> {
@ -32,7 +34,11 @@ export class RequestFlyout extends PureComponent<Props> {
const { close, cluster } = this.props;
const { name } = cluster;
const endpoint = 'PUT _cluster/settings';
const payload = JSON.stringify(serializeCluster(cluster), null, 2);
const payload = JSON.stringify(
serializeCluster(cluster, this.props.previousClusterMode),
null,
2
);
const request = `${endpoint}\n${payload}`;
return (

View file

@ -438,6 +438,7 @@ export const RemoteClusterForm: React.FC<Props> = ({
cluster={getCluster()}
nextButtonTestSubj={'remoteClusterFormNextButton'}
backButtonTestSubj={'remoteClusterFormBackButton'}
previousClusterMode={cluster?.mode}
/>
</>
);

View file

@ -124,9 +124,6 @@ describe('ADD remote clusters', () => {
skip_unavailable: false,
mode: 'sniff',
node_connections: null,
proxy_address: null,
proxy_socket_connections: null,
server_name: null,
},
},
},
@ -181,10 +178,8 @@ describe('ADD remote clusters', () => {
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',
@ -256,9 +251,6 @@ describe('ADD remote clusters', () => {
skip_unavailable: false,
mode: 'sniff',
node_connections: null,
proxy_address: null,
proxy_socket_connections: null,
server_name: null,
},
},
},

View file

@ -72,7 +72,7 @@ export const register = (deps: RouteDependencies): void => {
hasDeprecatedProxySetting: boolean
) => {
try {
const body = serializeCluster({ name, hasDeprecatedProxySetting });
const body = serializeCluster({ name, hasDeprecatedProxySetting }, undefined, true);
const updateClusterResponse = await clusterClient.asCurrentUser.cluster.putSettings({
body,

View file

@ -141,8 +141,83 @@ describe('UPDATE remote clusters', () => {
skip_unavailable: true,
mode: 'sniff',
node_connections: null,
proxy_address: null,
proxy_socket_connections: null,
},
},
},
},
},
});
});
test('updates remote cluster with mode change', async () => {
remoteInfoMockFn.mockResponseOnce({
test: {
connected: true,
mode: 'sniff',
seeds: ['127.0.0.1:9300'],
num_nodes_connected: 1,
max_connections_per_cluster: 3,
initial_connect_timeout: '30s',
skip_unavailable: false,
},
});
putSettingsMockFn.mockResponseOnce({
acknowledged: true,
persistent: {
cluster: {
remote: {
test: {
connected: true,
proxy_address: '127.0.0.1:9300',
initial_connect_timeout: '30s',
skip_unavailable: true,
mode: 'proxy',
proxy_socket_connections: 18,
},
},
},
},
transient: {},
});
const mockRequest = createMockRequest({
proxyAddress: '127.0.0.1:9300',
skipUnavailable: true,
mode: 'proxy',
proxySocketConnections: 18,
});
const response = await handler(
coreMock.createCustomRequestHandlerContext(mockContext),
mockRequest,
kibanaResponseFactory
);
expect(response.status).toBe(200);
expect(response.payload).toEqual({
initialConnectTimeout: '30s',
isConfiguredByNode: false,
isConnected: true,
proxyAddress: '127.0.0.1:9300',
name: 'test',
skipUnavailable: true,
mode: 'proxy',
securityModel: SECURITY_MODEL.CERTIFICATE,
});
expect(remoteInfoMockFn).toHaveBeenCalledWith();
expect(putSettingsMockFn).toHaveBeenCalledWith({
body: {
persistent: {
cluster: {
remote: {
test: {
proxy_address: '127.0.0.1:9300',
skip_unavailable: true,
mode: 'proxy',
proxy_socket_connections: 18,
node_connections: null,
seeds: null,
server_name: null,
},
},
@ -220,8 +295,6 @@ describe('UPDATE remote clusters', () => {
proxy_address: '127.0.0.1:9300',
skip_unavailable: true,
mode: 'proxy',
node_connections: null,
seeds: null,
proxy_socket_connections: 18,
server_name: null,
proxy: null,
@ -301,9 +374,6 @@ describe('UPDATE remote clusters', () => {
skip_unavailable: false,
mode: 'sniff',
node_connections: null,
proxy_address: null,
proxy_socket_connections: null,
server_name: null,
},
},
},

View file

@ -12,7 +12,6 @@ import { RequestHandler } from '@kbn/core/server';
import { API_BASE_PATH, SNIFF_MODE, PROXY_MODE } from '../../../common/constants';
import { serializeCluster, deserializeCluster, Cluster, ClusterInfoEs } from '../../../common/lib';
import { doesClusterExist } from '../../lib/does_cluster_exist';
import { RouteDependencies } from '../../types';
import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory';
@ -52,7 +51,8 @@ export const register = (deps: RouteDependencies): void => {
const { name } = request.params;
// Check if cluster does exist.
const existingCluster = await doesClusterExist(clusterClient, name);
const previousClusterConfig = await clusterClient.asCurrentUser.cluster.remoteInfo();
const existingCluster = Boolean(previousClusterConfig && previousClusterConfig[name]);
if (!existingCluster) {
return response.notFound({
body: {
@ -65,9 +65,14 @@ export const register = (deps: RouteDependencies): void => {
},
});
}
// Update cluster as new settings
const updateClusterPayload = serializeCluster({ ...request.body, name } as Cluster);
const updateClusterPayload = serializeCluster(
{
...request.body,
name,
} as Cluster,
previousClusterConfig[name].mode
);
const updateClusterResponse = await clusterClient.asCurrentUser.cluster.putSettings({
body: updateClusterPayload,