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

ES Healthcheck v6 mapping compatibility #12714

Merged
merged 10 commits into from
Jul 10, 2017
2 changes: 2 additions & 0 deletions src/core_plugins/elasticsearch/lib/__tests__/health_check.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('plugins/elasticsearch', () => {

cluster = { callWithInternalUser: sinon.stub() };
cluster.callWithInternalUser.withArgs('index', sinon.match.any).returns(Promise.resolve());
cluster.callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve({ _id: 1, _version: 1 }));
cluster.callWithInternalUser.withArgs('mget', sinon.match.any).returns(Promise.resolve({ ok: true }));
cluster.callWithInternalUser.withArgs('get', sinon.match.any).returns(Promise.resolve({ found: false }));
cluster.callWithInternalUser.withArgs('search', sinon.match.any).returns(Promise.resolve({ hits: { hits: [] } }));
Expand All @@ -56,6 +57,7 @@ describe('plugins/elasticsearch', () => {
const get = sinon.stub();
get.withArgs('elasticsearch.url').returns(esUrl);
get.withArgs('kibana.index').returns('.my-kibana');
get.withArgs('pkg.version').returns('1.0.0');

const set = sinon.stub();

Expand Down
32 changes: 14 additions & 18 deletions src/core_plugins/elasticsearch/lib/__tests__/is_upgradeable.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ describe('plugins/elasticsearch', function () {
})
};

function upgradeDoc(_id, _version, bool) {
function upgradeDoc(id, _version, bool) {
describe('', function () {
before(function () { version = _version; });

it(`should return ${bool} for ${_id} <= ${version}`, function () {
expect(isUpgradeable(server, { _id: _id })).to.be(bool);
it(`should return ${bool} for ${id} <= ${version}`, function () {
expect(isUpgradeable(server, { id: id })).to.be(bool);
});

after(function () { version = pkg.version; });
Expand All @@ -42,32 +42,28 @@ describe('plugins/elasticsearch', function () {
upgradeDoc('4.1.0-rc1-SNAPSHOT', '4.1.0-rc1', false);
upgradeDoc('5.0.0-alpha1', '5.0.0', false);

it('should handle missing _id field', function () {
const doc = {
'_index': '.kibana',
'_type': 'config',
'_score': 1,
'_source': {
it('should handle missing id field', function () {
const configSavedObject = {
'type': 'config',
'attributes': {
'buildNum': 1.7976931348623157e+308,
'defaultIndex': '[logstash-]YYYY.MM.DD'
}
};

expect(isUpgradeable(server, doc)).to.be(false);
expect(isUpgradeable(server, configSavedObject)).to.be(false);
});

it('should handle _id of @@version', function () {
const doc = {
'_index': '.kibana',
'_type': 'config',
'_id': '@@version',
'_score': 1,
'_source': {
it('should handle id of @@version', function () {
const configSavedObject = {
'type': 'config',
'id': '@@version',
'attributes': {
'buildNum': 1.7976931348623157e+308,
'defaultIndex': '[logstash-]YYYY.MM.DD'
}
};
expect(isUpgradeable(server, doc)).to.be(false);
expect(isUpgradeable(server, configSavedObject)).to.be(false);
});

});
Expand Down
114 changes: 54 additions & 60 deletions src/core_plugins/elasticsearch/lib/__tests__/upgrade_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('plugins/elasticsearch', function () {
describe('lib/upgrade_config', function () {
let get;
let server;
let callWithInternalUser;
let savedObjectsClient;
let upgrade;

beforeEach(function () {
Expand All @@ -17,7 +17,9 @@ describe('plugins/elasticsearch', function () {
get.withArgs('pkg.version').returns('4.0.1');
get.withArgs('pkg.buildNum').returns(Math.random());

callWithInternalUser = sinon.stub();
savedObjectsClient = {
create: sinon.stub()
};

server = {
log: sinon.stub(),
Expand All @@ -26,22 +28,15 @@ describe('plugins/elasticsearch', function () {
get: get
};
},
plugins: {
elasticsearch: {
getCluster: sinon.stub().withArgs('admin').returns({
callWithInternalUser: callWithInternalUser
})
}
}
};
upgrade = upgradeConfig(server);
upgrade = upgradeConfig(server, savedObjectsClient);
});

describe('nothing is found', function () {
const response = { hits: { hits:[] } };
const configSavedObjects = { hits: { hits:[] } };

beforeEach(function () {
callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve());
savedObjectsClient.create.returns(Promise.resolve({ id: 1, version: 1 }));
});

describe('production', function () {
Expand All @@ -52,17 +47,17 @@ describe('plugins/elasticsearch', function () {
});

it('should resolve buildNum to pkg.buildNum config', function () {
return upgrade(response).then(function () {
sinon.assert.calledOnce(callWithInternalUser);
const params = callWithInternalUser.args[0][1];
expect(params.body).to.have.property('buildNum', get('pkg.buildNum'));
return upgrade(configSavedObjects).then(function () {
sinon.assert.calledOnce(savedObjectsClient.create);
const attributes = savedObjectsClient.create.args[0][1];
expect(attributes).to.have.property('buildNum', get('pkg.buildNum'));
});
});

it('should resolve version to pkg.version config', function () {
return upgrade(response).then(function () {
const params = callWithInternalUser.args[0][1];
expect(params).to.have.property('id', get('pkg.version'));
return upgrade(configSavedObjects).then(function () {
const options = savedObjectsClient.create.args[0][2];
expect(options).to.have.property('id', get('pkg.version'));
});
});
});
Expand All @@ -75,68 +70,68 @@ describe('plugins/elasticsearch', function () {
});

it('should resolve buildNum to pkg.buildNum config', function () {
return upgrade(response).then(function () {
const params = callWithInternalUser.args[0][1];
expect(params.body).to.have.property('buildNum', get('pkg.buildNum'));
return upgrade(configSavedObjects).then(function () {
const attributes = savedObjectsClient.create.args[0][1];
expect(attributes).to.have.property('buildNum', get('pkg.buildNum'));
});
});

it('should resolve version to pkg.version config', function () {
return upgrade(response).then(function () {
const params = callWithInternalUser.args[0][1];
expect(params).to.have.property('id', get('pkg.version'));
return upgrade(configSavedObjects).then(function () {
const options = savedObjectsClient.create.args[0][2];
expect(options).to.have.property('id', get('pkg.version'));
});
});
});
});

it('should resolve with undefined if the current version is found', function () {
const response = { hits: { hits: [ { _id: '4.0.1' } ] } };
return upgrade(response).then(function (resp) {
const configSavedObjects = [ { id: '4.0.1' } ];
return upgrade(configSavedObjects).then(function (resp) {
expect(resp).to.be(undefined);
});
});

it('should create new config if the nothing is upgradeable', function () {
get.withArgs('pkg.buildNum').returns(9833);
callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve());

const response = { hits: { hits: [ { _id: '4.0.1-alpha3' }, { _id: '4.0.1-beta1' }, { _id: '4.0.0-SNAPSHOT1' } ] } };
return upgrade(response).then(function () {
sinon.assert.calledOnce(callWithInternalUser);
const params = callWithInternalUser.args[0][1];
expect(params).to.have.property('body');
expect(params.body).to.have.property('buildNum', 9833);
expect(params).to.have.property('index', '.my-kibana');
expect(params).to.have.property('type', 'config');
expect(params).to.have.property('id', '4.0.1');
savedObjectsClient.create.returns(Promise.resolve({ id: 1, version: 1 }));

const configSavedObjects = [ { id: '4.0.1-alpha3' }, { id: '4.0.1-beta1' }, { id: '4.0.0-SNAPSHOT1' } ];
return upgrade(configSavedObjects).then(function () {
sinon.assert.calledOnce(savedObjectsClient.create);
const savedObjectType = savedObjectsClient.create.args[0][0];
expect(savedObjectType).to.eql('config');
const attributes = savedObjectsClient.create.args[0][1];
expect(attributes).to.have.property('buildNum', 9833);
const options = savedObjectsClient.create.args[0][2];
expect(options).to.have.property('id', '4.0.1');
});
});

it('should update the build number on the new config', function () {
get.withArgs('pkg.buildNum').returns(5801);
callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve());

const response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1 } } ] } };

return upgrade(response).then(function () {
sinon.assert.calledOnce(callWithInternalUser);
const params = callWithInternalUser.args[0][1];
expect(params).to.have.property('body');
expect(params.body).to.have.property('buildNum', 5801);
expect(params).to.have.property('index', '.my-kibana');
expect(params).to.have.property('type', 'config');
expect(params).to.have.property('id', '4.0.1');
savedObjectsClient.create.returns(Promise.resolve({ id: 1, version: 1 }));

const configSavedObjects = [ { id: '4.0.0', attributes: { buildNum: 1 } } ];

return upgrade(configSavedObjects).then(function () {
sinon.assert.calledOnce(savedObjectsClient.create);
const attributes = savedObjectsClient.create.args[0][1];
expect(attributes).to.have.property('buildNum', 5801);
const savedObjectType = savedObjectsClient.create.args[0][0];
expect(savedObjectType).to.eql('config');
const options = savedObjectsClient.create.args[0][2];
expect(options).to.have.property('id', '4.0.1');
});
});

it('should log a message for upgrades', function () {
get.withArgs('pkg.buildNum').returns(5801);
callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve());
savedObjectsClient.create.returns(Promise.resolve({ id: 1, version: 1 }));

const response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1 } } ] } };
const configSavedObjects = [ { id: '4.0.0', attributes: { buildNum: 1 } } ];

return upgrade(response).then(function () {
return upgrade(configSavedObjects).then(function () {
sinon.assert.calledOnce(server.log);
expect(server.log.args[0][0]).to.eql(['plugin', 'elasticsearch']);
const msg = server.log.args[0][1];
Expand All @@ -148,15 +143,14 @@ describe('plugins/elasticsearch', function () {

it('should copy attributes from old config', function () {
get.withArgs('pkg.buildNum').returns(5801);
callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve());
savedObjectsClient.create.returns(Promise.resolve({ id: 1, version: 1 }));

const response = { hits: { hits: [ { _id: '4.0.0', _source: { buildNum: 1, defaultIndex: 'logstash-*' } } ] } };
const configSavedObjects = [ { id: '4.0.0', attributes: { buildNum: 1, defaultIndex: 'logstash-*' } } ];

return upgrade(response).then(function () {
sinon.assert.calledOnce(callWithInternalUser);
const params = callWithInternalUser.args[0][1];
expect(params).to.have.property('body');
expect(params.body).to.have.property('defaultIndex', 'logstash-*');
return upgrade(configSavedObjects).then(function () {
sinon.assert.calledOnce(savedObjectsClient.create);
const attributes = savedObjectsClient.create.args[0][1];
expect(attributes).to.have.property('defaultIndex', 'logstash-*');
});
});
});
Expand Down
12 changes: 6 additions & 6 deletions src/core_plugins/elasticsearch/lib/is_upgradeable.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import semver from 'semver';
const rcVersionRegex = /(\d+\.\d+\.\d+)\-rc(\d+)/i;

export default function (server, doc) {
export default function (server, configSavedObject) {
const config = server.config();
if (/alpha|beta|snapshot/i.test(doc._id)) return false;
if (!doc._id) return false;
if (doc._id === config.get('pkg.version')) return false;
if (/alpha|beta|snapshot/i.test(configSavedObject.id)) return false;
if (!configSavedObject.id) return false;
if (configSavedObject.id === config.get('pkg.version')) return false;

let packageRcRelease = Infinity;
let rcRelease = Infinity;
let packageVersion = config.get('pkg.version');
let version = doc._id;
const matches = doc._id.match(rcVersionRegex);
let version = configSavedObject.id;
const matches = configSavedObject.id.match(rcVersionRegex);
const packageMatches = config.get('pkg.version').match(rcVersionRegex);

if (matches) {
Expand Down
28 changes: 11 additions & 17 deletions src/core_plugins/elasticsearch/lib/migrate_config.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { get } from 'lodash';
import upgrade from './upgrade_config';
import { SavedObjectsClient } from '../../../server/saved_objects';

export default function (server, { mappings }) {
export default async function (server, { mappings }) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since server is just used to create the savedObjectsClient, how about updating these to simply accept the savedObjectsClient and we create it once in the health check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

upgrade is using the server for server.log as well as server.config(), so I can't remove passing the server to these functions; however, I am now passing the savedObjectsClient to the upgrade function so it's only being constructed once.

const config = server.config();
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
const options = {
index: config.get('kibana.index'),

const savedObjectsClient = new SavedObjectsClient(config.get('kibana.index'), mappings, callWithInternalUser);
const { saved_objects: configSavedObjects } = await savedObjectsClient.find({
type: 'config',
body: {
size: 1000,
sort: [
{
buildNum: {
order: 'desc',
unmapped_type: get(mappings, 'config.properties.buildNum.type') || 'keyword'
}
}
]
}
};
page: 1,
perPage: 1000,
sortField: 'buildNum',
sortOrder: 'desc'
});

return callWithInternalUser('search', options).then(upgrade(server));
return await upgrade(server, savedObjectsClient)(configSavedObjects);
}
31 changes: 13 additions & 18 deletions src/core_plugins/elasticsearch/lib/upgrade_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,26 @@ import Promise from 'bluebird';
import isUpgradeable from './is_upgradeable';
import _ from 'lodash';

export default function (server) {
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
export default function (server, savedObjectsClient) {
const config = server.config();

function createNewConfig() {
return callWithInternalUser('create', {
index: config.get('kibana.index'),
type: 'config',
body: { buildNum: config.get('pkg.buildNum') },
return savedObjectsClient.create('config', {
buildNum: config.get('pkg.buildNum')
}, {
id: config.get('pkg.version')
});
}

return function (response) {
return function (configSavedObjects) {
// Check to see if there are any doc. If not then we set the build number and id
if (response.hits.hits.length === 0) {
if (configSavedObjects.length === 0) {
return createNewConfig();
}

// if we already have a the current version in the index then we need to stop
const devConfig = _.find(response.hits.hits, function currentVersion(hit) {
return hit._id !== '@@version' && hit._id === config.get('pkg.version');
const devConfig = _.find(configSavedObjects, function currentVersion(configSavedObject) {
return configSavedObject.id !== '@@version' && configSavedObject.id === config.get('pkg.version');
});

if (devConfig) {
Expand All @@ -32,25 +30,22 @@ export default function (server) {

// Look for upgradeable configs. If none of them are upgradeable
// then create a new one.
const body = _.find(response.hits.hits, isUpgradeable.bind(null, server));
if (!body) {
const configSavedObject = _.find(configSavedObjects, isUpgradeable.bind(null, server));
if (!configSavedObject) {
return createNewConfig();
}

// if the build number is still the template string (which it wil be in development)
// then we need to set it to the max interger. Otherwise we will set it to the build num
body._source.buildNum = config.get('pkg.buildNum');
configSavedObject.attributes.buildNum = config.get('pkg.buildNum');
Copy link
Contributor

Choose a reason for hiding this comment

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

We could use configSavedObject.get('buildNum') here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not seeing any get/set methods on the configSavedObject.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point - that is on the client-side. Sometime in the future I should move the SavedObject to common and use for both client and server-side.


server.log(['plugin', 'elasticsearch'], {
tmpl: 'Upgrade config from <%= prevVersion %> to <%= newVersion %>',
prevVersion: body._id,
prevVersion: configSavedObject.id,
newVersion: config.get('pkg.version')
});

return callWithInternalUser('create', {
index: config.get('kibana.index'),
type: 'config',
body: body._source,
return savedObjectsClient.create('config', configSavedObject.attributes, {
id: config.get('pkg.version')
});
};
Expand Down
Loading