Skip to content

Commit dba67fd

Browse files
add warning when user has remote clusters configured
1 parent 45be560 commit dba67fd

File tree

12 files changed

+278
-7
lines changed

12 files changed

+278
-7
lines changed

x-pack/plugins/remote_clusters/kibana.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"name": "Stack Management",
77
"githubTeam": "kibana-stack-management"
88
},
9-
"requiredPlugins": ["licensing", "management", "indexManagement", "features"],
9+
"requiredPlugins": ["licensing", "management", "indexManagement", "features", "share"],
1010
"optionalPlugins": ["usageCollection", "cloud"],
1111
"server": true,
1212
"ui": true,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import type { SerializableRecord } from '@kbn/utility-types';
9+
import { ManagementAppLocator } from 'src/plugins/management/common';
10+
import { LocatorDefinition } from '../../../../src/plugins/share/public/';
11+
import { PLUGIN } from '../common/constants';
12+
13+
export const REMOTE_CLUSTERS_LOCATOR_ID = 'REMOTE_CLUSTERS_LOCATOR';
14+
15+
export interface RemoteClustersLocatorParams extends SerializableRecord {
16+
page: 'remoteClusters';
17+
}
18+
19+
export interface RemoteClustersLocatorDefinitionDependencies {
20+
managementAppLocator: ManagementAppLocator;
21+
}
22+
23+
export class RemoteClustersLocatorDefinition
24+
implements LocatorDefinition<RemoteClustersLocatorParams>
25+
{
26+
constructor(protected readonly deps: RemoteClustersLocatorDefinitionDependencies) {}
27+
28+
public readonly id = REMOTE_CLUSTERS_LOCATOR_ID;
29+
30+
public readonly getLocation = async (params: RemoteClustersLocatorParams) => {
31+
const location = await this.deps.managementAppLocator.getLocation({
32+
sectionId: 'data',
33+
appId: 'remote_clusters',
34+
});
35+
36+
switch (params.page) {
37+
case 'remoteClusters': {
38+
return {
39+
...location,
40+
path: location.path,
41+
};
42+
}
43+
}
44+
};
45+
}

x-pack/plugins/remote_clusters/public/plugin.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { init as initUiMetric } from './application/services/ui_metric';
1616
import { init as initNotification } from './application/services/notification';
1717
import { init as initRedirect } from './application/services/redirect';
1818
import { Dependencies, ClientConfigType } from './types';
19+
import { RemoteClustersLocatorDefinition } from './locator';
1920

2021
export interface RemoteClustersPluginSetup {
2122
isUiEnabled: boolean;
@@ -28,7 +29,7 @@ export class RemoteClustersUIPlugin
2829

2930
setup(
3031
{ notifications: { toasts }, http, getStartServices }: CoreSetup,
31-
{ management, usageCollection, cloud }: Dependencies
32+
{ management, usageCollection, cloud, share }: Dependencies
3233
) {
3334
const {
3435
ui: { enabled: isRemoteClustersUiEnabled },
@@ -78,6 +79,12 @@ export class RemoteClustersUIPlugin
7879
};
7980
},
8081
});
82+
83+
share.url.locators.create(
84+
new RemoteClustersLocatorDefinition({
85+
managementAppLocator: management.locator,
86+
})
87+
);
8188
}
8289

8390
return {

x-pack/plugins/remote_clusters/public/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import { ManagementSetup } from 'src/plugins/management/public';
99
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
1010
import { RegisterManagementAppArgs } from 'src/plugins/management/public';
11+
import { SharePluginSetup } from 'src/plugins/share/public';
1112
import { I18nStart } from 'kibana/public';
1213
import { CloudSetup } from '../../cloud/public';
1314

1415
export interface Dependencies {
1516
management: ManagementSetup;
1617
usageCollection: UsageCollectionSetup;
1718
cloud: CloudSetup;
19+
share: SharePluginSetup;
1820
}
1921

2022
export interface ClientConfigType {

x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('ES deprecations table', () => {
4444
aliases: [],
4545
},
4646
});
47+
httpRequestsMockHelpers.setLoadRemoteClustersResponse([]);
4748

4849
await act(async () => {
4950
testBed = await setupElasticsearchPage({ isReadOnlyMode: false });
@@ -105,6 +106,27 @@ describe('ES deprecations table', () => {
105106
expect(find('warningDeprecationsCount').text()).toContain(warningDeprecations.length);
106107
});
107108

109+
describe('remote clusters callout', () => {
110+
beforeEach(async () => {
111+
const { exists, find, component } = testBed;
112+
113+
httpRequestsMockHelpers.setLoadRemoteClustersResponse(['test_remote_cluster']);
114+
115+
await act(async () => {
116+
testBed = await setupElasticsearchPage({ isReadOnlyMode: false });
117+
});
118+
119+
testBed.component.update();
120+
});
121+
122+
it('shows a warning message if a user has remote clusters configured', () => {
123+
const { exists, find } = testBed;
124+
125+
// Verify warning exists
126+
expect(exists('remoteClustersWarningCallout')).toBe(true);
127+
});
128+
});
129+
108130
describe('search bar', () => {
109131
it('filters results by "critical" status', async () => {
110132
const { find, actions } = testBed;

x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
203203
]);
204204
};
205205

206+
const setLoadRemoteClustersResponse = (response?: object, error?: ResponseError) => {
207+
const status = error ? error.statusCode || 400 : 200;
208+
const body = error ? error : response;
209+
210+
server.respondWith('GET', `${API_BASE_PATH}/remote_clusters`, [
211+
status,
212+
{ 'Content-Type': 'application/json' },
213+
JSON.stringify(body),
214+
]);
215+
};
216+
206217
return {
207218
setLoadCloudBackupStatusResponse,
208219
setLoadEsDeprecationsResponse,
@@ -220,6 +231,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
220231
setReindexStatusResponse,
221232
setLoadMlUpgradeModeResponse,
222233
setGetUpgradeStatusResponse,
234+
setLoadRemoteClustersResponse,
223235
};
224236
};
225237

x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import React, { useEffect, useMemo } from 'react';
99
import { withRouter, RouteComponentProps } from 'react-router-dom';
1010

11-
import { EuiPageHeader, EuiSpacer, EuiPageContent, EuiLink } from '@elastic/eui';
11+
import { EuiPageHeader, EuiSpacer, EuiPageContent, EuiLink, EuiCallOut } from '@elastic/eui';
1212
import { i18n } from '@kbn/i18n';
1313
import { FormattedMessage } from '@kbn/i18n/react';
1414
import { DocLinksStart } from 'kibana/public';
@@ -51,6 +51,26 @@ const i18nTexts = {
5151
isLoading: i18n.translate('xpack.upgradeAssistant.esDeprecations.loadingText', {
5252
defaultMessage: 'Loading deprecation issues…',
5353
}),
54+
remoteClustersDetectedTitle: i18n.translate(
55+
'xpack.upgradeAssistant.esDeprecations.remoteClustersDetectedTitle',
56+
{
57+
defaultMessage: 'Remote cluster compatibility',
58+
}
59+
),
60+
getRemoteClustersDetectedDescription: (remoteClustersCount: number) =>
61+
i18n.translate('xpack.upgradeAssistant.esDeprecations.remoteClustersDetectedDescription', {
62+
defaultMessage:
63+
'You have {remoteClustersCount} {remoteClustersCount, plural, one {remote cluster} other {remote clusters}} configured. If you use cross-cluster search, note that 8.x can only search remote clusters 7.17 or later. If you use cross-cluster replication, a cluster that contains follower indices must run the same or newer version as the remote cluster.',
64+
values: {
65+
remoteClustersCount,
66+
},
67+
}),
68+
remoteClustersLinkText: i18n.translate(
69+
'xpack.upgradeAssistant.esDeprecations.remoteClustersLinkText',
70+
{
71+
defaultMessage: 'View remote clusters.',
72+
}
73+
),
5474
};
5575

5676
const getBatchReindexLink = (docLinks: DocLinksStart) => {
@@ -75,6 +95,22 @@ const getBatchReindexLink = (docLinks: DocLinksStart) => {
7595
);
7696
};
7797

98+
const RemoteClustersAppLink: React.FunctionComponent = () => {
99+
const {
100+
plugins: { share },
101+
} = useAppContext();
102+
103+
const remoteClustersUrl = share.url.locators
104+
.get('REMOTE_CLUSTERS_LOCATOR')
105+
?.useUrl({ page: 'remoteClusters' });
106+
107+
return (
108+
<EuiLink href={remoteClustersUrl} data-test-subj="remoteClustersLink">
109+
{i18nTexts.remoteClustersLinkText}
110+
</EuiLink>
111+
);
112+
};
113+
78114
export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
79115
const {
80116
services: {
@@ -85,6 +121,7 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
85121
} = useAppContext();
86122

87123
const { data: esDeprecations, isLoading, error, resendRequest } = api.useLoadEsDeprecations();
124+
const { data: remoteClusters } = api.useLoadRemoteClusters();
88125

89126
const deprecationsCountByLevel: {
90127
warningDeprecations: number;
@@ -140,10 +177,29 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
140177
</>
141178
}
142179
>
143-
<DeprecationCount
144-
totalCriticalDeprecations={deprecationsCountByLevel.criticalDeprecations}
145-
totalWarningDeprecations={deprecationsCountByLevel.warningDeprecations}
146-
/>
180+
<>
181+
{remoteClusters && remoteClusters.length > 0 && (
182+
<>
183+
<EuiCallOut
184+
title={i18nTexts.remoteClustersDetectedTitle}
185+
color="warning"
186+
iconType="help"
187+
data-test-subj="remoteClustersWarningCallout"
188+
>
189+
<p>
190+
{i18nTexts.getRemoteClustersDetectedDescription(remoteClusters.length)}{' '}
191+
<RemoteClustersAppLink />
192+
</p>
193+
</EuiCallOut>
194+
<EuiSpacer />
195+
</>
196+
)}
197+
198+
<DeprecationCount
199+
totalCriticalDeprecations={deprecationsCountByLevel.criticalDeprecations}
200+
totalWarningDeprecations={deprecationsCountByLevel.warningDeprecations}
201+
/>
202+
</>
147203
</EuiPageHeader>
148204

149205
<EuiSpacer size="l" />

x-pack/plugins/upgrade_assistant/public/application/lib/api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@ export class ApiService {
239239
method: 'get',
240240
});
241241
}
242+
243+
public useLoadRemoteClusters() {
244+
return this.useRequest<string[]>({
245+
path: `${API_BASE_PATH}/remote_clusters`,
246+
method: 'get',
247+
});
248+
}
242249
}
243250

244251
export const apiService = new ApiService();

x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { registerUpdateSettingsRoute } from './update_index_settings';
1818
import { registerMlSnapshotRoutes } from './ml_snapshots';
1919
import { ReindexWorker } from '../lib/reindexing';
2020
import { registerUpgradeStatusRoute } from './status';
21+
import { registerRemoteClustersRoute } from './remote_clusters';
2122

2223
export function registerRoutes(dependencies: RouteDependencies, getWorker: () => ReindexWorker) {
2324
registerAppRoutes(dependencies);
@@ -32,4 +33,5 @@ export function registerRoutes(dependencies: RouteDependencies, getWorker: () =>
3233
registerMlSnapshotRoutes(dependencies);
3334
// Route for cloud to retrieve the upgrade status for ES and Kibana
3435
registerUpgradeStatusRoute(dependencies);
36+
registerRemoteClustersRoute(dependencies);
3537
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { API_BASE_PATH } from '../../common/constants';
9+
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
10+
import { RouteDependencies } from '../types';
11+
12+
export function registerRemoteClustersRoute({ router, lib: { handleEsError } }: RouteDependencies) {
13+
router.get(
14+
{
15+
path: `${API_BASE_PATH}/remote_clusters`,
16+
validate: false,
17+
},
18+
versionCheckHandlerWrapper(
19+
async (
20+
{
21+
core: {
22+
elasticsearch: { client },
23+
},
24+
},
25+
request,
26+
response
27+
) => {
28+
try {
29+
const { body: clustersByName } = await client.asCurrentUser.cluster.remoteInfo();
30+
31+
const remoteClusters = Object.keys(clustersByName);
32+
33+
return response.ok({ body: remoteClusters });
34+
} catch (error) {
35+
return handleEsError({ error, response });
36+
}
37+
}
38+
)
39+
);
40+
}

0 commit comments

Comments
 (0)