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

[savedObjects] fix error handling when Kibana index is missing #14141

Merged
merged 34 commits into from
Oct 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4b2f6cb
[savedObjects/delete+bulk_get] add failing tests
spalger Sep 21, 2017
859a5ce
[savedObjects/delete+bulk_get] improve 404 handling
spalger Sep 21, 2017
cf1b5a7
[savedObjects/client] fix mocha tests
spalger Sep 21, 2017
5032648
Merge branch 'master' of github.com:elastic/kibana into fix/savedObje…
spalger Sep 22, 2017
b99bf09
[savedObjects/tests] remove extra test wrapper
spalger Sep 22, 2017
00aaac6
[apiIntegration/kbnServer] basically disable es healthcheck
spalger Sep 22, 2017
a93cae0
[savedObjects/create] add integration test
spalger Sep 22, 2017
ec21fe4
[savedObjects/find] add failing integration tests
spalger Sep 22, 2017
3c6ee2f
[savedObjects/find] fix failing test
spalger Sep 22, 2017
de1a3fc
[savedObjects/client] explain reason for generic 404s
spalger Sep 22, 2017
8d2e623
[savedObjects/get] add integration tests
spalger Sep 22, 2017
bcd3880
[savedObjects/find] test request with unkown type
spalger Sep 22, 2017
dae161d
[savedObjects/find] add some more weird param tests
spalger Sep 22, 2017
058e66d
[savedObjects/find] test that weird params pass when no index
spalger Sep 22, 2017
69272c7
[savedObjects/update] use generic 404
spalger Sep 22, 2017
4353cd2
fix typos
spalger Sep 22, 2017
7381d0c
[savedObjects/update] add integration tests
spalger Sep 22, 2017
56842cf
remove debugging uncomment
spalger Sep 22, 2017
64e7bfb
[savedObjects/tests] move backup kibana index delete out of tests
spalger Sep 22, 2017
73df8dd
[savedObjects/tests/esArchives] remove logstash data
spalger Sep 22, 2017
ba595f8
[savedObjects] update test
spalger Sep 22, 2017
d80cf10
[uiSettings] remove detailed previously leaked from API
spalger Sep 22, 2017
a2c38e6
Merge branch 'master' of github.com:elastic/kibana into fix/savedObje…
spalger Sep 26, 2017
499af72
[functional/dashboard] wrap check that is only failing on Jenkins
spalger Sep 26, 2017
47dde37
Merge branch 'master' of github.com:elastic/kibana into fix/savedObje…
spalger Sep 28, 2017
36386dc
Merge branch 'master' of github.com:elastic/kibana into fix/savedObje…
spalger Sep 29, 2017
baa9e07
[savedObjects/error] replace decorateNotFound with createGenericNotFound
spalger Sep 29, 2017
b976a21
fix typo
spalger Sep 29, 2017
aaf4bb4
[savedObjectsClient/errors] fix decorateEsError() test
spalger Sep 29, 2017
4b683cb
Merge branch 'master' of github.com:elastic/kibana into fix/savedObje…
spalger Oct 2, 2017
eaa739f
[savedObjectsClient] fix typos
spalger Oct 2, 2017
977728f
[savedObjects/tests/functional] delete document that would normally e…
spalger Oct 2, 2017
f98c009
[savedObjectsClient/tests] use sinon assertions
spalger Oct 2, 2017
9b2f127
[savedObjects/apiTests] create without index responds with 503 after …
spalger Oct 3, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 77 additions & 70 deletions src/server/saved_objects/client/__tests__/saved_objects_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,9 @@ describe('SavedObjectsClient', () => {
title: 'Logstash'
});

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWith(callAdminCluster, 'index');
sinon.assert.calledOnce(onBeforeWrite);

const args = callAdminCluster.getCall(0).args;
expect(args[0]).to.be('index');
});

it('should use create action if ID defined and overwrite=false', async () => {
Expand All @@ -141,35 +139,35 @@ describe('SavedObjectsClient', () => {
id: 'logstash-*',
});

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWith(callAdminCluster, 'create');
sinon.assert.calledOnce(onBeforeWrite);

const args = callAdminCluster.getCall(0).args;
expect(args[0]).to.be('create');
});

it('allows for id to be provided', async () => {
await savedObjectsClient.create('index-pattern', {
title: 'Logstash'
}, { id: 'logstash-*' });

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(onBeforeWrite);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
id: 'index-pattern:logstash-*'
}));

const args = callAdminCluster.getCall(0).args;
expect(args[1].id).to.be('index-pattern:logstash-*');
sinon.assert.calledOnce(onBeforeWrite);
});

it('self-generates an ID', async () => {
await savedObjectsClient.create('index-pattern', {
title: 'Logstash'
});

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(onBeforeWrite);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
id: sinon.match(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/)
}));

const args = callAdminCluster.getCall(0).args;
expect(args[1].id).to.match(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/);
sinon.assert.calledOnce(onBeforeWrite);
});
});

Expand All @@ -182,26 +180,24 @@ describe('SavedObjectsClient', () => {
{ type: 'index-pattern', id: 'two', attributes: { title: 'Test Two' } }
]);

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(onBeforeWrite);

const args = callAdminCluster.getCall(0).args;
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, 'bulk', sinon.match({
body: [
{ create: { _type: 'doc', _id: 'config:one' } },
{ type: 'config', ...mockTimestampFields, config: { title: 'Test One' } },
{ create: { _type: 'doc', _id: 'index-pattern:two' } },
{ type: 'index-pattern', ...mockTimestampFields, 'index-pattern': { title: 'Test Two' } }
]
}));

expect(args[0]).to.be('bulk');
expect(args[1].body).to.eql([
{ create: { _type: 'doc', _id: 'config:one' } },
{ type: 'config', ...mockTimestampFields, config: { title: 'Test One' } },
{ create: { _type: 'doc', _id: 'index-pattern:two' } },
{ type: 'index-pattern', ...mockTimestampFields, 'index-pattern': { title: 'Test Two' } }
]);
sinon.assert.calledOnce(onBeforeWrite);
});

it('should overwrite objects if overwrite is truthy', async () => {
callAdminCluster.returns({ items: [] });

await savedObjectsClient.bulkCreate([{ type: 'foo', id: 'bar', attributes: {} }], { overwrite: false });
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledOnce(onBeforeWrite);
sinon.assert.calledWithExactly(callAdminCluster, 'bulk', sinon.match({
body: [
// uses create because overwriting is not allowed
Expand All @@ -210,12 +206,13 @@ describe('SavedObjectsClient', () => {
]
}));

sinon.assert.calledOnce(onBeforeWrite);

callAdminCluster.reset();
onBeforeWrite.reset();

await savedObjectsClient.bulkCreate([{ type: 'foo', id: 'bar', attributes: {} }], { overwrite: true });
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledOnce(onBeforeWrite);
sinon.assert.calledWithExactly(callAdminCluster, 'bulk', sinon.match({
body: [
// uses index because overwriting is allowed
Expand All @@ -224,7 +221,7 @@ describe('SavedObjectsClient', () => {
]
}));


sinon.assert.calledOnce(onBeforeWrite);
});

it('returns document errors', async () => {
Expand Down Expand Up @@ -310,7 +307,9 @@ describe('SavedObjectsClient', () => {

describe('#delete', () => {
it('throws notFound when ES is unable to find the document', async () => {
callAdminCluster.returns(Promise.resolve({ found: false }));
callAdminCluster.returns(Promise.resolve({
result: 'not_found'
}));

try {
await savedObjectsClient.delete('index-pattern', 'logstash-*');
Expand All @@ -323,20 +322,21 @@ describe('SavedObjectsClient', () => {
});

it('passes the parameters to callAdminCluster', async () => {
callAdminCluster.returns({});
callAdminCluster.returns({
result: 'deleted'
});
await savedObjectsClient.delete('index-pattern', 'logstash-*');

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(onBeforeWrite);

const [method, args] = callAdminCluster.getCall(0).args;
expect(method).to.be('delete');
expect(args).to.eql({
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, 'delete', {
type: 'doc',
id: 'index-pattern:logstash-*',
refresh: 'wait_for',
index: '.kibana-test'
index: '.kibana-test',
ignore: [404],
});

sinon.assert.calledOnce(onBeforeWrite);
});
});

Expand Down Expand Up @@ -416,24 +416,26 @@ describe('SavedObjectsClient', () => {
it('accepts per_page/page', async () => {
await savedObjectsClient.find({ perPage: 10, page: 6 });

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.notCalled(onBeforeWrite);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
size: 10,
from: 50
}));

const options = callAdminCluster.getCall(0).args[1];
expect(options.size).to.be(10);
expect(options.from).to.be(50);
sinon.assert.notCalled(onBeforeWrite);
});

it('can filter by fields', async () => {
await savedObjectsClient.find({ fields: ['title'] });

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.notCalled(onBeforeWrite);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
_source: [
'*.title', 'type', 'title'
]
}));

const options = callAdminCluster.getCall(0).args[1];
expect(options._source).to.eql([
'*.title', 'type', 'title'
]);
sinon.assert.notCalled(onBeforeWrite);
});
});

Expand Down Expand Up @@ -471,9 +473,11 @@ describe('SavedObjectsClient', () => {
await savedObjectsClient.get('index-pattern', 'logstash-*');

sinon.assert.notCalled(onBeforeWrite);
const [, args] = callAdminCluster.getCall(0).args;
expect(args.id).to.eql('index-pattern:logstash-*');
expect(args.type).to.eql('doc');
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
id: 'index-pattern:logstash-*',
type: 'doc'
}));
});
});

Expand All @@ -486,14 +490,17 @@ describe('SavedObjectsClient', () => {
{ id: 'two', type: 'index-pattern' }
]);

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.notCalled(onBeforeWrite);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
body: {
docs: [
{ _type: 'doc', _id: 'config:one' },
{ _type: 'doc', _id: 'index-pattern:two' }
]
}
}));

const options = callAdminCluster.getCall(0).args[1];
expect(options.body.docs).to.eql([
{ _type: 'doc', _id: 'config:one' },
{ _type: 'doc', _id: 'index-pattern:two' }
]);
sinon.assert.notCalled(onBeforeWrite);
});

it('returns early for empty objects argument', async () => {
Expand All @@ -502,7 +509,7 @@ describe('SavedObjectsClient', () => {
const response = await savedObjectsClient.bulkGet([]);

expect(response.saved_objects).to.have.length(0);
expect(callAdminCluster.notCalled).to.be(true);
sinon.assert.notCalled(callAdminCluster);
sinon.assert.notCalled(onBeforeWrite);
});

Expand Down Expand Up @@ -578,29 +585,29 @@ describe('SavedObjectsClient', () => {
{ version: newVersion - 1 }
);

const esParams = callAdminCluster.getCall(0).args[1];
expect(esParams.version).to.be(newVersion - 1);
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
version: newVersion - 1
}));
});

it('passes the parameters to callAdminCluster', async () => {
await savedObjectsClient.update('index-pattern', 'logstash-*', { title: 'Testing' });

expect(callAdminCluster.calledOnce).to.be(true);
sinon.assert.calledOnce(onBeforeWrite);

const args = callAdminCluster.getCall(0).args;

expect(args[0]).to.be('update');
expect(args[1]).to.eql({
sinon.assert.calledOnce(callAdminCluster);
sinon.assert.calledWithExactly(callAdminCluster, 'update', {
type: 'doc',
id: 'index-pattern:logstash-*',
version: undefined,
body: {
doc: { updated_at: mockTimestamp, 'index-pattern': { title: 'Testing' } }
},
ignore: [404],
refresh: 'wait_for',
index: '.kibana-test'
});

sinon.assert.calledOnce(onBeforeWrite);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ describe('savedObjectsClient/decorateEsError', () => {
expect(isForbiddenError(error)).to.be(true);
});

it('makes es.NotFound a SavedObjectsClient/NotFound error', () => {
it('discards es.NotFound errors and returns a generic NotFound error', () => {
const error = new esErrors.NotFound();
expect(isNotFoundError(error)).to.be(false);
expect(decorateEsError(error)).to.be(error);
expect(isNotFoundError(error)).to.be(true);
const genericError = decorateEsError(error);
expect(genericError).to.not.be(error);
expect(isNotFoundError(error)).to.be(false);
expect(isNotFoundError(genericError)).to.be(true);
});

it('makes es.BadRequest a SavedObjectsClient/BadRequest error', () => {
Expand Down
38 changes: 11 additions & 27 deletions src/server/saved_objects/client/lib/__tests__/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
isNotAuthorizedError,
decorateForbiddenError,
isForbiddenError,
decorateNotFoundError,
createGenericNotFoundError,
isNotFoundError,
decorateConflictError,
isConflictError,
Expand Down Expand Up @@ -145,42 +145,26 @@ describe('savedObjectsClient/errorTypes', () => {
});
});
describe('NotFound error', () => {
describe('decorateNotFoundError', () => {
it('returns original object', () => {
const error = new Error();
expect(decorateNotFoundError(error)).to.be(error);
});

it('makes the error identifiable as a NotFound error', () => {
const error = new Error();
expect(isNotFoundError(error)).to.be(false);
decorateNotFoundError(error);
describe('createGenericNotFoundError', () => {
it('makes an error identifiable as a NotFound error', () => {
const error = createGenericNotFoundError();
expect(isNotFoundError(error)).to.be(true);
});

it('adds boom properties', () => {
const error = decorateNotFoundError(new Error());
it('is a boom error, has boom properties', () => {
const error = createGenericNotFoundError();
expect(error).to.have.property('isBoom', true);
expect(error.output).to.be.an('object');
expect(error.output.statusCode).to.be(404);
});

it('preserves boom properties of input', () => {
const error = Boom.forbidden();
decorateNotFoundError(error);
expect(error.output.statusCode).to.be(403);
});

describe('error.output', () => {
it('defaults to message of erorr', () => {
const error = decorateNotFoundError(new Error('foobar'));
expect(error.output.payload).to.have.property('message', 'foobar');
});
it('prefixes message with passed reason', () => {
const error = decorateNotFoundError(new Error('foobar'), 'biz');
expect(error.output.payload).to.have.property('message', 'biz: foobar');
it('Uses "Not Found" message', () => {
const error = createGenericNotFoundError();
expect(error.output.payload).to.have.property('message', 'Not Found');
Copy link
Member

Choose a reason for hiding this comment

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

question: there is no need to change anything here, just wondering why not to use to.be?

expect(error.output.payload.message).to.be('Not Found')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the error message shows the properties that do exist, that is all

});
it('sets statusCode to 404', () => {
const error = decorateNotFoundError(new Error('foo'));
const error = createGenericNotFoundError();
expect(error.output).to.have.property('statusCode', 404);
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/server/saved_objects/client/lib/decorate_es_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
decorateBadRequestError,
decorateNotAuthorizedError,
decorateForbiddenError,
decorateNotFoundError,
createGenericNotFoundError,
decorateConflictError,
decorateEsUnavailableError,
decorateGeneralError,
Expand Down Expand Up @@ -51,7 +51,7 @@ export function decorateEsError(error) {
}

if (error instanceof NotFound) {
return decorateNotFoundError(error, reason);
return createGenericNotFoundError();
}

if (error instanceof BadRequest) {
Expand Down
4 changes: 2 additions & 2 deletions src/server/saved_objects/client/lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export function isForbiddenError(error) {

// 404 - Not Found
const CODE_NOT_FOUND = 'SavedObjectsClient/notFound';
export function decorateNotFoundError(error, reason) {
return decorate(error, CODE_NOT_FOUND, 404, reason);
export function createGenericNotFoundError() {
return decorate(Boom.notFound(), CODE_NOT_FOUND, 404);
}
export function isNotFoundError(error) {
return error && error[code] === CODE_NOT_FOUND;
Expand Down
Loading