Skip to content

Commit

Permalink
Add on-Cloud state to Upgrade Assistant 'Back up data' step.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcenizal committed Aug 25, 2021
1 parent 923bf40 commit 46fdd4a
Show file tree
Hide file tree
Showing 12 changed files with 320 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
2 changes: 2 additions & 0 deletions x-pack/plugins/upgrade_assistant/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ export const API_BASE_PATH = '/api/upgrade_assistant';
export const DEPRECATION_WARNING_UPPER_LIMIT = 999999;
export const DEPRECATION_LOGS_SOURCE_ID = 'deprecation_logs';
export const DEPRECATION_LOGS_INDEX_PATTERN = '.logs-deprecation.elasticsearch-default';

export const CLOUD_BACKUP_STATUS_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;
time?: 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 { CloudSetup } from '../../../../../../cloud/public';
import { OnPremBackup } from './on_prem_backup';
import { CloudBackup, CloudBackupStatus } from './cloud_backup';

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

interface Props {
cloud?: CloudSetup;
cloudBackupStatus: CloudBackupStatus;
}

export const getBackupStep = ({ cloud, cloudBackupStatus }: Props): EuiStepProps => {
if (cloud?.isCloudEnabled) {
return {
title,
status: cloudBackupStatus?.data?.isBackedUp ? 'complete' : 'incomplete',
children: (
<CloudBackup
cloudBackupStatus={cloudBackupStatus}
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,135 @@
/*
* 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';

interface Props {
cloudBackupStatus: CloudBackupStatus;
cloudSnapshotsUrl: string;
}

export interface CloudBackupStatus {
isLoading?: boolean;
isInitialRequest?: boolean;
resendRequest?: () => void;
error?: any;
data?: {
isBackedUp: boolean;
time: string;
};
}

export const CloudBackup: React.FunctionComponent<Props> = ({
cloudBackupStatus: { isLoading, isInitialRequest, resendRequest, error, data },
cloudSnapshotsUrl,
}) => {
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 time = moment(data!.time).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 {time}."
values={{
time: (
<>
<FormattedDate value={time} year="numeric" month="long" day="2-digit" />{' '}
<FormattedTime value={time} 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" />

{/* TODO: move this link to the Cloud plugin where it's easier to track */}
<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,26 @@ export const Overview: FunctionComponent = () => {
breadcrumbs.setBreadcrumbs('overview');
}, [breadcrumbs]);

let cloudBackupStatus = {};

if (cloud?.isCloudEnabled) {
const {
data,
isLoading,
error,
isInitialRequest,
resendRequest,
} = api.useLoadCloudBackupStatus();

cloudBackupStatus = {
data,
isLoading,
error,
isInitialRequest,
resendRequest,
};
}

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

<EuiSteps
steps={[
getBackupStep(),
getBackupStep({ cloud, cloudBackupStatus }),
getFixIssuesStep({ nextMajor }),
getFixLogsStep(),
getUpgradeStep({ docLinks, nextMajor }),
Expand Down
12 changes: 10 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,8 @@
*/

import { HttpSetup } from 'src/core/public';
import { ESUpgradeStatus } from '../../../common/types';
import { API_BASE_PATH } from '../../../common/constants';
import { ESUpgradeStatus, CloudBackupStatus } from '../../../common/types';
import { API_BASE_PATH, CLOUD_BACKUP_STATUS_POLL_INTERVAL_MS } from '../../../common/constants';
import {
UseRequestConfig,
SendRequestConfig,
Expand Down Expand Up @@ -45,6 +45,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 46fdd4a

Please sign in to comment.