Skip to content

Commit

Permalink
[Snapshot & Restore] Decode URIs and fix editing of a policy (#76278)
Browse files Browse the repository at this point in the history
* fix URI decoding and editing of a policy which backs up all indices

* fix type issue

* fix general use of encoding and update decode algo

* fix serialisation of snapshots and added a test

* Fix test description name

* Update attempt_to_uri_decode.ts

* catch errors from decoding in the already throwing code

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
jloleysens and elasticmachine authored Aug 31, 2020
1 parent 1e5f5df commit 3fb839e
Show file tree
Hide file tree
Showing 24 changed files with 277 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { deserializeSnapshotDetails } from './snapshot_serialization';
import { deserializeSnapshotDetails, serializeSnapshotConfig } from './snapshot_serialization';

describe('deserializeSnapshotDetails', () => {
test('deserializes a snapshot', () => {
expect(
deserializeSnapshotDetails(
'repositoryName',
{
snapshot: 'snapshot name',
uuid: 'UUID',
version_id: 5,
version: 'version',
indices: ['index2', 'index3', 'index1'],
include_global_state: false,
state: 'SUCCESS',
start_time: '0',
start_time_in_millis: 0,
end_time: '1',
end_time_in_millis: 1,
duration_in_millis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
failures: [
{
index: 'z',
shard: 1,
},
{
index: 'a',
shard: 3,
},
{
index: 'a',
shard: 1,
},
{
index: 'a',
shard: 2,
},
],
},
'found-snapshots',
[
describe('Snapshot serialization and deserialization', () => {
describe('deserializeSnapshotDetails', () => {
test('deserializes a snapshot', () => {
expect(
deserializeSnapshotDetails(
'repositoryName',
{
snapshot: 'last_successful_snapshot',
uuid: 'last_successful_snapshot_UUID',
snapshot: 'snapshot name',
uuid: 'UUID',
version_id: 5,
version: 'version',
indices: ['index2', 'index3', 'index1'],
Expand Down Expand Up @@ -87,56 +49,109 @@ describe('deserializeSnapshotDetails', () => {
},
],
},
]
)
).toEqual({
repository: 'repositoryName',
snapshot: 'snapshot name',
uuid: 'UUID',
versionId: 5,
version: 'version',
// Indices are sorted.
indices: ['index1', 'index2', 'index3'],
dataStreams: [],
includeGlobalState: false,
// Failures are grouped and sorted by index, and the failures themselves are sorted by shard.
indexFailures: [
{
index: 'a',
failures: [
'found-snapshots',
[
{
shard: 1,
},
{
shard: 2,
},
{
shard: 3,
},
],
},
{
index: 'z',
failures: [
{
shard: 1,
snapshot: 'last_successful_snapshot',
uuid: 'last_successful_snapshot_UUID',
version_id: 5,
version: 'version',
indices: ['index2', 'index3', 'index1'],
include_global_state: false,
state: 'SUCCESS',
start_time: '0',
start_time_in_millis: 0,
end_time: '1',
end_time_in_millis: 1,
duration_in_millis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
failures: [
{
index: 'z',
shard: 1,
},
{
index: 'a',
shard: 3,
},
{
index: 'a',
shard: 1,
},
{
index: 'a',
shard: 2,
},
],
},
],
]
)
).toEqual({
repository: 'repositoryName',
snapshot: 'snapshot name',
uuid: 'UUID',
versionId: 5,
version: 'version',
// Indices are sorted.
indices: ['index1', 'index2', 'index3'],
dataStreams: [],
includeGlobalState: false,
// Failures are grouped and sorted by index, and the failures themselves are sorted by shard.
indexFailures: [
{
index: 'a',
failures: [
{
shard: 1,
},
{
shard: 2,
},
{
shard: 3,
},
],
},
{
index: 'z',
failures: [
{
shard: 1,
},
],
},
],
state: 'SUCCESS',
startTime: '0',
startTimeInMillis: 0,
endTime: '1',
endTimeInMillis: 1,
durationInMillis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
],
state: 'SUCCESS',
startTime: '0',
startTimeInMillis: 0,
endTime: '1',
endTimeInMillis: 1,
durationInMillis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
managedRepository: 'found-snapshots',
isLastSuccessfulSnapshot: false,
managedRepository: 'found-snapshots',
isLastSuccessfulSnapshot: false,
});
});
});

describe('serializeSnapshotConfig', () => {
test('serializes config as expected', () => {
const metadata = { test: 'what have you' };
expect(serializeSnapshotConfig({ metadata, indices: '.k*' })).toEqual({
metadata,
indices: ['.k*'],
});
});
test('serializes empty config as expected', () => {
expect(serializeSnapshotConfig({})).toEqual({});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ export function deserializeSnapshotConfig(snapshotConfigEs: SnapshotConfigEs): S
export function serializeSnapshotConfig(snapshotConfig: SnapshotConfig): SnapshotConfigEs {
const { indices, ignoreUnavailable, includeGlobalState, partial, metadata } = snapshotConfig;

const indicesArray = csvToArray(indices);
const maybeIndicesArray = csvToArray(indices);

const snapshotConfigEs: SnapshotConfigEs = {
indices: indicesArray,
indices: maybeIndicesArray,
ignore_unavailable: ignoreUnavailable,
include_global_state: includeGlobalState,
partial,
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/snapshot_restore/common/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const csvToArray = (indices?: string | string[]): string[] => {
export const csvToArray = (indices?: string | string[]): string[] | undefined => {
return indices && Array.isArray(indices)
? indices
: typeof indices === 'string'
? indices.split(',')
: [];
: undefined;
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const maximumItemPreviewCount = 10;

export const useCollapsibleList = ({ items }: Arg): ReturnValue => {
const [isShowingFullList, setIsShowingFullList] = useState<boolean>(false);
const itemsArray = csvToArray(items);
const itemsArray = csvToArray(items) ?? [];
const displayItems: ChildItems =
items === undefined
? 'all'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ export const PolicyStepRetention: React.FunctionComponent<StepProps> = ({
}) => {
const { retention = {} } = policy;

const updatePolicyRetention = (updatedFields: Partial<SlmPolicyPayload['retention']>): void => {
const updatePolicyRetention = (
updatedFields: Partial<SlmPolicyPayload['retention']>,
validationHelperData = {}
): void => {
const newRetention = { ...retention, ...updatedFields };
updatePolicy({
retention: newRetention,
});
updatePolicy(
{
retention: newRetention,
},
validationHelperData
);
};

// State for touched inputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

import { SlmPolicyPayload } from '../../../../../../../../common/types';
import { useServices } from '../../../../../../app_context';
import { PolicyValidation } from '../../../../../../services/validation';
import { PolicyValidation, ValidatePolicyHelperData } from '../../../../../../services/validation';

import { orderDataStreamsAndIndices } from '../../../../../lib';
import { DataStreamBadge } from '../../../../../data_stream_badge';
Expand All @@ -34,12 +34,16 @@ import { mapSelectionToIndicesOptions, determineListMode } from './helpers';

import { DataStreamsAndIndicesListHelpText } from './data_streams_and_indices_list_help_text';

interface IndicesConfig {
indices?: string[] | string;
}

interface Props {
isManagedPolicy: boolean;
policy: SlmPolicyPayload;
indices: string[];
dataStreams: string[];
onUpdate: (arg: { indices?: string[] | string }) => void;
onUpdate: (arg: IndicesConfig, validateHelperData: ValidatePolicyHelperData) => void;
errors: PolicyValidation['errors'];
}

Expand All @@ -53,7 +57,7 @@ export const IndicesAndDataStreamsField: FunctionComponent<Props> = ({
dataStreams,
indices,
policy,
onUpdate,
onUpdate: _onUpdate,
errors,
}) => {
const { i18n } = useServices();
Expand All @@ -66,6 +70,12 @@ export const IndicesAndDataStreamsField: FunctionComponent<Props> = ({
!config.indices || (Array.isArray(config.indices) && config.indices.length === 0)
);

const onUpdate = (data: IndicesConfig) => {
_onUpdate(data, {
validateIndicesCount: !isAllIndices,
});
};

const [indicesAndDataStreamsSelection, setIndicesAndDataStreamsSelection] = useState<string[]>(
() =>
Array.isArray(config.indices) && !isAllIndices
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ export const PolicyStepSettings: React.FunctionComponent<StepProps> = ({
}) => {
const { config = {}, isManagedPolicy } = policy;

const updatePolicyConfig = (updatedFields: Partial<SlmPolicyPayload['config']>): void => {
const updatePolicyConfig = (
updatedFields: Partial<SlmPolicyPayload['config']>,
validationHelperData = {}
): void => {
const newConfig = { ...config, ...updatedFields };
updatePolicy({
config: newConfig,
});
updatePolicy(
{
config: newConfig,
},
validationHelperData
);
};

const renderIgnoreUnavailableField = () => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export const RestoreSnapshotStepLogistics: React.FunctionComponent<StepProps> =
});
}
}}
selectedIndicesAndDataStreams={csvToArray(restoreIndices)}
selectedIndicesAndDataStreams={csvToArray(restoreIndices) ?? []}
indices={snapshotIndices}
dataStreams={snapshotDataStreams}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const attemptToURIDecode = (value: string) => {
let result: string;

try {
result = decodeURI(value);
result = decodeURIComponent(result);
} catch (e1) {
try {
result = decodeURIComponent(value);
} catch (e2) {
result = value;
}
}

return result;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { useDecodedParams } from './use_decoded_params';
Loading

0 comments on commit 3fb839e

Please sign in to comment.