Skip to content

Commit b423288

Browse files
committed
Better message in case of unsupported build flavor (#1498)
1 parent 1e4d2b6 commit b423288

File tree

4 files changed

+159
-26
lines changed

4 files changed

+159
-26
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ of `^7.10.0`).
5454

5555
### Compatibility
5656

57-
The library is compatible with all Elasticsearch versions since 5.x, and you should use the same major version of the Elasticsearch instance that you are using.
57+
Elastic language clients are guaranteed to be able to communicate with Elasticsearch or Elastic solutions running on the same major version and greater or equal minor version.
58+
59+
Language clients are forward compatible; meaning that clients support communicating with greater minor versions of Elasticsearch. Elastic language clients are not guaranteed to be backwards compatible.
5860

5961
| Elasticsearch Version | Client Version |
6062
| --------------------- |----------------|

docs/installation.asciidoc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ To install a specific major version of the client, run the following command:
1717
npm install @elastic/elasticsearch@<major>
1818
----
1919

20-
To learn more about the supported major versions, please refer to the
20+
To learn more about the supported major versions, please refer to the
2121
<<js-compatibility-matrix>>.
2222

2323
[discrete]
@@ -37,7 +37,7 @@ to support that version for at least another minor release. If you are using the
3737
with a version of Node.js that will be unsupported soon, you will see a warning
3838
in your logs (the client will start logging the warning with two minors in advance).
3939

40-
Unless you are *always* using a supported version of Node.js,
40+
Unless you are *always* using a supported version of Node.js,
4141
we recommend defining the client dependency in your
4242
`package.json` with the `~` instead of `^`. In this way, you will lock the
4343
dependency on the minor release and not the major. (for example, `~7.10.0` instead
@@ -62,9 +62,12 @@ of `^7.10.0`).
6262
[[js-compatibility-matrix]]
6363
=== Compatibility matrix
6464

65-
The library is compatible with all {es} versions since 5.x. We recommend you to
66-
use the same major version of the client as the {es} instance that you are
67-
using.
65+
Elastic language clients are guaranteed to be able to communicate with Elasticsearch
66+
or Elastic solutions running on the same major version and greater or equal minor version.
67+
68+
Language clients are forward compatible; meaning that clients support communicating
69+
with greater minor versions of Elasticsearch. Elastic language clients are not
70+
guaranteed to be backwards compatible.
6871

6972
[%header,cols=2*]
7073
|===
@@ -91,4 +94,4 @@ using.
9194
WARNING: There is no official support for the browser environment. It exposes
9295
your {es} instance to everyone, which could lead to security issues. We
9396
recommend you to write a lightweight proxy that uses this client instead,
94-
you can see a proxy example https://github.com/elastic/elasticsearch-js/tree/master/docs/examples/proxy[here].
97+
you can see a proxy example https://github.com/elastic/elasticsearch-js/tree/master/docs/examples/proxy[here].

lib/Transport.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class Transport {
6969
this.generateRequestId = opts.generateRequestId || generateRequestId()
7070
this.name = opts.name
7171
this.opaqueIdPrefix = opts.opaqueIdPrefix
72-
this[kProductCheck] = 0 // 0 = to be checked, 1 = checking, 2 = checked-ok, 3 checked-notok
72+
this[kProductCheck] = 0 // 0 = to be checked, 1 = checking, 2 = checked-ok, 3 checked-notok, 4 checked-nodefault
7373
this[kApiVersioning] = process.env.ELASTIC_CLIENT_APIVERSIONING === 'true'
7474

7575
this.nodeFilter = opts.nodeFilter || defaultNodeFilter
@@ -455,9 +455,12 @@ class Transport {
455455
prepareRequest()
456456
} else {
457457
// wait for product check to finish
458-
productCheckEmitter.once('product-check', status => {
458+
productCheckEmitter.once('product-check', (error, status) => {
459459
if (status === false) {
460-
const err = new ProductNotSupportedError(result)
460+
const err = error || new ProductNotSupportedError(result)
461+
if (this[kProductCheck] === 4) {
462+
err.message = 'The client noticed that the server is not a supported distribution of Elasticsearch'
463+
}
461464
this.emit('request', err, result)
462465
process.nextTick(callback, err, result)
463466
} else {
@@ -470,8 +473,11 @@ class Transport {
470473
}
471474
}
472475
// the product check is finished and it's not Elasticsearch
473-
} else if (this[kProductCheck] === 3) {
476+
} else if (this[kProductCheck] === 3 || this[kProductCheck] === 4) {
474477
const err = new ProductNotSupportedError(result)
478+
if (this[kProductCheck] === 4) {
479+
err.message = 'The client noticed that the server is not a supported distribution of Elasticsearch'
480+
}
475481
this.emit('request', err, result)
476482
process.nextTick(callback, err, result)
477483
// the product check finished and it's Elasticsearch
@@ -550,42 +556,48 @@ class Transport {
550556
if (err.statusCode === 401 || err.statusCode === 403) {
551557
this[kProductCheck] = 2
552558
process.emitWarning('The client is unable to verify that the server is Elasticsearch due to security privileges on the server side. Some functionality may not be compatible if the server is running an unsupported product.')
553-
productCheckEmitter.emit('product-check', true)
559+
productCheckEmitter.emit('product-check', null, true)
554560
} else {
555561
this[kProductCheck] = 0
556-
productCheckEmitter.emit('product-check', false)
562+
productCheckEmitter.emit('product-check', err, false)
557563
}
558564
} else {
559565
debug('Checking elasticsearch version', result.body, result.headers)
560566
if (result.body.version == null || typeof result.body.version.number !== 'string') {
561567
debug('Can\'t access Elasticsearch version')
562-
return productCheckEmitter.emit('product-check', false)
568+
return productCheckEmitter.emit('product-check', null, false)
563569
}
564570
const tagline = result.body.tagline
565571
const version = result.body.version.number.split('.')
566572
const major = Number(version[0])
567573
const minor = Number(version[1])
568574
if (major < 6) {
569-
return productCheckEmitter.emit('product-check', false)
575+
return productCheckEmitter.emit('product-check', null, false)
570576
} else if (major >= 6 && major < 7) {
571577
if (tagline !== 'You Know, for Search') {
572578
debug('Bad tagline')
573-
return productCheckEmitter.emit('product-check', false)
579+
return productCheckEmitter.emit('product-check', null, false)
574580
}
575581
} else if (major === 7 && minor < 14) {
576-
if (tagline !== 'You Know, for Search' || result.body.version.build_flavor !== 'default') {
577-
debug('Bad tagline or build_flavor')
578-
return productCheckEmitter.emit('product-check', false)
582+
if (tagline !== 'You Know, for Search') {
583+
debug('Bad tagline')
584+
return productCheckEmitter.emit('product-check', null, false)
585+
}
586+
587+
if (result.body.version.build_flavor !== 'default') {
588+
debug('Bad build_flavor')
589+
this[kProductCheck] = 4
590+
return productCheckEmitter.emit('product-check', null, false)
579591
}
580592
} else {
581593
if (result.headers['x-elastic-product'] !== 'Elasticsearch') {
582594
debug('x-elastic-product not recognized')
583-
return productCheckEmitter.emit('product-check', false)
595+
return productCheckEmitter.emit('product-check', null, false)
584596
}
585597
}
586598
debug('Valid Elasticsearch distribution')
587599
this[kProductCheck] = 2
588-
productCheckEmitter.emit('product-check', true)
600+
productCheckEmitter.emit('product-check', null, true)
589601
}
590602
})
591603
}

test/acceptance/product-check.test.js

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const { Client } = require('../../')
2424
const {
2525
connection: {
2626
MockConnectionTimeout,
27+
MockConnectionError,
2728
buildMockConnection
2829
}
2930
} = require('../utils')
@@ -210,7 +211,7 @@ test('No errors ≤v7.13', t => {
210211
})
211212
})
212213

213-
test('Errors ≤v7.13', t => {
214+
test('Errors ≤v7.13 (tagline)', t => {
214215
t.plan(3)
215216
const MockConnection = buildMockConnection({
216217
onRequest (params) {
@@ -222,7 +223,7 @@ test('Errors ≤v7.13', t => {
222223
cluster_uuid: 'cQ5pAMvRRTyEzObH4L5mTA',
223224
version: {
224225
number: '7.13.0-SNAPSHOT',
225-
build_flavor: 'other',
226+
build_flavor: 'default',
226227
build_type: 'docker',
227228
build_hash: '5fb4c050958a6b0b6a70a6fb3e616d0e390eaac3',
228229
build_date: '2021-07-10T01:45:02.136546168Z',
@@ -271,6 +272,83 @@ test('Errors ≤v7.13', t => {
271272
})
272273
})
273274

275+
test('Errors ≤v7.13 (build flavor)', t => {
276+
t.plan(5)
277+
const MockConnection = buildMockConnection({
278+
onRequest (params) {
279+
return {
280+
statusCode: 200,
281+
body: {
282+
name: '1ef419078577',
283+
cluster_name: 'docker-cluster',
284+
cluster_uuid: 'cQ5pAMvRRTyEzObH4L5mTA',
285+
version: {
286+
number: '7.13.0-SNAPSHOT',
287+
build_flavor: 'other',
288+
build_type: 'docker',
289+
build_hash: '5fb4c050958a6b0b6a70a6fb3e616d0e390eaac3',
290+
build_date: '2021-07-10T01:45:02.136546168Z',
291+
build_snapshot: true,
292+
lucene_version: '8.9.0',
293+
minimum_wire_compatibility_version: '7.15.0',
294+
minimum_index_compatibility_version: '7.0.0'
295+
},
296+
tagline: 'You Know, for Search'
297+
}
298+
}
299+
}
300+
})
301+
302+
const requests = [{
303+
method: 'GET',
304+
path: '/'
305+
}, {
306+
method: 'POST',
307+
path: '/foo/_search'
308+
}, {
309+
method: 'POST',
310+
path: '/foo/_search'
311+
}]
312+
313+
const client = new Client({
314+
node: 'http://localhost:9200',
315+
Connection: MockConnection
316+
})
317+
318+
client.on('request', (err, event) => {
319+
const req = requests.shift()
320+
if (req.method === 'GET') {
321+
t.error(err)
322+
} else {
323+
t.equal(err.message, 'The client noticed that the server is not a supported distribution of Elasticsearch')
324+
}
325+
})
326+
327+
client.search({
328+
index: 'foo',
329+
body: {
330+
query: {
331+
match_all: {}
332+
}
333+
}
334+
}, (err, result) => {
335+
t.equal(err.message, 'The client noticed that the server is not a supported distribution of Elasticsearch')
336+
})
337+
338+
setTimeout(() => {
339+
client.search({
340+
index: 'foo',
341+
body: {
342+
query: {
343+
match_all: {}
344+
}
345+
}
346+
}, (err, result) => {
347+
t.equal(err.message, 'The client noticed that the server is not a supported distribution of Elasticsearch')
348+
})
349+
}, 100)
350+
})
351+
274352
test('No errors v6', t => {
275353
t.plan(7)
276354
const MockConnection = buildMockConnection({
@@ -571,7 +649,7 @@ test('500 error', t => {
571649
}
572650
}
573651
}, (err, result) => {
574-
t.equal(err.message, 'The client noticed that the server is not Elasticsearch and we do not support this unknown product.')
652+
t.equal(err.message, 'Response Error')
575653

576654
client.search({
577655
index: 'foo',
@@ -608,7 +686,7 @@ test('TimeoutError', t => {
608686
if (req.method === 'GET') {
609687
t.error(err)
610688
} else {
611-
t.equal(err.message, 'The client noticed that the server is not Elasticsearch and we do not support this unknown product.')
689+
t.equal(err.message, 'Request timed out')
612690
}
613691
})
614692

@@ -620,7 +698,45 @@ test('TimeoutError', t => {
620698
}
621699
}
622700
}, (err, result) => {
623-
t.equal(err.message, 'The client noticed that the server is not Elasticsearch and we do not support this unknown product.')
701+
t.equal(err.message, 'Request timed out')
702+
})
703+
})
704+
705+
test('ConnectionError', t => {
706+
t.plan(3)
707+
708+
const requests = [{
709+
method: 'GET',
710+
path: '/'
711+
}, {
712+
method: 'POST',
713+
path: '/foo/_search'
714+
}]
715+
716+
const client = new Client({
717+
node: 'http://localhost:9200',
718+
Connection: MockConnectionError,
719+
maxRetries: 0
720+
})
721+
722+
client.on('request', (err, event) => {
723+
const req = requests.shift()
724+
if (req.method === 'GET') {
725+
t.error(err)
726+
} else {
727+
t.equal(err.message, 'Kaboom')
728+
}
729+
})
730+
731+
client.search({
732+
index: 'foo',
733+
body: {
734+
query: {
735+
match_all: {}
736+
}
737+
}
738+
}, (err, result) => {
739+
t.equal(err.message, 'Kaboom')
624740
})
625741
})
626742

0 commit comments

Comments
 (0)