-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support changes that require reindex too
- Loading branch information
1 parent
577a67b
commit 583d4d5
Showing
6 changed files
with
337 additions
and
110 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
src/core_plugins/elasticsearch/lib/kibana_index_changes/change_mistyped_type_field.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { getRootProperties } from '../../../../server/mappings'; | ||
import { KibanaIndexChange } from './kibana_index_change'; | ||
|
||
export class ChangeMistypedTypeField extends KibanaIndexChange { | ||
getNewMappings() { | ||
const properties = getRootProperties(this.currentMappingsDsl); | ||
const typeProperty = properties.type; | ||
if (typeProperty.type !== 'keyword') { | ||
properties.type.type = 'keyword'; | ||
return properties; | ||
} | ||
} | ||
} |
159 changes: 159 additions & 0 deletions
159
src/core_plugins/elasticsearch/lib/kibana_index_changes/kibana_index_change.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
export class KibanaIndexChange { | ||
constructor(options) { | ||
this.log = options.log; | ||
this.indexName = options.indexName; | ||
this.kibanaIndexMappingsDsl = options.kibanaIndexMappingsDsl; | ||
this.currentMappingsDsl = options.currentMappingsDsl; | ||
this.rootEsType = options.rootEsType; | ||
this.callCluster = options.callCluster; | ||
} | ||
|
||
async getNewMappings() { } | ||
|
||
getNewIndex() { | ||
return `${this.indexName}.1`; | ||
} | ||
|
||
getBackupIndex() { | ||
return `${this.indexName}.backup`; | ||
} | ||
|
||
async rollback() { | ||
// Delete index | ||
const indexName = this.getNewIndex(); | ||
|
||
await this.deleteIndex(indexName); | ||
} | ||
|
||
async applyChange() { | ||
const newIndexName = this.getNewIndex(); | ||
const backupIndexName = this.getBackupIndex(); | ||
const newMappings = await this.getNewMappings(); | ||
|
||
// If there are no mapping changes, bail | ||
if (!newMappings) { | ||
return; | ||
} | ||
|
||
// Create the new index | ||
try { | ||
await this.createNewIndex(newIndexName, newMappings); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to create new index ${newIndexName}`, | ||
}); | ||
return; | ||
} | ||
|
||
// Perform a backup for safety! | ||
try { | ||
await this.reindex(this.indexName, backupIndexName); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to reindex to ${backupIndexName}`, | ||
}); | ||
await this.rollback(); | ||
return; | ||
} | ||
|
||
// Reindex into the new index | ||
try { | ||
await this.reindex(this.indexName, newIndexName); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to reindex to ${newIndexName}`, | ||
}); | ||
await this.rollback(); | ||
return; | ||
} | ||
|
||
// Delete existing .kibana index so we can alias into it | ||
try { | ||
await this.deleteIndex(this.indexName); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to delete ${this.indexName}`, | ||
}); | ||
await this.rollback(); | ||
return; | ||
} | ||
|
||
try { | ||
await this.createAlias(newIndexName); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to alias to ${newIndexName}`, | ||
}); | ||
await this.rollback(); | ||
return; | ||
} | ||
} | ||
|
||
async createAlias(newIndexName) { | ||
// log about new properties | ||
this.log(['info', 'elasticsearch'], { | ||
tmpl: `Aliasing ${newIndexName} to ${this.indexName}`, | ||
}); | ||
|
||
// add the new properties to the index mapping | ||
await this.callCluster('indices.putAlias', { | ||
index: newIndexName, | ||
name: this.indexName, | ||
}); | ||
} | ||
|
||
async reindex(source, dest) { | ||
// log about new properties | ||
this.log(['info', 'elasticsearch'], { | ||
tmpl: `Reindexing from ${source} to ${dest}`, | ||
}); | ||
|
||
// add the new properties to the index mapping | ||
await this.callCluster('reindex', { | ||
body: { | ||
source: { | ||
index: source, | ||
}, | ||
dest: { | ||
index: dest, | ||
} | ||
}, | ||
}); | ||
} | ||
|
||
async createNewIndex(indexName, newMappings) { | ||
// log about new properties | ||
this.log(['info', 'elasticsearch'], { | ||
tmpl: `Creating new .kibana index, named ${indexName}`, | ||
}); | ||
|
||
// add the new properties to the index mapping | ||
await this.callCluster('indices.create', { | ||
index: indexName, | ||
body: { | ||
mappings: { | ||
doc: { | ||
properties: newMappings, | ||
} | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
async deleteIndex(indexName) { | ||
// log about new properties | ||
this.log(['info', 'elasticsearch'], { | ||
tmpl: `Deleting index ${indexName}`, | ||
}); | ||
|
||
// add the new properties to the index mapping | ||
await this.callCluster('indices.delete', { | ||
index: indexName | ||
}); | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/core_plugins/elasticsearch/lib/kibana_index_patches/kibana_index_patch.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
export class KibanaIndexPatch { | ||
constructor(options) { | ||
this.log = options.log; | ||
this.indexName = options.indexName; | ||
this.kibanaIndexMappingsDsl = options.kibanaIndexMappingsDsl; | ||
this.currentMappingsDsl = options.currentMappingsDsl; | ||
this.rootEsType = options.rootEsType; | ||
this.callCluster = options.callCluster; | ||
} | ||
|
||
async applyPatch() { | ||
const patchMappings = await this.getUpdatedPatchMappings(); | ||
if (!patchMappings) { | ||
return; | ||
} | ||
|
||
try { | ||
await this.putMappings(patchMappings); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to patch mappings for "<%= cls %>"`, | ||
cls: this.constructor.name, | ||
}); | ||
return; | ||
} | ||
|
||
try { | ||
await this.applyChanges(patchMappings); | ||
} | ||
catch (e) { | ||
this.log(['error', 'elasticsearch'], { | ||
tmpl: `Unable to apply patch changes for "<%= cls %>"`, | ||
cls: this.constructor.name, | ||
}); | ||
} | ||
} | ||
|
||
getUpdatedPatchMappings() {} | ||
applyChanges() {} | ||
|
||
async putMappings(patchMappings) { | ||
// log about new properties | ||
this.log(['info', 'elasticsearch'], { | ||
tmpl: `Adding mappings to kibana index for SavedObject types "<%= names.join('", "') %>"`, | ||
names: Object.keys(patchMappings), | ||
}); | ||
|
||
// add the new properties to the index mapping | ||
await this.callCluster('indices.putMapping', { | ||
index: this.indexName, | ||
type: this.rootEsType, | ||
body: { | ||
properties: patchMappings, | ||
}, | ||
update_all_types: true | ||
}); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/core_plugins/elasticsearch/lib/kibana_index_patches/patch_missing_properties.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { getRootProperties } from '../../../../server/mappings'; | ||
import { KibanaIndexPatch } from './kibana_index_patch'; | ||
|
||
export class PatchMissingProperties extends KibanaIndexPatch { | ||
getUpdatedPatchMappings() { | ||
const expectedProps = getRootProperties(this.kibanaIndexMappingsDsl); | ||
const existingProps = getRootProperties(this.currentMappingsDsl); | ||
|
||
return Object.keys(expectedProps) | ||
.reduce((acc, prop) => { | ||
if (existingProps[prop]) { | ||
return acc; | ||
} else { | ||
return { ...acc || {}, [prop]: expectedProps[prop] }; | ||
} | ||
}, null); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...core_plugins/elasticsearch/lib/kibana_index_patches/patch_missing_title_keyword_fields.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { getRootProperties } from '../../../../server/mappings'; | ||
import { get } from 'lodash'; | ||
import { KibanaIndexPatch } from './kibana_index_patch'; | ||
|
||
const propertiesWithTitles = [ | ||
'index-pattern', | ||
'dashboard', | ||
'visualization', | ||
'search', | ||
]; | ||
|
||
export class PatchMissingTitleKeywordFields extends KibanaIndexPatch { | ||
getUpdatedPatchMappings() { | ||
const properties = getRootProperties(this.currentMappingsDsl); | ||
const mappings = {}; | ||
|
||
for (const property of propertiesWithTitles) { | ||
const hasKeyword = !!get(properties, `${property}.properties.title.fields.keyword`); | ||
if (hasKeyword) { | ||
continue; | ||
} | ||
|
||
const titleMapping = get(properties, `${property}.properties.title`); | ||
mappings[property] = { | ||
properties: { | ||
title: { | ||
...titleMapping, | ||
fields: { | ||
keyword: { | ||
type: 'keyword', | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
// Make sure we return a falsy value | ||
if (!Object.keys(mappings).length) { | ||
return; | ||
} | ||
|
||
return mappings; | ||
} | ||
|
||
async applyChanges(patchMappings) { | ||
const properties = Object.keys(patchMappings); | ||
const types = properties.map(type => ({ match: { type } })); | ||
|
||
this.log(['info', 'elasticsearch'], { | ||
tmpl: `Updating by query for Saved Object types "<%= names.join('", "') %>"`, | ||
names: properties, | ||
}); | ||
|
||
await this.callCluster('updateByQuery', { | ||
conflicts: 'proceed', | ||
index: this.indexName, | ||
type: this.rootEsType, | ||
body: { | ||
query: { | ||
bool: { | ||
should: types, | ||
}, | ||
}, | ||
}, | ||
}); | ||
} | ||
} |
Oops, something went wrong.