Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow User to Cleanup Repository from UI #53047

Merged
merged 27 commits into from
Jan 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a0e1773
Added repository cleanup button. Added logic for spinner while loadin…
Dec 13, 2019
77c9076
Added additional bindings for server side to hit the cleanup endpoint.
Dec 13, 2019
bdd834f
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Dec 13, 2019
a3f48b6
fix cleanup request
alisonelizabeth Dec 17, 2019
004b93f
Added data test subject to the code editors to differentiate them and…
Dec 18, 2019
34d2a69
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Dec 18, 2019
c6cac4a
Merge branch 'master' of github.com:elastic/kibana into 43904_Add_Cle…
Dec 19, 2019
4d2b85f
Added files for a component integration test. The tests are failing r…
Dec 21, 2019
2307730
Merge branch 'master' of github.com:elastic/kibana into 43904_Add_Cle…
Dec 23, 2019
f86725a
Added change to the way data-test-subjects were created for the repos…
Dec 26, 2019
1296528
Merge branch 'master' of github.com:elastic/kibana into 43904_Add_Cle…
Dec 26, 2019
6ad982d
Removed the jest tests for repository details until we get jest fixed.
Dec 27, 2019
9deb14e
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Dec 29, 2019
bf413be
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Dec 31, 2019
71fd891
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Jan 4, 2020
6d536a9
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Jan 7, 2020
f6e34d0
Merge branch 'master' of github.com:elastic/kibana into 43904_Add_Cle…
Jan 7, 2020
0d0c470
Fixed jest test to reflect updated test subjects.
Jan 8, 2020
e9e9dbf
Made changes per feedback in PR comments.
Jan 8, 2020
f61a139
Fixed i10n issues using <FormattedMessage>. Removed reference to blue…
Jan 8, 2020
590a904
Added i10n fixes for header.
Jan 9, 2020
dae7208
Added i10n fixes for header.
Jan 9, 2020
ce69496
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Jan 9, 2020
c065f88
Added name parameter for i18n strings.
Jan 9, 2020
da765a0
Merge branch '43904_Add_Cleanup_Action_To_UI' of github.com:cuff-link…
Jan 9, 2020
558ff5e
Removed i18n string from JSON.stringify call since it's already a str…
Jan 11, 2020
a8b21c7
Merge branch 'master' into 43904_Add_Cleanup_Action_To_UI
elasticmachine Jan 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const setup = async (): Promise<HomeTestBed> => {
const tabs = ['snapshots', 'repositories'];

testBed
.find('tab')
.find(`${tab}_tab`)
.at(tabs.indexOf(tab))
.simulate('click');
};
Expand Down Expand Up @@ -360,7 +360,10 @@ export type TestSubjects =
| 'state'
| 'state.title'
| 'state.value'
| 'tab'
| 'repositories_tab'
| 'snapshots_tab'
| 'policies_tab'
| 'restore_status_tab'
| 'tableHeaderCell_durationInMillis_3'
| 'tableHeaderCell_durationInMillis_3.tableHeaderSortButton'
| 'tableHeaderCell_indices_4'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
]);
};

const setCleanupRepositoryResponse = (response?: HttpResponse, error?: any) => {
cuff-links marked this conversation as resolved.
Show resolved Hide resolved
const status = error ? error.status || 503 : 200;

server.respondWith('POST', `${API_BASE_PATH}repositories/:name/cleanup`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setGetPolicyResponse = (response?: HttpResponse) => {
server.respondWith('GET', `${API_BASE_PATH}policy/:name`, [
200,
Expand All @@ -113,6 +123,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
setLoadIndicesResponse,
setAddPolicyResponse,
setGetPolicyResponse,
setCleanupRepositoryResponse,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,15 @@ describe('<SnapshotRestoreHome />', () => {
test('should have 4 tabs', () => {
const { find } = testBed;

expect(find('tab').length).toBe(4);
expect(find('tab').map(t => t.text())).toEqual([
const tabs = [
find('snapshots_tab'),
find('repositories_tab'),
find('policies_tab'),
find('restore_status_tab'),
];

expect(tabs.length).toBe(4);
expect(tabs.map(t => t.text())).toEqual([
'Snapshots',
'Repositories',
'Policies',
Expand Down
12 changes: 12 additions & 0 deletions x-pack/legacy/plugins/snapshot_restore/common/types/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,15 @@ export interface InvalidRepositoryVerification {
}

export type RepositoryVerification = ValidRepositoryVerification | InvalidRepositoryVerification;

export interface SuccessfulRepositoryCleanup {
cleaned: true;
response: object;
}

export interface FailedRepositoryCleanup {
cleaned: false;
error: object;
}

export type RepositoryCleanup = FailedRepositoryCleanup | SuccessfulRepositoryCleanup;
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const UIM_REPOSITORY_DELETE = 'repository_delete';
export const UIM_REPOSITORY_DELETE_MANY = 'repository_delete_many';
export const UIM_REPOSITORY_SHOW_DETAILS_CLICK = 'repository_show_details_click';
export const UIM_REPOSITORY_DETAIL_PANEL_VERIFY = 'repository_detail_panel_verify';
export const UIM_REPOSITORY_DETAIL_PANEL_CLEANUP = 'repository_detail_panel_cleanup';
export const UIM_SNAPSHOT_LIST_LOAD = 'snapshot_list_load';
export const UIM_SNAPSHOT_SHOW_DETAILS_CLICK = 'snapshot_show_details_click';
export const UIM_SNAPSHOT_DETAIL_PANEL_SUMMARY_TAB = 'snapshot_detail_panel_summary_tab';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export const SnapshotRestoreHome: React.FunctionComponent<RouteComponentProps<Ma
onClick={() => onSectionChange(tab.id)}
isSelected={tab.id === section}
key={tab.id}
data-test-subj="tab"
data-test-subj={tab.id.toLowerCase() + '_tab'}
>
{tab.name}
</EuiTab>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
EuiButton,
EuiButtonEmpty,
EuiCallOut,
EuiCodeEditor,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
Expand All @@ -19,6 +18,8 @@ import {
EuiLink,
EuiSpacer,
EuiTitle,
EuiCodeBlock,
EuiText,
} from '@elastic/eui';

import 'brace/theme/textmate';
Expand All @@ -28,12 +29,17 @@ import { documentationLinksService } from '../../../../services/documentation';
import {
useLoadRepository,
verifyRepository as verifyRepositoryRequest,
cleanupRepository as cleanupRepositoryRequest,
} from '../../../../services/http';
import { textService } from '../../../../services/text';
import { linkToSnapshots, linkToEditRepository } from '../../../../services/navigation';

import { REPOSITORY_TYPES } from '../../../../../../common/constants';
import { Repository, RepositoryVerification } from '../../../../../../common/types';
import {
Repository,
RepositoryVerification,
RepositoryCleanup,
} from '../../../../../../common/types';
import {
RepositoryDeleteProvider,
SectionError,
Expand Down Expand Up @@ -61,7 +67,9 @@ export const RepositoryDetails: React.FunctionComponent<Props> = ({
const { FormattedMessage } = i18n;
const { error, data: repositoryDetails } = useLoadRepository(repositoryName);
const [verification, setVerification] = useState<RepositoryVerification | undefined>(undefined);
const [cleanup, setCleanup] = useState<RepositoryCleanup | undefined>(undefined);
const [isLoadingVerification, setIsLoadingVerification] = useState<boolean>(false);
const [isLoadingCleanup, setIsLoadingCleanup] = useState<boolean>(false);

const verifyRepository = async () => {
setIsLoadingVerification(true);
Expand All @@ -70,11 +78,20 @@ export const RepositoryDetails: React.FunctionComponent<Props> = ({
setIsLoadingVerification(false);
};

// Reset verification state when repository name changes, either from adjust URL or clicking
const cleanupRepository = async () => {
setIsLoadingCleanup(true);
const { data } = await cleanupRepositoryRequest(repositoryName);
setCleanup(data.cleanup);
setIsLoadingCleanup(false);
};

// Reset verification state and cleanup when repository name changes, either from adjust URL or clicking
// into a different repository in table list.
useEffect(() => {
setVerification(undefined);
setIsLoadingVerification(false);
setCleanup(undefined);
setIsLoadingCleanup(false);
}, [repositoryName]);

const renderBody = () => {
Expand Down Expand Up @@ -231,6 +248,8 @@ export const RepositoryDetails: React.FunctionComponent<Props> = ({
<TypeDetails repository={repository} />
<EuiHorizontalRule />
{renderVerification()}
<EuiHorizontalRule />
{renderCleanup()}
</Fragment>
);
};
Expand Down Expand Up @@ -260,36 +279,13 @@ export const RepositoryDetails: React.FunctionComponent<Props> = ({
</EuiTitle>
<EuiSpacer size="s" />
{verification ? (
<EuiCodeEditor
mode="json"
theme="textmate"
width="100%"
isReadOnly
value={JSON.stringify(
<EuiCodeBlock language="json" inline={false} data-test-subj="verificationCodeBlock">
{JSON.stringify(
verification.valid ? verification.response : verification.error,
null,
2
)}
setOptions={{
showLineNumbers: false,
tabSize: 2,
maxLines: Infinity,
}}
editorProps={{
$blockScrolling: Infinity,
}}
showGutter={false}
minLines={6}
aria-label={
<FormattedMessage
id="xpack.snapshotRestore.repositoryDetails.verificationDetails"
defaultMessage="Verification details repository '{name}'"
values={{
name,
}}
/>
}
/>
</EuiCodeBlock>
) : null}
<EuiSpacer size="m" />
<EuiButton onClick={verifyRepository} color="primary" isLoading={isLoadingVerification}>
Expand Down Expand Up @@ -318,6 +314,78 @@ export const RepositoryDetails: React.FunctionComponent<Props> = ({
</Fragment>
);

const renderCleanup = () => (
<>
<EuiTitle size="s">
<h3>
<FormattedMessage
id="xpack.snapshotRestore.repositoryDetails.cleanupTitle"
defaultMessage="Repository cleanup"
/>
</h3>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.snapshotRestore.repositoryDetails.cleanupRepositoryMessage"
defaultMessage="You can clean up a repository to delete any unreferenced data from a snapshot. This
may provide storage space savings. Note: If you regularly delete snapshots, this
functionality will likely not be as beneficial and should be used less frequently."
/>
</p>
</EuiText>
{cleanup ? (
<>
<EuiSpacer size="s" />
{cleanup.cleaned ? (
<div>
<EuiTitle size="xs">
<h4>
<FormattedMessage
id="xpack.snapshotRestore.repositoryDetails.cleanupDetailsTitle"
defaultMessage="Details"
/>
</h4>
</EuiTitle>
<EuiCodeBlock language="json" inline={false} data-test-subj="cleanupCodeBlock">
{JSON.stringify(cleanup.response, null, 2)}
</EuiCodeBlock>
</div>
) : (
<EuiCallOut
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also include the actual error from the request underneath the title (as CJ mentioned in his comment)?

color="danger"
iconType="alert"
title={i18n.translate('xpack.snapshotRestore.repositoryDetails.cleanupErrorTitle', {
defaultMessage: 'Sorry, there was an error cleaning the repository.',
})}
>
<p>
{cleanup.error
? JSON.stringify(cleanup.error)
: i18n.translate('xpack.snapshotRestore.repositoryDetails.cleanupUnknownError', {
defaultMessage: '503: Unknown error',
})}
</p>
</EuiCallOut>
)}
</>
) : null}
<EuiSpacer size="m" />
<EuiButton
onClick={cleanupRepository}
color="primary"
isLoading={isLoadingCleanup}
data-test-subj="cleanupRepositoryButton"
>
<FormattedMessage
id="xpack.snapshotRestore.repositoryDetails.cleanupButtonLabel"
defaultMessage="Clean up repository"
/>
</EuiButton>
</>
);

const renderFooter = () => {
return (
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const RepositoryTable: React.FunctionComponent<Props> = ({
},
},
{
field: 'actions',
name: i18n.translate('xpack.snapshotRestore.repositoryList.table.actionsColumnTitle', {
defaultMessage: 'Actions',
}),
Expand Down Expand Up @@ -302,8 +303,8 @@ export const RepositoryTable: React.FunctionComponent<Props> = ({
rowProps={() => ({
'data-test-subj': 'row',
})}
cellProps={() => ({
'data-test-subj': 'cell',
cellProps={(item, field) => ({
'data-test-subj': `${field.name}_cell`,
})}
data-test-subj="repositoryTable"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
UIM_REPOSITORY_DELETE,
UIM_REPOSITORY_DELETE_MANY,
UIM_REPOSITORY_DETAIL_PANEL_VERIFY,
UIM_REPOSITORY_DETAIL_PANEL_CLEANUP,
} from '../../constants';
import { uiMetricService } from '../ui_metric';
import { httpService } from './http';
Expand Down Expand Up @@ -44,6 +45,20 @@ export const verifyRepository = async (name: Repository['name']) => {
return result;
};

export const cleanupRepository = async (name: Repository['name']) => {
const result = await sendRequest({
path: httpService.addBasePath(
`${API_BASE_PATH}repositories/${encodeURIComponent(name)}/cleanup`
),
method: 'post',
body: undefined,
cuff-links marked this conversation as resolved.
Show resolved Hide resolved
});

const { trackUiMetric } = uiMetricService;
trackUiMetric(UIM_REPOSITORY_DETAIL_PANEL_CLEANUP);
return result;
};

export const useLoadRepositoryTypes = () => {
return useRequest({
path: httpService.addBasePath(`${API_BASE_PATH}repository_types`),
Expand Down