Skip to content

Commit

Permalink
[Upgrade Assistant] Add warning when remote clusters are configured (e…
Browse files Browse the repository at this point in the history
  • Loading branch information
alisonelizabeth committed Feb 10, 2022
1 parent bc01e77 commit f5e6b4a
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 7 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/remote_clusters/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"name": "Stack Management",
"githubTeam": "kibana-stack-management"
},
"requiredPlugins": ["licensing", "management", "indexManagement", "features"],
"requiredPlugins": ["licensing", "management", "indexManagement", "features", "share"],
"optionalPlugins": ["usageCollection", "cloud"],
"server": true,
"ui": true,
Expand Down
44 changes: 44 additions & 0 deletions x-pack/plugins/remote_clusters/public/locator.ts
Original file line number Diff line number Diff line change
@@ -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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { SerializableRecord } from '@kbn/utility-types';
import { ManagementAppLocator } from 'src/plugins/management/common';
import { LocatorDefinition } from '../../../../src/plugins/share/public/';

export const REMOTE_CLUSTERS_LOCATOR_ID = 'REMOTE_CLUSTERS_LOCATOR';

export interface RemoteClustersLocatorParams extends SerializableRecord {
page: 'remoteClusters';
}

export interface RemoteClustersLocatorDefinitionDependencies {
managementAppLocator: ManagementAppLocator;
}

export class RemoteClustersLocatorDefinition
implements LocatorDefinition<RemoteClustersLocatorParams>
{
constructor(protected readonly deps: RemoteClustersLocatorDefinitionDependencies) {}

public readonly id = REMOTE_CLUSTERS_LOCATOR_ID;

public readonly getLocation = async (params: RemoteClustersLocatorParams) => {
const location = await this.deps.managementAppLocator.getLocation({
sectionId: 'data',
appId: 'remote_clusters',
});

switch (params.page) {
case 'remoteClusters': {
return {
...location,
path: location.path,
};
}
}
};
}
9 changes: 8 additions & 1 deletion x-pack/plugins/remote_clusters/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { init as initUiMetric } from './application/services/ui_metric';
import { init as initNotification } from './application/services/notification';
import { init as initRedirect } from './application/services/redirect';
import { Dependencies, ClientConfigType } from './types';
import { RemoteClustersLocatorDefinition } from './locator';

export interface RemoteClustersPluginSetup {
isUiEnabled: boolean;
Expand All @@ -28,7 +29,7 @@ export class RemoteClustersUIPlugin

setup(
{ notifications: { toasts }, http, getStartServices }: CoreSetup,
{ management, usageCollection, cloud }: Dependencies
{ management, usageCollection, cloud, share }: Dependencies
) {
const {
ui: { enabled: isRemoteClustersUiEnabled },
Expand Down Expand Up @@ -79,6 +80,12 @@ export class RemoteClustersUIPlugin
};
},
});

share.url.locators.create(
new RemoteClustersLocatorDefinition({
managementAppLocator: management.locator,
})
);
}

return {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/remote_clusters/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import { ManagementSetup } from 'src/plugins/management/public';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
import { RegisterManagementAppArgs } from 'src/plugins/management/public';
import { SharePluginSetup } from 'src/plugins/share/public';
import { I18nStart } from 'kibana/public';
import { CloudSetup } from '../../cloud/public';

export interface Dependencies {
management: ManagementSetup;
usageCollection: UsageCollectionSetup;
cloud: CloudSetup;
share: SharePluginSetup;
}

export interface ClientConfigType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('ES deprecations table', () => {
aliases: [],
},
});
httpRequestsMockHelpers.setLoadRemoteClustersResponse([]);

await act(async () => {
testBed = await setupElasticsearchPage({ isReadOnlyMode: false });
Expand Down Expand Up @@ -105,6 +106,25 @@ describe('ES deprecations table', () => {
expect(find('warningDeprecationsCount').text()).toContain(warningDeprecations.length);
});

describe('remote clusters callout', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadRemoteClustersResponse(['test_remote_cluster']);

await act(async () => {
testBed = await setupElasticsearchPage({ isReadOnlyMode: false });
});

testBed.component.update();
});

it('shows a warning message if a user has remote clusters configured', () => {
const { exists } = testBed;

// Verify warning exists
expect(exists('remoteClustersWarningCallout')).toBe(true);
});
});

describe('search bar', () => {
it('filters results by "critical" status', async () => {
const { find, actions } = testBed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,17 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
]);
};

const setLoadRemoteClustersResponse = (response?: object, error?: ResponseError) => {
const status = error ? error.statusCode || 400 : 200;
const body = error ? error : response;

server.respondWith('GET', `${API_BASE_PATH}/remote_clusters`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(body),
]);
};

return {
setLoadCloudBackupStatusResponse,
setLoadEsDeprecationsResponse,
Expand All @@ -220,6 +231,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
setReindexStatusResponse,
setLoadMlUpgradeModeResponse,
setGetUpgradeStatusResponse,
setLoadRemoteClustersResponse,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import React, { useEffect, useMemo } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';

import { EuiPageHeader, EuiSpacer, EuiPageContent, EuiLink } from '@elastic/eui';
import { EuiPageHeader, EuiSpacer, EuiPageContent, EuiLink, EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { DocLinksStart } from 'kibana/public';
Expand Down Expand Up @@ -51,6 +51,26 @@ const i18nTexts = {
isLoading: i18n.translate('xpack.upgradeAssistant.esDeprecations.loadingText', {
defaultMessage: 'Loading deprecation issues…',
}),
remoteClustersDetectedTitle: i18n.translate(
'xpack.upgradeAssistant.esDeprecations.remoteClustersDetectedTitle',
{
defaultMessage: 'Remote cluster compatibility',
}
),
getRemoteClustersDetectedDescription: (remoteClustersCount: number) =>
i18n.translate('xpack.upgradeAssistant.esDeprecations.remoteClustersDetectedDescription', {
defaultMessage:
'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 running the previous minor version 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.',
values: {
remoteClustersCount,
},
}),
remoteClustersLinkText: i18n.translate(
'xpack.upgradeAssistant.esDeprecations.remoteClustersLinkText',
{
defaultMessage: 'View remote clusters.',
}
),
};

const getBatchReindexLink = (docLinks: DocLinksStart) => {
Expand All @@ -75,6 +95,22 @@ const getBatchReindexLink = (docLinks: DocLinksStart) => {
);
};

const RemoteClustersAppLink: React.FunctionComponent = () => {
const {
plugins: { share },
} = useAppContext();

const remoteClustersUrl = share.url.locators
.get('REMOTE_CLUSTERS_LOCATOR')
?.useUrl({ page: 'remoteClusters' });

return (
<EuiLink href={remoteClustersUrl} data-test-subj="remoteClustersLink">
{i18nTexts.remoteClustersLinkText}
</EuiLink>
);
};

export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
const {
services: {
Expand All @@ -85,6 +121,7 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
} = useAppContext();

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

const deprecationsCountByLevel: {
warningDeprecations: number;
Expand Down Expand Up @@ -140,10 +177,29 @@ export const EsDeprecations = withRouter(({ history }: RouteComponentProps) => {
</>
}
>
<DeprecationCount
totalCriticalDeprecations={deprecationsCountByLevel.criticalDeprecations}
totalWarningDeprecations={deprecationsCountByLevel.warningDeprecations}
/>
<>
{remoteClusters && remoteClusters.length > 0 && (
<>
<EuiCallOut
title={i18nTexts.remoteClustersDetectedTitle}
color="warning"
iconType="help"
data-test-subj="remoteClustersWarningCallout"
>
<p>
{i18nTexts.getRemoteClustersDetectedDescription(remoteClusters.length)}{' '}
<RemoteClustersAppLink />
</p>
</EuiCallOut>
<EuiSpacer />
</>
)}

<DeprecationCount
totalCriticalDeprecations={deprecationsCountByLevel.criticalDeprecations}
totalWarningDeprecations={deprecationsCountByLevel.warningDeprecations}
/>
</>
</EuiPageHeader>

<EuiSpacer size="l" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ export class ApiService {
method: 'get',
});
}

public useLoadRemoteClusters() {
return this.useRequest<string[]>({
path: `${API_BASE_PATH}/remote_clusters`,
method: 'get',
});
}
}

export const apiService = new ApiService();
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { registerUpdateSettingsRoute } from './update_index_settings';
import { registerMlSnapshotRoutes } from './ml_snapshots';
import { ReindexWorker } from '../lib/reindexing';
import { registerUpgradeStatusRoute } from './status';
import { registerRemoteClustersRoute } from './remote_clusters';

export function registerRoutes(dependencies: RouteDependencies, getWorker: () => ReindexWorker) {
registerAppRoutes(dependencies);
Expand All @@ -32,4 +33,5 @@ export function registerRoutes(dependencies: RouteDependencies, getWorker: () =>
registerMlSnapshotRoutes(dependencies);
// Route for cloud to retrieve the upgrade status for ES and Kibana
registerUpgradeStatusRoute(dependencies);
registerRemoteClustersRoute(dependencies);
}
40 changes: 40 additions & 0 deletions x-pack/plugins/upgrade_assistant/server/routes/remote_clusters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 { API_BASE_PATH } from '../../common/constants';
import { versionCheckHandlerWrapper } from '../lib/es_version_precheck';
import { RouteDependencies } from '../types';

export function registerRemoteClustersRoute({ router, lib: { handleEsError } }: RouteDependencies) {
router.get(
{
path: `${API_BASE_PATH}/remote_clusters`,
validate: false,
},
versionCheckHandlerWrapper(
async (
{
core: {
elasticsearch: { client },
},
},
request,
response
) => {
try {
const { body: clustersByName } = await client.asCurrentUser.cluster.remoteInfo();

const remoteClusters = Object.keys(clustersByName);

return response.ok({ body: remoteClusters });
} catch (error) {
return handleEsError({ error, response });
}
}
)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./privileges'));
loadTestFile(require.resolve('./es_deprecations'));
loadTestFile(require.resolve('./es_deprecation_logs'));
loadTestFile(require.resolve('./remote_clusters'));
});
}
Loading

0 comments on commit f5e6b4a

Please sign in to comment.