[Index management] Api integration tests (#36343) (#36428)

This commit is contained in:
Sébastien Loix 2019-05-10 14:54:41 +02:00 committed by GitHub
parent b9799b58f1
commit e8baa9920c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 666 additions and 36 deletions

View file

@ -1,36 +0,0 @@
/*
* 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.
*/
function formatHits(hits) {
return hits.map(hit => {
return {
health: hit.health,
status: hit.status,
name: hit.index,
uuid: hit.uuid,
primary: hit.pri,
replica: hit.rep,
documents: hit['docs.count'],
documents_deleted: hit['docs.deleted'],
size: hit['store.size'],
primary_size: hit['pri.store.size'],
};
});
}
const handler = async (request, callWithRequest) => {
const { indexNames = [] } = request.payload;
const params = {
format: 'json',
index: indexNames
};
const hits = await callWithRequest('settings', params);
const response = formatHits(hits);
return response;
};
export function registerSettingsRoute(router) {
router.post('indices/settings', handler);
}

View file

@ -10,6 +10,7 @@ export default function ({ loadTestFile }) {
loadTestFile(require.resolve('./cross_cluster_replication'));
loadTestFile(require.resolve('./remote_clusters'));
loadTestFile(require.resolve('./rollup'));
loadTestFile(require.resolve('./index_management'));
loadTestFile(require.resolve('./index_lifecycle_management'));
});
}

View file

@ -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 const API_BASE_PATH = '/api/index_management';

View file

@ -0,0 +1,14 @@
/*
* 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 default function ({ loadTestFile }) {
describe('index management', () => {
loadTestFile(require.resolve('./indices'));
loadTestFile(require.resolve('./mapping'));
loadTestFile(require.resolve('./settings'));
loadTestFile(require.resolve('./stats'));
});
}

View file

@ -0,0 +1,57 @@
/*
* 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.
*/
import { API_BASE_PATH } from './constants';
export const registerHelpers = ({ supertest }) => {
const executeActionOnIndices = (index, urlParam, args) => {
const indices = Array.isArray(index) ? index : [index];
return supertest.post(`${API_BASE_PATH}/indices/${urlParam}`)
.set('kbn-xsrf', 'xxx')
.send({ indices, ...args });
};
const closeIndex = (index) => executeActionOnIndices(index, 'close');
const openIndex = (index) => executeActionOnIndices(index, 'open');
const deleteIndex = (index) => executeActionOnIndices(index, 'delete');
const flushIndex = (index) => executeActionOnIndices(index, 'flush');
const refreshIndex = (index) => executeActionOnIndices(index, 'refresh');
const forceMerge = (index, args) => executeActionOnIndices(index, 'forcemerge', args);
const freeze = (index) => executeActionOnIndices(index, 'freeze');
const unfreeze = (index) => executeActionOnIndices(index, 'unfreeze');
const clearCache = (index) => executeActionOnIndices(index, 'clear_cache');
const list = () => supertest.get(`${API_BASE_PATH}/indices`);
const reload = (indexNames) => (
supertest.post(`${API_BASE_PATH}/indices/reload`)
.set('kbn-xsrf', 'xxx')
.send({ indexNames })
);
return {
closeIndex,
openIndex,
deleteIndex,
flushIndex,
refreshIndex,
forceMerge,
freeze,
unfreeze,
list,
reload,
clearCache,
};
};

View file

@ -0,0 +1,226 @@
/*
* 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.
*/
import expect from '@kbn/expect';
import { initElasticsearchHelpers } from './lib';
import { registerHelpers } from './indices.helpers';
export default function ({ getService }) {
const supertest = getService('supertest');
const es = getService('es');
const {
createIndex,
catIndex,
indexStats,
cleanUp: cleanUpEsResources
} = initElasticsearchHelpers(es);
const {
closeIndex,
openIndex,
deleteIndex,
flushIndex,
refreshIndex,
forceMerge,
freeze,
unfreeze,
list,
reload,
clearCache,
} = registerHelpers({ supertest });
describe('indices', () => {
after(() => Promise.all([cleanUpEsResources()]));
describe('clear cache', () => {
it('should clear the cache on all indices', async () => {
await clearCache('*').expect(200);
});
it('should clear the cache on a single index', async () => {
const index = await createIndex();
await clearCache(index).expect(200);
});
});
describe('close', () => {
it('should close an index', async () => {
const index = await createIndex();
// Make sure the index is open
const [cat1] = await catIndex(index);
expect(cat1.status).to.be('open');
await closeIndex(index).expect(200);
// Make sure the index has been closed
const [cat2] = await catIndex(index);
expect(cat2.status).to.be('close');
});
});
describe('open', () => {
it('should open an index', async () => {
const index = await createIndex();
await closeIndex(index);
// Make sure the index is closed
const [cat1] = await catIndex(index);
expect(cat1.status).to.be('close');
await openIndex(index).expect(200);
// Make sure the index is opened
const [cat2] = await catIndex(index);
expect(cat2.status).to.be('open');
});
});
describe('delete', () => {
it('should delete an index', async () => {
const index = await createIndex();
const indices1 = await catIndex(undefined, 'i');
expect(indices1.map(index => index.i)).to.contain(index);
await deleteIndex([index]).expect(200);
const indices2 = await catIndex(undefined, 'i');
expect(indices2.map(index => index.i)).not.to.contain(index);
});
it('should require index or indices to be provided', async () => {
const { body } = await deleteIndex().expect(400);
expect(body.message).to.contain('index / indices is missing');
});
});
describe('flush', () => {
it('should flush an index', async () => {
const index = await createIndex();
const { indices: indices1 } = await indexStats(index, 'flush');
expect(indices1[index].total.flush.total).to.be(0);
await flushIndex(index).expect(200);
const { indices: indices2 } = await indexStats(index, 'flush');
expect(indices2[index].total.flush.total).to.be(1);
});
});
describe('refresh', () => {
it('should refresh an index', async () => {
const index = await createIndex();
const { indices: indices1 } = await indexStats(index, 'refresh');
const previousRefreshes = indices1[index].total.refresh.total;
await refreshIndex(index).expect(200);
const { indices: indices2 } = await indexStats(index, 'refresh');
expect(indices2[index].total.refresh.total).to.be(previousRefreshes + 1);
});
});
describe('forcemerge', () => {
it('should force merge an index', async () => {
const index = await createIndex();
await forceMerge(index).expect(200);
});
it('should allow to define the number of segments', async () => {
const index = await createIndex();
await forceMerge(index, { max_num_segments: 1 }).expect(200);
});
});
describe('freeze', () => {
it('should freeze an index', async () => {
const index = await createIndex();
// "sth" correspond to search throttling. Frozen indices are normal indices
// with search throttling turned on.
const [cat1] = await catIndex(index, 'sth');
expect(cat1.sth).to.be('false');
await freeze(index).expect(200);
const [cat2] = await catIndex(index, 'sth');
expect(cat2.sth).to.be('true');
});
});
describe('unfreeze', () => {
it('should unfreeze an index', async () => {
const index = await createIndex();
await freeze(index).expect(200);
const [cat1] = await catIndex(index, 'sth');
expect(cat1.sth).to.be('true');
await unfreeze(index).expect(200);
const [cat2] = await catIndex(index, 'sth');
expect(cat2.sth).to.be('false');
});
});
describe('list', () => {
it('should list all the indices with the expected properties and data enrichers', async () => {
const { body } = await list().expect(200);
const expectedKeys = [
'health',
'status',
'name',
'uuid',
'primary',
'replica',
'documents',
'size',
'isFrozen',
'aliases',
'ilm', // data enricher
'isRollupIndex', // data enricher
'isFollowerIndex' // data enricher
];
expect(Object.keys(body[0])).to.eql(expectedKeys);
});
});
describe('reload', () => {
it('should list all the indices with the expected properties and data enrichers', async () => {
const { body } = await reload().expect(200);
const expectedKeys = [
'health',
'status',
'name',
'uuid',
'primary',
'replica',
'documents',
'size',
'isFrozen',
'aliases',
'ilm', // data enricher
'isRollupIndex', // data enricher
'isFollowerIndex' // data enricher
];
expect(Object.keys(body[0])).to.eql(expectedKeys);
expect(body.length > 1).to.be(true); // to contrast it with the next test
});
it('should allow reloading only certain indices', async () => {
const index = await createIndex();
const { body } = await reload([index]);
expect(body.length === 1).to.be(true);
expect(body[0].name).to.be(index);
});
});
});
}

View file

@ -0,0 +1,50 @@
/*
* 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.
*/
import { getRandomString } from './random';
/**
* Helpers to create and delete indices on the Elasticsearch instance
* during our tests.
* @param {ElasticsearchClient} es The Elasticsearch client instance
*/
export const initElasticsearchHelpers = (es) => {
let indicesCreated = [];
const createIndex = (index = getRandomString(), body) => {
indicesCreated.push(index);
return es.indices.create({ index, body }).then(() => index);
};
const deleteIndex = (index) => {
indicesCreated = indicesCreated.filter(i => i !== index);
return es.indices.delete({ index, ignoreUnavailable: true });
};
const deleteAllIndices = () => (
Promise.all(indicesCreated.map(deleteIndex)).then(() => indicesCreated = [])
);
const catIndex = (index, h) => (
es.cat.indices({ index, format: 'json', h })
);
const indexStats = (index, metric) => (
es.indices.stats({ index, metric })
);
const cleanUp = () => (
deleteAllIndices()
);
return ({
createIndex,
deleteIndex,
deleteAllIndices,
catIndex,
indexStats,
cleanUp,
});
};

View file

@ -0,0 +1,17 @@
/*
* 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 {
initElasticsearchHelpers
} from './elasticsearch';
export {
getRandomString,
} from './random';
export {
wait,
} from './utils';

View file

@ -0,0 +1,12 @@
/*
* 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.
*/
import Chance from 'chance';
const chance = new Chance();
const CHARS_POOL = 'abcdefghijklmnopqrstuvwxyz';
export const getRandomString = () => `${chance.string({ pool: CHARS_POOL })}-${Date.now()}`;

View file

@ -0,0 +1,11 @@
/*
* 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 wait = (time = 1000) => (
new Promise((resolve) => (
setTimeout(resolve, time)
))
);

View file

@ -0,0 +1,15 @@
/*
* 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.
*/
import { API_BASE_PATH } from './constants';
export const registerHelpers = ({ supertest }) => {
const getIndexMapping = (indexName) => supertest.get(`${API_BASE_PATH}/mapping/${indexName}`);
return {
getIndexMapping,
};
};

View file

@ -0,0 +1,41 @@
/*
* 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.
*/
import expect from '@kbn/expect';
import { initElasticsearchHelpers } from './lib';
import { registerHelpers } from './mapping.helpers';
export default function ({ getService }) {
const supertest = getService('supertest');
const es = getService('es');
const {
createIndex,
cleanUp: cleanUpEsResources
} = initElasticsearchHelpers(es);
const { getIndexMapping } = registerHelpers({ supertest });
describe('mapping', () => {
after(() => Promise.all([cleanUpEsResources()]));
it('should fetch the index mapping', async () => {
const mappings = {
properties: {
total: { type: 'long' },
tag: { type: 'keyword' },
createdAt: { type: 'date' },
}
};
const index = await createIndex(undefined, { mappings });
const { body } = await getIndexMapping(index).expect(200);
expect(body.mapping).to.eql(mappings);
});
});
}

View file

@ -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.
*/
import { API_BASE_PATH } from './constants';
export const registerHelpers = ({ supertest }) => {
const getIndexSettings = (indexName) => supertest.get(`${API_BASE_PATH}/settings/${indexName}`);
const updateIndexSettings = (indexName, settings) => (
supertest.put(`${API_BASE_PATH}/settings/${indexName}`)
.set('kbn-xsrf', 'xxx')
.send(settings)
);
return {
getIndexSettings,
updateIndexSettings,
};
};

View file

@ -0,0 +1,118 @@
/*
* 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.
*/
import expect from '@kbn/expect';
import { initElasticsearchHelpers } from './lib';
import { registerHelpers } from './settings.helpers';
export default function ({ getService }) {
const supertest = getService('supertest');
const es = getService('es');
const {
createIndex,
cleanUp: cleanUpEsResources
} = initElasticsearchHelpers(es);
const { getIndexSettings, updateIndexSettings } = registerHelpers({ supertest });
describe('settings', () => {
after(() => Promise.all([cleanUpEsResources()]));
it('should fetch an index settings', async () => {
const index = await createIndex();
const { body } = await getIndexSettings(index).expect(200);
// Verify we fetch the corret index settings
expect(body.settings.index.provided_name).to.be(index);
const expectedSettings = [
'max_inner_result_window',
'unassigned',
'max_terms_count',
'lifecycle',
'routing_partition_size',
'force_memory_term_dictionary',
'max_docvalue_fields_search',
'merge',
'max_refresh_listeners',
'max_regex_length',
'load_fixed_bitset_filters_eagerly',
'number_of_routing_shards',
'write',
'verified_before_close',
'mapping',
'source_only',
'soft_deletes',
'max_script_fields',
'query',
'format',
'frozen',
'sort',
'priority',
'codec',
'max_rescore_window',
'max_adjacency_matrix_filters',
'analyze',
'gc_deletes',
'max_ngram_diff',
'translog',
'auto_expand_replicas',
'mapper',
'requests',
'data_path',
'highlight',
'routing',
'search',
'fielddata',
'default_pipeline',
'max_slices_per_scroll',
'shard',
'xpack',
'percolator',
'allocation',
'refresh_interval',
'indexing',
'compound_format',
'blocks',
'max_result_window',
'store',
'queries',
'warmer',
'max_shingle_diff',
'query_string'
];
// Make sure none of the settings have been removed from ES API
expectedSettings.forEach((setting) => {
try {
expect(body.defaults.index.hasOwnProperty(setting)).to.be(true);
} catch {
throw new Error(`Expected setting "${setting}" not found.`);
}
});
});
it('should update an index settings', async () => {
const index = await createIndex();
const { body: body1 } = await getIndexSettings(index);
expect(body1.settings.index.number_of_replicas).to.be('1');
const settings = {
'index': {
'number_of_replicas': 2
}
};
await updateIndexSettings(index, settings);
const { body: body2 } = await getIndexSettings(index);
expect(body2.settings.index.number_of_replicas).to.be('2');
});
});
}

View file

@ -0,0 +1,15 @@
/*
* 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.
*/
import { API_BASE_PATH } from './constants';
export const registerHelpers = ({ supertest }) => {
const getIndexStats = (indexName) => supertest.get(`${API_BASE_PATH}/stats/${indexName}`);
return {
getIndexStats,
};
};

View file

@ -0,0 +1,60 @@
/*
* 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.
*/
import expect from '@kbn/expect';
import { initElasticsearchHelpers } from './lib';
import { registerHelpers } from './stats.helpers';
export default function ({ getService }) {
const supertest = getService('supertest');
const es = getService('es');
const {
createIndex,
cleanUp: cleanUpEsResources
} = initElasticsearchHelpers(es);
const { getIndexStats } = registerHelpers({ supertest });
describe('stats', () => {
after(() => Promise.all([cleanUpEsResources()]));
it('should fetch the index stats', async () => {
const index = await createIndex();
const { body } = await getIndexStats(index).expect(200);
const expectedStats = [
'docs',
'store',
'indexing',
'get',
'search',
'merges',
'refresh',
'flush',
'warmer',
'query_cache',
'fielddata',
'completion',
'segments',
'translog',
'request_cache',
'recovery',
];
// Make sure none of the stats have been removed from ES API
expectedStats.forEach((stat) => {
try {
expect(body.stats.total.hasOwnProperty(stat)).to.be(true);
} catch {
throw new Error(`Expected stat "${stat}" not found.`);
}
});
});
});
}