1 |
|
2 |
|
3 | const _ = require('lodash');
|
4 | const expect = require('chai').expect;
|
5 | const Promise = require('bluebird');
|
6 | const TestConfig = require('../config');
|
7 | const Utils = require('../utils');
|
8 | const Compiler = require('../../app/services/compiler');
|
9 | const Transfer = require('../../app/services/transfer');
|
10 | const createEsClient = require('../../config/elasticsearch.js');
|
11 | const config = require('../../config/index');
|
12 |
|
13 | const log = config.log;
|
14 | const compiler = new Compiler();
|
15 | const utils = new Utils();
|
16 |
|
17 | Promise.longStackTraces();
|
18 | Promise.onPossiblyUnhandledRejection((error) => log.error('Likely error: ', error.stack));
|
19 |
|
20 | const loadMutator = (path) => compiler.compile(utils.loadFile(path));
|
21 |
|
22 | describe('transfer', function () {
|
23 | this.timeout(5000);
|
24 |
|
25 | let transfer = null;
|
26 | let source = null;
|
27 | let dest = null;
|
28 |
|
29 | before((done) => {
|
30 | source = createEsClient(TestConfig.elasticsearch.source);
|
31 | dest = createEsClient(TestConfig.elasticsearch.destination);
|
32 |
|
33 | transfer = new Transfer(source, dest);
|
34 |
|
35 | utils.deleteAllTemplates(source)
|
36 | .finally(() => utils.deleteAllTemplates(dest))
|
37 | .finally(() => utils.deleteAllIndices(source))
|
38 | .finally(() => utils.deleteAllIndices(dest))
|
39 | .finally(() => done());
|
40 | });
|
41 |
|
42 | afterEach((done) => {
|
43 | transfer.clearMutators();
|
44 | transfer.setUpdateCallback(null);
|
45 |
|
46 | utils.deleteAllTemplates(source)
|
47 | .finally(() => utils.deleteAllTemplates(dest))
|
48 | .finally(() => utils.deleteAllIndices(source))
|
49 | .finally(() => utils.deleteAllIndices(dest))
|
50 | .finally(() => done());
|
51 | });
|
52 |
|
53 | const addTemplate = () => transfer.source.indices.putTemplate({
|
54 | name: 'test_template',
|
55 | body: {
|
56 | template: 'te*',
|
57 | refresh: true,
|
58 | settings: {
|
59 | number_of_shards: 1
|
60 | },
|
61 | mappings: {
|
62 | type1: {
|
63 | _source: {
|
64 | enabled: false
|
65 | },
|
66 | properties: {
|
67 | host_name: {
|
68 | type: 'keyword'
|
69 | },
|
70 | created_at: {
|
71 | type: 'date',
|
72 | format: 'EEE MMM dd HH:mm:ss Z YYYY'
|
73 | }
|
74 | }
|
75 | }
|
76 | }
|
77 | }
|
78 | });
|
79 |
|
80 | it('should get all indices and types', (done) => {
|
81 | utils.addData(source)
|
82 | .then(() => source.indices.refresh())
|
83 | .then(() => Transfer.getIndices(source, '*'))
|
84 | .then((indices) => {
|
85 | expect(_.size(indices)).to.be.equals(3);
|
86 |
|
87 | const myindex1 = _.find(indices, {name: 'myindex1'});
|
88 | expect(myindex1).to.not.be.undefined;
|
89 | expect(_.size(myindex1.mappings)).to.be.equals(1);
|
90 | expect(myindex1.mappings.mytype1).to.not.be.undefined;
|
91 | expect(myindex1.mappings.mytype2).to.be.undefined;
|
92 |
|
93 | const myindex2 = _.find(indices, {name: 'myindex2'});
|
94 | expect(myindex2).to.not.be.undefined;
|
95 | expect(_.size(myindex2.mappings)).to.be.equals(1);
|
96 | expect(myindex2.mappings.mytype1).to.not.be.undefined;
|
97 |
|
98 | const myindex3 = _.find(indices, {name: 'myindex3'});
|
99 | expect(myindex3).to.not.be.undefined;
|
100 | expect(_.size(myindex1.mappings)).to.be.equals(1);
|
101 | expect(myindex3.mappings.mytype2).to.be.undefined;
|
102 | expect(myindex3.mappings.mytype3).to.not.be.undefined;
|
103 | })
|
104 | .then(() => done())
|
105 | .catch(done);
|
106 | });
|
107 |
|
108 | it('should throw if getTemplates arg is not a non-zero string', (done) => {
|
109 | Transfer.getTemplates(source, {})
|
110 | .then(() => done('fail'))
|
111 | .catch((e) => expect(e.message).to.be.equals('targetTemplates must be string with length'))
|
112 | .then(() => Transfer.getTemplates(source, () => {
|
113 | }))
|
114 | .then(() => done('fail'))
|
115 | .catch((e) => expect(e.message).to.be.equals('targetTemplates must be string with length'))
|
116 | .then(() => Transfer.getTemplates(source, 1))
|
117 | .then(() => done('fail'))
|
118 | .catch((e) => expect(e.message).to.be.equals('targetTemplates must be string with length'))
|
119 | .then(() => Transfer.getTemplates(source, ''))
|
120 | .then(() => done('fail'))
|
121 | .catch((e) => expect(e.message).to.be.equals('targetTemplates must be string with length'))
|
122 | .then(() => done());
|
123 | });
|
124 |
|
125 | it('should reject if there are no templates', (done) => {
|
126 | Transfer.getTemplates(source, '*')
|
127 | .then(() => done('fail'))
|
128 | .catch((error) => {
|
129 | expect(error).to.match(/Templates asked to be copied, but none found/);
|
130 | done();
|
131 | });
|
132 | });
|
133 |
|
134 | it('should get templates', (done) => {
|
135 | addTemplate()
|
136 | .then(() => Transfer.getTemplates(source, '*'))
|
137 | .then((templates) => {
|
138 | expect(templates).to.be.an.instanceof(Array);
|
139 | expect(_.size(templates)).to.be.equals(1);
|
140 | expect(templates[0].name).to.be.equals('test_template');
|
141 | expect(templates[0].index_patterns[0]).to.be.equals('te*');
|
142 | done();
|
143 | })
|
144 | .catch(done);
|
145 | });
|
146 |
|
147 | it('should put templates', (done) => {
|
148 | const sourceTemplates = [
|
149 | {
|
150 | name: 'test_template',
|
151 | template: 'te*',
|
152 | refresh: true,
|
153 | settings: {
|
154 | number_of_shards: 1
|
155 | },
|
156 | mappings: {
|
157 | type1: {
|
158 | _source: {
|
159 | enabled: false
|
160 | },
|
161 | properties: {
|
162 | host_name: {
|
163 | type: 'text'
|
164 | },
|
165 | created_at: {
|
166 | type: 'date',
|
167 | format: 'EEE MMM dd HH:mm:ss Z YYYY'
|
168 | }
|
169 | }
|
170 | }
|
171 | }
|
172 | },
|
173 | {
|
174 | name: 'test_template_2',
|
175 | template: 'te2*',
|
176 | refresh: true,
|
177 | settings: {
|
178 | number_of_shards: 1
|
179 | },
|
180 | mappings: {
|
181 | type1: {
|
182 | _source: {
|
183 | enabled: false
|
184 | },
|
185 | properties: {
|
186 | host_name: {
|
187 | type: 'text'
|
188 | },
|
189 | created_at: {
|
190 | type: 'date',
|
191 | format: 'EEE MMM dd HH:mm:ss Z YYYY'
|
192 | }
|
193 | }
|
194 | }
|
195 | }
|
196 | }
|
197 | ];
|
198 |
|
199 | transfer.putTemplates(sourceTemplates)
|
200 | .then(() => transfer.dest.indices.getTemplate())
|
201 | .then((destTemplates) => {
|
202 | expect(destTemplates).to.have.property('test_template');
|
203 | expect(destTemplates.test_template.index_patterns[0]).to.be.equals('te*');
|
204 | expect(destTemplates).to.have.property('test_template_2');
|
205 | expect(destTemplates.test_template_2.index_patterns[0]).to.be.equals('te2*');
|
206 | done();
|
207 | })
|
208 | .catch(done);
|
209 | });
|
210 |
|
211 | it('should get indices', (done) => {
|
212 | const index = {
|
213 | settings: {
|
214 | number_of_shards: 1,
|
215 | number_of_replicas: 2
|
216 | },
|
217 | mappings: {
|
218 | type1: {
|
219 | properties: {
|
220 | field1: {type: 'text'}
|
221 | }
|
222 | }
|
223 | },
|
224 | aliases: {
|
225 | alias_1: {}
|
226 | }
|
227 | };
|
228 |
|
229 | transfer.source.indices.create({
|
230 | index: 'twitter1',
|
231 | body: index
|
232 | })
|
233 | .then(() => transfer.source.indices.create({
|
234 | index: 'twitter2',
|
235 | body: index
|
236 | }))
|
237 | .then(() => Transfer.getIndices(transfer.source, '*'))
|
238 | .then((indices) => {
|
239 | expect(indices).to.have.length(2);
|
240 | expect(Object.keys(indices[0])).to.include('name', 'settings', 'mappings', 'alias', 'warmers');
|
241 | expect(indices[0].name).to.be.oneOf([
|
242 | 'twitter1',
|
243 | 'twitter2'
|
244 | ]);
|
245 | expect(Object.keys(indices[1])).to.include('name', 'settings', 'mappings', 'alias', 'warmers');
|
246 | expect(indices[1].name).to.be.oneOf([
|
247 | 'twitter1',
|
248 | 'twitter2'
|
249 | ]);
|
250 | expect(indices[0].name).to.not.eql(indices[1].name);
|
251 | })
|
252 | .then(() => done());
|
253 | });
|
254 |
|
255 | it('should reject if there is an error during get indices', (done) => {
|
256 | Transfer.getIndices(transfer.source, 'missingIndexName')
|
257 | .then(() => done('fail'))
|
258 | .catch(() => done());
|
259 | });
|
260 |
|
261 | it('should put indices', (done) => {
|
262 | const index = {
|
263 | settings: {
|
264 | number_of_shards: 1,
|
265 | number_of_replicas: 2
|
266 | },
|
267 | mappings: {
|
268 | type1: {
|
269 | properties: {
|
270 | field1: {type: 'long'}
|
271 | }
|
272 | }
|
273 | },
|
274 | aliases: {
|
275 | alias_1: {}
|
276 | }
|
277 | };
|
278 |
|
279 | utils.deleteAllIndices(transfer.dest)
|
280 | .then(() => transfer.source.indices.create({
|
281 | index: 'twitter1',
|
282 | body: index
|
283 | }))
|
284 | .then(() => transfer.source.indices.create({
|
285 | index: 'twitter2',
|
286 | body: index
|
287 | }))
|
288 | .then(() => Transfer.getIndices(transfer.source, '*'))
|
289 | .then((indices) => transfer.putIndices(indices))
|
290 | .then(() => transfer.dest.indices.get({index: '*'}))
|
291 | .then((response) => {
|
292 | expect(_.size(response)).to.be.equals(2);
|
293 |
|
294 | expect(response.twitter1).to.not.be.undefined;
|
295 | expect(response.twitter1.settings.index.number_of_shards).to.be.equals('1');
|
296 | expect(response.twitter1.settings.index.number_of_replicas).to.be.equals('2');
|
297 | expect(response.twitter1.mappings.type1.properties.field1.type).to.be.equals('long');
|
298 |
|
299 | expect(response.twitter2).to.not.be.undefined;
|
300 | expect(response.twitter2.settings.index.number_of_shards).to.be.equals('1');
|
301 | expect(response.twitter2.settings.index.number_of_replicas).to.be.equals('2');
|
302 | expect(response.twitter2.mappings.type1.properties.field1.type).to.be.equals('long');
|
303 | })
|
304 | .then(() => done())
|
305 | .catch(done);
|
306 | });
|
307 |
|
308 | it('should reject if there is an error during put indices', (done) => {
|
309 | const indices = [
|
310 | {
|
311 | name: 'something',
|
312 | settings: {
|
313 | number_of_shards: -100
|
314 | },
|
315 | mappings: {
|
316 | type1: {
|
317 | properties: {
|
318 | field1: {type: 'text'}
|
319 | }
|
320 | }
|
321 | },
|
322 | aliases: {
|
323 | alias_1: {}
|
324 | }
|
325 | }
|
326 | ];
|
327 |
|
328 | transfer.putIndices(indices)
|
329 | .then(() => done('fail'))
|
330 | .catch(() => done());
|
331 | });
|
332 |
|
333 | it('should get all data in given index and type', (done) => {
|
334 | const index = 'myindex1';
|
335 | const type = 'mytype1';
|
336 |
|
337 | utils.createIndices(transfer, index, index)
|
338 | .then(() => source.bulk({
|
339 | refresh: true,
|
340 | body: require('./lotsOfData.json')
|
341 | }))
|
342 | .then(() => transfer.transferData(index, type, 10))
|
343 | .then(() => transfer.dest.indices.refresh({index: '*'}))
|
344 | .then(() => transfer.dest.search({size: 100}))
|
345 | .then((response) => expect(response.hits.hits).to.be.length(20))
|
346 | .then(() => done())
|
347 | .catch(done);
|
348 | });
|
349 |
|
350 | it('should throw if no indexName is provided', (done) => {
|
351 | const throws = () => transfer.transferData(null, 'mytype1');
|
352 | expect(throws).to.throw('targetIndex must be string with length');
|
353 | done();
|
354 | });
|
355 |
|
356 | it('should throw if no typeName is provided', (done) => {
|
357 | const throws = () => transfer.transferData('myindex', null);
|
358 | expect(throws).to.throw('targetType must be string with length');
|
359 | done();
|
360 | });
|
361 |
|
362 | it('should reject if index does not exist', (done) => {
|
363 | transfer.transferData('notthere', 'mytype1')
|
364 | .then(() => done('fail'))
|
365 | .catch(() => done());
|
366 | });
|
367 |
|
368 | it('should call callback with status updates', (done) => {
|
369 | const index = 'myindex1';
|
370 | const data = [];
|
371 |
|
372 | _.times(100, (n) => {
|
373 | data.push({
|
374 | index: {
|
375 | _index: index,
|
376 | _type: index
|
377 | }
|
378 | });
|
379 | data.push({something: `data${n}`});
|
380 | });
|
381 |
|
382 | transfer.setUpdateCallback((status) => {
|
383 | log.info('status', status);
|
384 | if (status.transferred === 100) {
|
385 | done();
|
386 | }
|
387 | });
|
388 |
|
389 | utils.createIndices(transfer, index, index)
|
390 | .then(() => source.bulk({body: data}))
|
391 | .then((results) => results.errors ? Promise.reject(`errors: ${results.errors}`) : source.indices.refresh())
|
392 | .then(() => transfer.transferData(index, index))
|
393 | .catch(done);
|
394 | });
|
395 |
|
396 | it('should return the original when no mutator is present', (done) => {
|
397 | source.indices.create({
|
398 | index: 'index_to_mutate',
|
399 | body: {settings: {number_of_shards: 4}}
|
400 | })
|
401 | .then(() => transfer.transferIndices('index_to_mutate'))
|
402 | .then(() => dest.indices.refresh())
|
403 | .then(() => dest.indices.get({index: 'index_to_mutate'}))
|
404 | .then((index) => expect(index.index_to_mutate.settings.index.number_of_shards).to.be.equals('4'))
|
405 | .then(() => done());
|
406 | });
|
407 |
|
408 | it('should return the original when the mutator does not apply', (done) => {
|
409 | transfer.setMutators({index: [loadMutator(`${__dirname}/validMutators/index.js`)]});
|
410 |
|
411 | source.indices.create({
|
412 | index: 'index_not_to_mutate',
|
413 | body: {settings: {number_of_shards: 4}}
|
414 | })
|
415 | .then(() => transfer.transferIndices('index_not_to_mutate'))
|
416 | .then(() => dest.indices.refresh())
|
417 | .then(() => dest.indices.get({index: 'index_not_to_mutate'}))
|
418 | .then((index) => expect(index.index_not_to_mutate.settings.index.number_of_shards).to.be.equals('4'))
|
419 | .then(() => done())
|
420 | .catch(done);
|
421 | });
|
422 |
|
423 | it('should use index mutator to change index during transfer', (done) => {
|
424 | transfer.setMutators({index: [loadMutator(`${__dirname}/validMutators/index.js`)]});
|
425 |
|
426 | source.indices.create({
|
427 | index: 'index_to_mutate',
|
428 | body: {settings: {number_of_shards: 4}}
|
429 | })
|
430 | .then(() => transfer.transferIndices('index_to_mutate'))
|
431 | .then(() => dest.indices.refresh())
|
432 | .then(() => dest.indices.get({index: 'new_index_name'}))
|
433 | .then((index) => {
|
434 | expect(index.new_index_name.settings.index.number_of_shards).to.be.equals('4');
|
435 | return dest.indices.get({index: 'index_to_mutate'}).catch((error) => {
|
436 | expect(error.status).to.be.equals(404);
|
437 | return 'not found';
|
438 | });
|
439 | })
|
440 | .then((result) => expect(result).to.be.equals('not found'))
|
441 | .then(() => done())
|
442 | .catch(done);
|
443 | });
|
444 |
|
445 | it('should call mutator with arguments', (done) => {
|
446 | const mutator = loadMutator(`${__dirname}/validMutators/indexWithArgs.js`);
|
447 | mutator.arguments = {
|
448 | name: 'creative',
|
449 | target: 'index_to_mutate'
|
450 | };
|
451 |
|
452 | transfer.setMutators({index: [mutator]});
|
453 |
|
454 | source.indices.create({
|
455 | index: 'index_to_mutate',
|
456 | body: {settings: {number_of_shards: 4}}
|
457 | })
|
458 | .then(() => transfer.transferIndices('index_to_mutate'))
|
459 | .then(() => dest.indices.refresh())
|
460 | .then(() => dest.indices.get({index: 'creative'}))
|
461 | .then((index) => {
|
462 | expect(index.creative.settings.index.number_of_shards).to.be.equals('4');
|
463 | return dest.indices.get({index: 'index_to_mutate'}).catch((error) => {
|
464 | expect(error.status).to.be.equals(404);
|
465 | return 'not found';
|
466 | });
|
467 | })
|
468 | .then((result) => expect(result).to.be.equals('not found'))
|
469 | .then(() => done())
|
470 | .catch(done);
|
471 | });
|
472 |
|
473 | it('should use a template mutator to change template during transfer', (done) => {
|
474 | transfer.setMutators({template: [loadMutator(`${__dirname}/validMutators/template.js`)]});
|
475 |
|
476 | source.indices.putTemplate({
|
477 | name: 'test_template',
|
478 | body: {template: 'template_this*'}
|
479 | })
|
480 | .then(() => transfer.transferTemplates('test_template'))
|
481 | .then(() => dest.indices.getTemplate({name: 'test_template'}))
|
482 | .then((template) => expect(template.test_template.index_patterns[0]).to.be.equals('template_that*'))
|
483 | .then(() => done())
|
484 | .catch(done);
|
485 | });
|
486 |
|
487 | it('should use a data mutator to change documents during transfer', (done) => {
|
488 | transfer.setMutators({data: [loadMutator(`${__dirname}/validMutators/data.js`)]});
|
489 |
|
490 | const srcIndex = 'something_1990-05-21';
|
491 | const dstIndex = 'something_1990-05';
|
492 | const type = 'sometype';
|
493 |
|
494 | utils.createIndices(transfer, srcIndex, dstIndex)
|
495 | .then(() => source.index({
|
496 | index: srcIndex,
|
497 | type: type,
|
498 | body: {field: 'daata'}
|
499 | }))
|
500 | .then(() => source.indices.refresh())
|
501 | .then(() => transfer.transferData(srcIndex, type))
|
502 | .then(() => dest.indices.refresh())
|
503 | .then(() => dest.search({index: 'something_1990*'}))
|
504 | .then((document) => {
|
505 | expect(document.hits.hits.length).to.be.equals(1);
|
506 | expect(document.hits.hits[0]._index).to.be.equals('something_1990-05');
|
507 | expect(document.hits.hits[0]._source.field).to.be.equals('daata');
|
508 | })
|
509 | .then(() => done())
|
510 | .catch(done);
|
511 | });
|
512 |
|
513 | it('should use a data mutator to drop some documents during transfer', (done) => {
|
514 | const mutator = loadMutator(`${__dirname}/validMutators/dropWithArgs.js`);
|
515 | mutator.arguments = {
|
516 | match: 'daata2'
|
517 | };
|
518 | transfer.setMutators({data: [mutator]});
|
519 |
|
520 | const index = 'something_1990-05-21';
|
521 | const type = 'sometype';
|
522 |
|
523 | utils.createIndices(transfer, index, index)
|
524 | .then(() => source.index({
|
525 | index: index,
|
526 | type: type,
|
527 | body: {field: 'daata'}
|
528 | }))
|
529 | .then(() => source.index({
|
530 | index: index,
|
531 | type: type,
|
532 | body: {field: 'daata2'}
|
533 | }))
|
534 | .then(() => source.indices.refresh())
|
535 | .then(() => transfer.transferData(index, type))
|
536 | .then(() => dest.indices.refresh())
|
537 | .then(() => dest.search({index}))
|
538 | .then((document) => {
|
539 | expect(document.hits.hits.length).to.be.equals(1);
|
540 | expect(document.hits.hits[0]._index).to.be.equals(index);
|
541 | expect(document.hits.hits[0]._source.field).to.be.equals('daata');
|
542 | })
|
543 | .then(() => done())
|
544 | .catch(done);
|
545 | });
|
546 | }); |
\ | No newline at end of file |