Merge pull request #3444 from w33ble/pattern-selection

Pattern selection race conditions
This commit is contained in:
Lukas Olson 2015-03-27 16:57:42 -07:00
commit 0148f8f934

View file

@ -12,11 +12,12 @@ define(function (require) {
});
require('modules').get('apps/settings')
.controller('settingsIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config) {
.controller('settingsIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise) {
var notify = new Notifier();
var MissingIndices = errors.IndexPatternMissingIndices;
var refreshKibanaIndex = Private(require('plugins/settings/sections/indices/_refresh_kibana_index'));
var intervals = indexPatterns.intervals;
var samplePromise;
// this and child scopes will write pattern vars here
var index = $scope.index = {
@ -33,83 +34,8 @@ define(function (require) {
index.nameInterval = _.find(index.nameIntervalOptions, { name: 'daily' });
index.timeField = null;
var updateSamples = function () {
index.samples = null;
index.existing = null;
index.patternErrors = [];
if (!index.nameInterval || !index.name) {
return;
}
var pattern = mockIndexPattern(index);
indexPatterns.mapper.getIndicesForIndexPattern(pattern)
.then(function (existing) {
var all = existing.all;
var matches = existing.matches;
if (all.length) {
index.existing = {
class: 'success',
all: all,
matches: matches,
matchPercent: Math.round((matches.length / all.length) * 100) + '%',
failures: _.difference(all, matches)
};
return;
}
index.patternErrors.push('Pattern does not match any existing indices');
var radius = Math.round(index.sampleCount / 2);
var samples = intervals.toIndexList(index.name, index.nameInterval, -radius, radius);
if (_.uniq(samples).length !== samples.length) {
index.patternErrors.push('Invalid pattern, interval does not create unique index names');
} else {
index.samples = samples;
}
})
.catch(notify.error);
};
$scope.refreshFieldList = function () {
index.dateFields = index.timeField = index.listUsed = null;
var useIndexList = index.isTimeBased && index.nameIsPattern;
// we don't have enough info to continue
if (!index.name) {
index.fetchFieldsError = 'Set an index name first';
return;
}
if (useIndexList && !index.nameInterval) {
index.fetchFieldsError = 'Select the interval at which your indices are populated.';
return;
}
return indexPatterns.mapper.clearCache(index.name)
.then(function () {
var pattern = mockIndexPattern(index);
return indexPatterns.mapper.getFieldsForIndexPattern(pattern, true)
.catch(function (err) {
// TODO: we should probably display a message of some kind
if (err instanceof MissingIndices) {
index.fetchFieldsError = 'Unable to fetch mapping. Do you have indices matching the pattern?';
return [];
}
throw err;
});
})
.then(function (fields) {
if (fields.length > 0) {
index.fetchFieldsError = null;
index.dateFields = fields.filter(function (field) {
return field.type === 'date';
});
}
}, notify.fatal);
fetchFieldList().then(updateFieldList);
};
$scope.createIndexPattern = function () {
@ -156,24 +82,6 @@ define(function (require) {
'index.nameIsPattern',
'index.nameInterval.name'
], function (newVal, oldVal) {
function getPatternDefault(interval) {
switch (interval) {
case 'hours':
return '[logstash-]YYYY.MM.DD.HH';
case 'days':
return '[logstash-]YYYY.MM.DD';
case 'weeks':
return '[logstash-]GGGG.WW';
case 'months':
return '[logstash-]YYYY.MM';
case 'years':
return '[logstash-]YYYY';
default:
return 'logstash-*';
}
}
var isTimeBased = newVal[0];
var nameIsPattern = newVal[1];
var newDefault = getPatternDefault(newVal[2]);
@ -194,17 +102,8 @@ define(function (require) {
index.nameInterval = index.nameInterval || intervals.byName['days'];
index.name = index.name || getPatternDefault(index.nameInterval);
}
});
var mockIndexPattern = function (index) {
// trick the mapper into thinking this is an indexPattern
return {
id: index.name,
intervalName: index.nameInterval
};
};
$scope.moreSamples = function (andUpdate) {
index.sampleCount += 5;
if (andUpdate) updateSamples();
@ -213,13 +112,164 @@ define(function (require) {
$scope.$watchMulti([
'index.name',
'index.nameInterval'
], updateSamples);
], function (newVal, oldVal) {
var lastPromise;
samplePromise = lastPromise = updateSamples()
.then(function () {
promiseMatch(lastPromise, function () {
index.samples = null;
index.patternErrors = [];
});
})
.catch(function (errors) {
promiseMatch(lastPromise, function () {
index.existing = null;
index.patternErrors = errors;
});
})
.finally(function () {
// prevent running when no change happened (ie, first watcher call)
if (!_.isEqual(newVal, oldVal)) {
fetchFieldList().then(function (results) {
if (lastPromise === samplePromise) {
updateFieldList(results);
}
});
}
});
});
$scope.$watchMulti([
'index.name',
'index.isTimeBased',
'index.nameInterval',
'index.sampleCount'
], $scope.refreshFieldList);
function updateSamples() {
var patternErrors = [];
if (!index.nameInterval || !index.name) {
return Promise.resolve();
}
var pattern = mockIndexPattern(index);
return indexPatterns.mapper.getIndicesForIndexPattern(pattern)
.catch(notify.error)
.then(function (existing) {
var all = existing.all;
var matches = existing.matches;
if (all.length) {
index.existing = {
class: 'success',
all: all,
matches: matches,
matchPercent: Math.round((matches.length / all.length) * 100) + '%',
failures: _.difference(all, matches)
};
return;
}
patternErrors.push('Pattern does not match any existing indices');
var radius = Math.round(index.sampleCount / 2);
var samples = intervals.toIndexList(index.name, index.nameInterval, -radius, radius);
if (_.uniq(samples).length !== samples.length) {
patternErrors.push('Invalid pattern, interval does not create unique index names');
} else {
index.samples = samples;
}
throw patternErrors;
});
}
function fetchFieldList() {
index.dateFields = index.timeField = index.listUsed = null;
var useIndexList = index.isTimeBased && index.nameIsPattern;
var fetchFieldsError;
var dateFields;
// we don't have enough info to continue
if (!index.name) {
fetchFieldsError = 'Set an index name first';
return;
}
if (useIndexList && !index.nameInterval) {
fetchFieldsError = 'Select the interval at which your indices are populated.';
return;
}
return indexPatterns.mapper.clearCache(index.name)
.then(function () {
var pattern = mockIndexPattern(index);
return indexPatterns.mapper.getFieldsForIndexPattern(pattern, true)
.catch(function (err) {
// TODO: we should probably display a message of some kind
if (err instanceof MissingIndices) {
fetchFieldsError = 'Unable to fetch mapping. Do you have indices matching the pattern?';
return [];
}
throw err;
});
})
.then(function (fields) {
if (fields.length > 0) {
fetchFieldsError = null;
dateFields = fields.filter(function (field) {
return field.type === 'date';
});
}
return {
fetchFieldsError: fetchFieldsError,
dateFields: dateFields
};
}, notify.fatal);
}
function updateFieldList(results) {
index.fetchFieldsError = results.fetchFieldsError;
index.dateFields = results.dateFields;
}
function promiseMatch(lastPromise, cb) {
if (lastPromise === samplePromise) {
cb();
} else if (samplePromise != null) {
// haven't hit the last promise yet, reset index params
index.patternErrors = [];
index.samples = null;
index.existing = null;
index.fetchFieldsError = 'Loading';
}
}
function getPatternDefault(interval) {
switch (interval) {
case 'hours':
return '[logstash-]YYYY.MM.DD.HH';
case 'days':
return '[logstash-]YYYY.MM.DD';
case 'weeks':
return '[logstash-]GGGG.WW';
case 'months':
return '[logstash-]YYYY.MM';
case 'years':
return '[logstash-]YYYY';
default:
return 'logstash-*';
}
}
function mockIndexPattern(index) {
// trick the mapper into thinking this is an indexPattern
return {
id: index.name,
intervalName: index.nameInterval
};
}
});
});