Skip to content

Commit

Permalink
Add on-Cloud state to Upgrade Assistant 'Back up data' step (elastic#…
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcenizal authored and sabarasaba committed Oct 20, 2021
1 parent 05417f9 commit 45251a1
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const idToUrlMap = {
const shareMock = sharePluginMock.createSetupContract();
shareMock.url.locators.get = (id) => ({
// @ts-expect-error This object is missing some properties that we're not using in the UI
getUrl: (): string | undefined => idToUrlMap[id],
useUrl: (): string | undefined => idToUrlMap[id],
});

export const getAppContextMock = (mockHttpClient: HttpSetup) => ({
Expand Down
6 changes: 6 additions & 0 deletions x-pack/plugins/upgrade_assistant/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ export const indexSettingDeprecations = {

export const API_BASE_PATH = '/api/upgrade_assistant';

/**
* This is the repository where Cloud stores its backup snapshots.
*/
export const CLOUD_SNAPSHOT_REPOSITORY = 'found-snapshots';

export const DEPRECATION_WARNING_UPPER_LIMIT = 999999;
export const DEPRECATION_LOGS_SOURCE_ID = 'deprecation_logs';
export const DEPRECATION_LOGS_INDEX = '.logs-deprecation.elasticsearch-default';
export const DEPRECATION_LOGS_INDEX_PATTERN = '.logs-deprecation.elasticsearch-default';

export const CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS = 60000;
export const DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS = 60000;
5 changes: 5 additions & 0 deletions x-pack/plugins/upgrade_assistant/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ export interface EnrichedDeprecationInfo
resolveDuringUpgrade: boolean;
}

export interface CloudBackupStatus {
isBackedUp: boolean;
lastBackupTime?: string;
}

export interface ESUpgradeStatus {
totalCriticalDeprecations: number;
deprecations: EnrichedDeprecationInfo[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,40 @@
* 2.0.
*/

import React, { useState, useEffect } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiText, EuiButton, EuiSpacer } from '@elastic/eui';
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';

import { useAppContext } from '../../../app_context';

const i18nTexts = {
backupStepTitle: i18n.translate('xpack.upgradeAssistant.overview.backupStepTitle', {
defaultMessage: 'Back up your data',
}),

backupStepDescription: i18n.translate('xpack.upgradeAssistant.overview.backupStepDescription', {
defaultMessage: 'Back up your data before addressing any deprecation warnings.',
}),
};

const SnapshotRestoreAppLink: React.FunctionComponent = () => {
const { share } = useAppContext();

const [snapshotRestoreUrl, setSnapshotRestoreUrl] = useState<string | undefined>();

useEffect(() => {
const getSnapshotRestoreUrl = async () => {
const locator = share.url.locators.get('SNAPSHOT_RESTORE_LOCATOR');

if (!locator) {
return;
}

const url = await locator.getUrl({
page: 'snapshots',
});
setSnapshotRestoreUrl(url);
import type { CloudSetup } from '../../../../../../cloud/public';
import { OnPremBackup } from './on_prem_backup';
import { CloudBackup, CloudBackupStatusResponse } from './cloud_backup';

const title = i18n.translate('xpack.upgradeAssistant.overview.backupStepTitle', {
defaultMessage: 'Back up your data',
});

interface Props {
cloud?: CloudSetup;
cloudBackupStatusResponse?: CloudBackupStatusResponse;
}

export const getBackupStep = ({ cloud, cloudBackupStatusResponse }: Props): EuiStepProps => {
if (cloud?.isCloudEnabled) {
return {
title,
status: cloudBackupStatusResponse!.data?.isBackedUp ? 'complete' : 'incomplete',
children: (
<CloudBackup
cloudBackupStatusResponse={cloudBackupStatusResponse!}
cloudSnapshotsUrl={`${cloud!.deploymentUrl}/elasticsearch/snapshots`}
/>
),
};
}

getSnapshotRestoreUrl();
}, [share]);

return (
<EuiButton href={snapshotRestoreUrl} data-test-subj="snapshotRestoreLink">
{i18n.translate('xpack.upgradeAssistant.overview.snapshotRestoreLink', {
defaultMessage: 'Create snapshot',
})}
</EuiButton>
);
};

const BackupStep: React.FunctionComponent = () => {
return (
<>
<EuiText>
<p>{i18nTexts.backupStepDescription}</p>
</EuiText>

<EuiSpacer size="s" />

<SnapshotRestoreAppLink />
</>
);
};

export const getBackupStep = (): EuiStepProps => {
return {
title: i18nTexts.backupStepTitle,
title,
status: 'incomplete',
children: <BackupStep />,
children: <OnPremBackup />,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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 moment from 'moment-timezone';
import { FormattedDate, FormattedTime, FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import {
EuiLoadingContent,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiText,
EuiButton,
EuiSpacer,
EuiCallOut,
} from '@elastic/eui';

import { CloudBackupStatus } from '../../../../../common/types';
import { UseRequestResponse } from '../../../../shared_imports';
import { ResponseError } from '../../../lib/api';

export type CloudBackupStatusResponse = UseRequestResponse<CloudBackupStatus, ResponseError>;

interface Props {
cloudBackupStatusResponse: UseRequestResponse<CloudBackupStatus, ResponseError>;
cloudSnapshotsUrl: string;
}

export const CloudBackup: React.FunctionComponent<Props> = ({
cloudBackupStatusResponse,
cloudSnapshotsUrl,
}) => {
const { isInitialRequest, isLoading, error, data, resendRequest } = cloudBackupStatusResponse;

if (isInitialRequest && isLoading) {
return <EuiLoadingContent lines={3} />;
}

if (error) {
return (
<EuiCallOut
title={i18n.translate('xpack.upgradeAssistant.overview.cloudBackup.loadingError', {
defaultMessage: 'An error occurred while retrieving the latest snapshot status',
})}
color="danger"
iconType="alert"
data-test-subj="cloudBackupErrorCallout"
>
<p>
{error.statusCode} - {error.message}
</p>
<EuiButton color="danger" onClick={resendRequest} data-test-subj="cloudBackupRetryButton">
{i18n.translate('xpack.upgradeAssistant.overview.cloudBackup.retryButton', {
defaultMessage: 'Try again',
})}
</EuiButton>
</EuiCallOut>
);
}

const lastBackupTime = moment(data!.lastBackupTime).toISOString();

const statusMessage = data!.isBackedUp ? (
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiIcon type="check" color="success" />
</EuiFlexItem>

<EuiFlexItem>
<EuiText>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.overview.cloudBackup.hasSnapshotMessage"
defaultMessage="Last snapshot created on {lastBackupTime}."
values={{
lastBackupTime: (
<>
<FormattedDate
value={lastBackupTime}
year="numeric"
month="long"
day="2-digit"
/>{' '}
<FormattedTime value={lastBackupTime} timeZoneName="short" hour12={false} />
</>
),
}}
/>
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
) : (
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiIcon type="alert" color="danger" />
</EuiFlexItem>

<EuiFlexItem>
<EuiText>
<p>
{i18n.translate('xpack.upgradeAssistant.overview.cloudBackup.noSnapshotMessage', {
defaultMessage: `Your data isn't backed up.`,
})}
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);

return (
<>
{statusMessage}

<EuiSpacer size="s" />

<EuiButton
href={cloudSnapshotsUrl}
data-test-subj="cloudSnapshotsLink"
target="_blank"
iconType="popout"
iconSide="right"
>
<FormattedMessage
id="xpack.upgradeAssistant.overview.cloudBackup.snapshotsLink"
defaultMessage="Create snapshot"
/>
</EuiButton>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { EuiText, EuiButton, EuiSpacer } from '@elastic/eui';

import { useAppContext } from '../../../app_context';

const SnapshotRestoreAppLink: React.FunctionComponent = () => {
const { share } = useAppContext();

const snapshotRestoreUrl = share.url.locators
.get('SNAPSHOT_RESTORE_LOCATOR')
?.useUrl({ page: 'snapshots' });

return (
<EuiButton href={snapshotRestoreUrl} data-test-subj="snapshotRestoreLink">
<FormattedMessage
id="xpack.upgradeAssistant.overview.snapshotRestoreLink"
defaultMessage="Create snapshot"
/>
</EuiButton>
);
};

export const OnPremBackup: React.FunctionComponent = () => {
return (
<>
<EuiText>
<p>
{i18n.translate('xpack.upgradeAssistant.overview.backupStepDescription', {
defaultMessage: 'Back up your data before addressing any deprecation issues.',
})}
</p>
</EuiText>

<EuiSpacer size="s" />

<SnapshotRestoreAppLink />
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import { useKibana } from '../../../shared_imports';
import { useAppContext } from '../../app_context';
import { getBackupStep } from './backup_step';
import { getFixIssuesStep } from './fix_issues_step';
import { getFixLogsStep } from './fix_logs_step';
import { getUpgradeStep } from './upgrade_step';

export const Overview: FunctionComponent = () => {
const {
services: { cloud },
} = useKibana();
const { kibanaVersionInfo, breadcrumbs, docLinks, api } = useAppContext();
const { nextMajor } = kibanaVersionInfo;

Expand All @@ -44,6 +48,12 @@ export const Overview: FunctionComponent = () => {
breadcrumbs.setBreadcrumbs('overview');
}, [breadcrumbs]);

let cloudBackupStatusResponse;

if (cloud?.isCloudEnabled) {
cloudBackupStatusResponse = api.useLoadCloudBackupStatus();
}

return (
<EuiPageBody restrictWidth={true}>
<EuiPageContent horizontalPosition="center" color="transparent" paddingSize="none">
Expand Down Expand Up @@ -84,7 +94,7 @@ export const Overview: FunctionComponent = () => {

<EuiSteps
steps={[
getBackupStep(),
getBackupStep({ cloud, cloudBackupStatusResponse }),
getFixIssuesStep({ nextMajor }),
getFixLogsStep(),
getUpgradeStep({ docLinks, nextMajor }),
Expand Down
17 changes: 15 additions & 2 deletions x-pack/plugins/upgrade_assistant/public/application/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
*/

import { HttpSetup } from 'src/core/public';
import { ESUpgradeStatus } from '../../../common/types';
import { API_BASE_PATH, DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS } from '../../../common/constants';

import { ESUpgradeStatus, CloudBackupStatus } from '../../../common/types';
import {
API_BASE_PATH,
DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS,
CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS,
} from '../../../common/constants';
import {
UseRequestConfig,
SendRequestConfig,
Expand Down Expand Up @@ -45,6 +50,14 @@ export class ApiService {
this.client = httpClient;
}

public useLoadCloudBackupStatus() {
return this.useRequest<CloudBackupStatus>({
path: `${API_BASE_PATH}/cloud_backup_status`,
method: 'get',
pollIntervalMs: CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS,
});
}

public useLoadEsDeprecations() {
return this.useRequest<ESUpgradeStatus>({
path: `${API_BASE_PATH}/es_deprecations`,
Expand Down
Loading

0 comments on commit 45251a1

Please sign in to comment.