Merge branch 'master' into issue4366

This commit is contained in:
Stéphane Campinas 2016-06-09 09:29:38 +01:00
commit 984d3349ba
63 changed files with 973 additions and 729 deletions

4
.gitignore vendored
View file

@ -12,7 +12,9 @@ target
.idea
*.iml
*.log
/test/output
/test/screenshots/diff
/test/screenshots/failure
/test/screenshots/session
/esvm
.htpasswd
.eslintcache

View file

@ -69,7 +69,7 @@ module.exports = function (grunt) {
grunt.config.merge(config);
config.userScriptsDir = __dirname + '/build/userScripts';
config.packageScriptsDir = __dirname + '/tasks/build/package_scripts';
// ensure that these run first, other configs need them
config.services = require('./tasks/config/services')(grunt);
config.platforms = require('./tasks/config/platforms')(grunt);

View file

@ -88,5 +88,5 @@
# logging.verbose: false
# Set the interval in milliseconds to sample system and process performance
# metrics. Minimum is 100ms. Defaults to 10000.
# ops.interval: 10000
# metrics. Minimum is 100ms. Defaults to 5000.
# ops.interval: 5000

View file

@ -42,7 +42,7 @@ retrying.
error messages.
`logging.verbose`:: *Default: false* Set the value of this setting to `true` to log all events, including system usage
information and all requests.
`ops.interval`:: *Default: 10000* Set the interval in milliseconds to sample system and process performance metrics.
`ops.interval`:: *Default: 5000* Set the interval in milliseconds to sample system and process performance metrics.
The minimum value is 100.
`status.allowAnonymous`:: *Default: false* If authentication is enabled, setting this to `true` allows
unauthenticated users to access the Kibana server status API and status page.

View file

@ -59,7 +59,8 @@
"makelogs": "makelogs",
"mocha": "mocha",
"mocha:debug": "mocha --debug-brk",
"sterilize": "grunt sterilize"
"sterilize": "grunt sterilize",
"compareScreenshots": "node utilities/compareScreenshots"
},
"repository": {
"type": "git",
@ -171,6 +172,7 @@
"gruntify-eslint": "1.0.1",
"html-entities": "1.1.3",
"husky": "0.8.1",
"image-diff": "1.6.0",
"intern": "3.0.1",
"istanbul-instrumenter-loader": "0.1.3",
"karma": "0.13.9",

View file

@ -41,6 +41,7 @@ describe('plugins/elasticsearch', function () {
upgradeDoc('4.0.0-rc1', '4.0.0', true);
upgradeDoc('4.0.0-rc1-snapshot', '4.0.0', false);
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 () {
let doc = {

View file

@ -91,10 +91,18 @@ describe('plugins/elasticsearch', function () {
});
});
it('should resolve with undefined if the nothing is upgradeable', function () {
const response = { hits: { hits: [ { _id: '4.0.1-beta1' }, { _id: '4.0.0-snapshot1' } ] } };
it('should create new config if the nothing is upgradeable', function () {
get.withArgs('pkg.buildNum').returns(9833);
client.create.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 (resp) {
expect(resp).to.be(undefined);
sinon.assert.calledOnce(client.create);
const params = client.create.args[0][0];
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');
});
});

View file

@ -3,7 +3,7 @@ const rcVersionRegex = /(\d+\.\d+\.\d+)\-rc(\d+)/i;
module.exports = function (server, doc) {
const config = server.config();
if (/beta|snapshot/i.test(doc._id)) return false;
if (/alpha|beta|snapshot/i.test(doc._id)) return false;
if (!doc._id) return false;
if (doc._id === config.get('pkg.version')) return false;

View file

@ -9,17 +9,21 @@ module.exports = function (server) {
const client = server.plugins.elasticsearch.client;
const config = server.config();
function createNewConfig() {
return client.create({
index: config.get('kibana.index'),
type: 'config',
body: { buildNum: config.get('pkg.buildNum') },
id: config.get('pkg.version')
});
}
return function (response) {
const newConfig = {};
// Check to see if there are any doc. If not then we set the build number and id
if (response.hits.hits.length === 0) {
return client.create({
index: config.get('kibana.index'),
type: 'config',
body: { buildNum: config.get('pkg.buildNum') },
id: config.get('pkg.version')
});
return createNewConfig();
}
// if we already have a the current version in the index then we need to stop
@ -30,9 +34,11 @@ module.exports = function (server) {
if (devConfig) return Promise.resolve();
// Look for upgradeable configs. If none of them are upgradeable
// then resolve with null.
// then create a new one.
const body = _.find(response.hits.hits, isUpgradeable.bind(null, server));
if (!body) return Promise.resolve();
if (!body) {
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

View file

@ -0,0 +1,13 @@
import _ from 'lodash';
import $ from 'jquery';
import uiModules from 'ui/modules';
import noResultsTemplate from '../partials/no_results.html';
uiModules
.get('apps/discover')
.directive('discoverNoResults', function () {
return {
restrict: 'E',
template: noResultsTemplate
};
});

View file

@ -56,58 +56,7 @@
<div class="discover-wrapper col-md-10">
<div class="discover-content">
<!-- no results -->
<div ng-show="resultState === 'none'">
<div class="col-md-10 col-md-offset-1">
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
<p>
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
</p>
<div class="shard-failures" ng-show="failures">
<h3>Shard Failures</h3>
<p>The following shard failures ocurred:</p>
<ul>
<li ng-repeat="failure in failures | limitTo: failuresShown"><strong>Index:</strong> {{failure.index}} <strong>Shard:</strong> {{failure.shard}} <strong>Reason:</strong> {{failure.reason}} </li>
</ul>
<a ng-click="showAllFailures()" ng-if="failures.length > failuresShown" title="Show More">Show More</a>
<a ng-click="showLessFailures()" ng-if="failures.length === failuresShown && failures.length > 5" title="Show Less">Show Less</a>
</div>
<div ng-show="opts.timefield">
<p>
<h3>Expand your time range</h3>
<p>I see you are looking at an index with a date field. It is possible your query does not match anything in the current time range, or that there is no data at all in the currently selected time range. Try selecting a wider time range by opening the time picker <i aria-hidden="true" class="fa fa-clock-o"></i> in the top right corner of your screen.
</p>
</div>
<h3>Refine your query</h3>
<p>
The search bar at the top uses Elasticsearch's support for Lucene <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax" target="_blank">Query String syntax</a>. Let's say we're searching web server logs that have been parsed into a few fields.
</p>
<p>
<h4>Examples:</h4>
Find requests that contain the number 200, in any field:
<pre>200</pre>
Or we can search in a specific field. Find 200 in the status field:
<pre>status:200</pre>
Find all status codes between 400-499:
<pre>status:[400 TO 499]</pre>
Find status codes 400-499 with the extension php:
<pre>status:[400 TO 499] AND extension:PHP</pre>
Or HTML
<pre>status:[400 TO 499] AND (extension:php OR extension:html)</pre>
</p>
</div>
</div>
<discover-no-results ng-show="resultState === 'none'"></discover-no-results>
<!-- loading -->
<div ng-show="resultState === 'loading'">

View file

@ -1,4 +1,5 @@
import 'plugins/kibana/discover/saved_searches/saved_searches';
import 'plugins/kibana/discover/directives/no_results';
import 'plugins/kibana/discover/directives/timechart';
import 'ui/navbar_extensions';
import 'ui/collapsible_sidebar';

View file

@ -0,0 +1,51 @@
<div>
<div class="col-md-10 col-md-offset-1" data-test-subj="discoverNoResults">
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
<p>
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
</p>
<div class="shard-failures" ng-show="failures">
<h3>Shard Failures</h3>
<p>The following shard failures ocurred:</p>
<ul>
<li ng-repeat="failure in failures | limitTo: failuresShown"><strong>Index:</strong> {{failure.index}} <strong>Shard:</strong> {{failure.shard}} <strong>Reason:</strong> {{failure.reason}} </li>
</ul>
<a ng-click="showAllFailures()" ng-if="failures.length > failuresShown" title="Show More">Show More</a>
<a ng-click="showLessFailures()" ng-if="failures.length === failuresShown && failures.length > 5" title="Show Less">Show Less</a>
</div>
<div ng-show="opts.timefield">
<p>
<h3>Expand your time range</h3>
<p>I see you are looking at an index with a date field. It is possible your query does not match anything in the current time range, or that there is no data at all in the currently selected time range. Click the button below to open the time picker. For future reference you can open the time picker by clicking on the <a class="btn btn-xs navbtn" ng-click="kbnTopNav.toggle('filter')" aria-expanded="kbnTopNav.is('filter')" aria-label="time picker" data-test-subj="discoverNoResultsTimefilter"><i aria-hidden="true" class="fa fa-clock-o"></i> time picker</a> button in the top right corner of your screen.
</p>
</div>
<h3>Refine your query</h3>
<p>
The search bar at the top uses Elasticsearch's support for Lucene <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax" target="_blank">Query String syntax</a>. Let's say we're searching web server logs that have been parsed into a few fields.
</p>
<p>
<h4>Examples:</h4>
Find requests that contain the number 200, in any field:
<pre>200</pre>
Or we can search in a specific field. Find 200 in the status field:
<pre>status:200</pre>
Find all status codes between 400-499:
<pre>status:[400 TO 499]</pre>
Find status codes 400-499 with the extension php:
<pre>status:[400 TO 499] AND extension:PHP</pre>
Or HTML
<pre>status:[400 TO 499] AND (extension:php OR extension:html)</pre>
</p>
</div>
</div>

View file

@ -86,7 +86,7 @@ module.exports = () => Joi.object({
.default(),
ops: Joi.object({
interval: Joi.number().default(10000),
interval: Joi.number().default(5000),
}),
plugins: Joi.object({

View file

@ -23,7 +23,7 @@ export default function (Private) {
this.getSourceFiltering = sinon.spy();
this.metaFields = ['_id', '_type', '_source'];
this.fieldFormatMap = {};
this.routes = IndexPattern.prototype.routes;
this.routes = IndexPattern.routes;
this.toIndexList = _.constant(Promise.resolve([pattern]));
this.toDetailedIndexList = _.constant(Promise.resolve([

View file

@ -12,6 +12,7 @@ import IndexPatternsMapperProvider from 'ui/index_patterns/_mapper';
import UtilsMappingSetupProvider from 'ui/utils/mapping_setup';
import IndexPatternsIntervalsProvider from 'ui/index_patterns/_intervals';
import IndexPatternsIndexPatternProvider from 'ui/index_patterns/_index_pattern';
describe('index pattern', function () {
let IndexPattern;
let mapper;
@ -77,7 +78,7 @@ describe('index pattern', function () {
// helper function to create index patterns
function create(id, payload) {
let indexPattern = new IndexPattern(id);
const indexPattern = new IndexPattern(id);
DocSource.prototype.doIndex.returns(Promise.resolve(id));
payload = _.defaults(payload || {}, docSourceResponse(id));
setDocsourcePayload(payload);
@ -129,16 +130,16 @@ describe('index pattern', function () {
describe('getScriptedFields', function () {
it('should return all scripted fields', function () {
let scriptedNames = _(mockLogstashFields).where({ scripted: true }).pluck('name').value();
let respNames = _.pluck(indexPattern.getScriptedFields(), 'name');
const scriptedNames = _(mockLogstashFields).where({ scripted: true }).pluck('name').value();
const respNames = _.pluck(indexPattern.getScriptedFields(), 'name');
expect(respNames).to.eql(scriptedNames);
});
});
describe('getNonScriptedFields', function () {
it('should return all non-scripted fields', function () {
let notScriptedNames = _(mockLogstashFields).where({ scripted: false }).pluck('name').value();
let respNames = _.pluck(indexPattern.getNonScriptedFields(), 'name');
const notScriptedNames = _(mockLogstashFields).where({ scripted: false }).pluck('name').value();
const respNames = _.pluck(indexPattern.getNonScriptedFields(), 'name');
expect(respNames).to.eql(notScriptedNames);
});
@ -147,7 +148,7 @@ describe('index pattern', function () {
describe('refresh fields', function () {
// override the default indexPattern, with a truncated field list
require('test_utils/no_digest_promises').activateForSuite();
let indexPatternId = 'test-pattern';
const indexPatternId = 'test-pattern';
let indexPattern;
let fieldLength;
let truncatedFields;
@ -181,8 +182,8 @@ describe('index pattern', function () {
indexPattern.refreshFields(),
])
.then(function (data) {
let expected = data[0]; // just the fields in the index
let fields = indexPattern.getNonScriptedFields(); // get all but scripted fields
const expected = data[0]; // just the fields in the index
const fields = indexPattern.getNonScriptedFields(); // get all but scripted fields
expect(_.pluck(fields, 'name')).to.eql(_.pluck(expected, 'name'));
});
@ -193,15 +194,15 @@ describe('index pattern', function () {
setDocsourcePayload(docSourceResponse(indexPatternId));
// add spy to indexPattern.getScriptedFields
let scriptedFieldsSpy = sinon.spy(indexPattern, 'getScriptedFields');
const scriptedFieldsSpy = sinon.spy(indexPattern, 'getScriptedFields');
// refresh fields, which will fetch
return indexPattern.refreshFields().then(function () {
// called to append scripted fields to the response from mapper.getFieldsForIndexPattern
expect(scriptedFieldsSpy.callCount).to.equal(1);
let scripted = _.where(mockLogstashFields, { scripted: true });
let expected = _.filter(indexPattern.fields, { scripted: true });
const scripted = _.where(mockLogstashFields, { scripted: true });
const expected = _.filter(indexPattern.fields, { scripted: true });
expect(_.pluck(expected, 'name')).to.eql(_.pluck(scripted, 'name'));
});
});
@ -210,29 +211,27 @@ describe('index pattern', function () {
describe('add and remove scripted fields', function () {
it('should append the scripted field', function () {
// keep a copy of the current scripted field count
let saveSpy = sinon.spy(indexPattern, 'save');
let oldCount = indexPattern.getScriptedFields().length;
const saveSpy = sinon.spy(indexPattern, 'save');
const oldCount = indexPattern.getScriptedFields().length;
// add a new scripted field
let scriptedField = {
const scriptedField = {
name: 'new scripted field',
script: 'false',
type: 'boolean'
};
indexPattern.addScriptedField(scriptedField.name, scriptedField.script, scriptedField.type);
indexPattern._indexFields(); // normally triggered by docSource.onUpdate()
let scriptedFields = indexPattern.getScriptedFields();
const scriptedFields = indexPattern.getScriptedFields();
expect(saveSpy.callCount).to.equal(1);
expect(scriptedFields).to.have.length(oldCount + 1);
expect(indexPattern.fields.byName[scriptedField.name].displayName).to.equal(scriptedField.name);
expect(indexPattern.fields.byName[scriptedField.name].name).to.equal(scriptedField.name);
});
it('should remove scripted field, by name', function () {
let saveSpy = sinon.spy(indexPattern, 'save');
let scriptedFields = indexPattern.getScriptedFields();
let oldCount = scriptedFields.length;
let scriptedField = _.last(scriptedFields);
const saveSpy = sinon.spy(indexPattern, 'save');
const scriptedFields = indexPattern.getScriptedFields();
const oldCount = scriptedFields.length;
const scriptedField = _.last(scriptedFields);
indexPattern.removeScriptedField(scriptedField.name);
@ -242,8 +241,8 @@ describe('index pattern', function () {
});
it('should not allow duplicate names', function () {
let scriptedFields = indexPattern.getScriptedFields();
let scriptedField = _.last(scriptedFields);
const scriptedFields = indexPattern.getScriptedFields();
const scriptedField = _.last(scriptedFields);
expect(function () {
indexPattern.addScriptedField(scriptedField.name, '\'new script\'', 'string');
}).to.throwError(function (e) {
@ -254,9 +253,9 @@ describe('index pattern', function () {
describe('popularizeField', function () {
it('should increment the poplarity count by default', function () {
let saveSpy = sinon.stub(indexPattern, 'save');
const saveSpy = sinon.stub(indexPattern, 'save');
indexPattern.fields.forEach(function (field, i) {
let oldCount = field.count;
const oldCount = field.count;
indexPattern.popularizeField(field.name);
@ -266,10 +265,10 @@ describe('index pattern', function () {
});
it('should increment the poplarity count', function () {
let saveSpy = sinon.stub(indexPattern, 'save');
const saveSpy = sinon.stub(indexPattern, 'save');
indexPattern.fields.forEach(function (field, i) {
let oldCount = field.count;
let incrementAmount = 4;
const oldCount = field.count;
const incrementAmount = 4;
indexPattern.popularizeField(field.name, incrementAmount);
@ -280,9 +279,9 @@ describe('index pattern', function () {
it('should decrement the poplarity count', function () {
indexPattern.fields.forEach(function (field, i) {
let oldCount = field.count;
let incrementAmount = 4;
let decrementAmount = -2;
const oldCount = field.count;
const incrementAmount = 4;
const decrementAmount = -2;
indexPattern.popularizeField(field.name, incrementAmount);
indexPattern.popularizeField(field.name, decrementAmount);
@ -293,7 +292,7 @@ describe('index pattern', function () {
it('should not go below 0', function () {
indexPattern.fields.forEach(function (field) {
let decrementAmount = -Number.MAX_VALUE;
const decrementAmount = -Number.MAX_VALUE;
indexPattern.popularizeField(field.name, decrementAmount);
expect(field.count).to.equal(0);
});
@ -311,11 +310,11 @@ describe('index pattern', function () {
it('invokes interval toDetailedIndexList with given start/stop times', async function () {
await indexPattern.toDetailedIndexList(1, 2);
let id = indexPattern.id;
const id = indexPattern.id;
expect(intervals.toIndexList.calledWith(id, interval, 1, 2)).to.be(true);
});
it('is fulfilled by the result of interval toDetailedIndexList', async function () {
let indexList = await indexPattern.toDetailedIndexList();
const indexList = await indexPattern.toDetailedIndexList();
expect(indexList[0].index).to.equal('foo');
expect(indexList[1].index).to.equal('bar');
});
@ -340,13 +339,13 @@ describe('index pattern', function () {
it('invokes calculateIndices with given start/stop times and sortOrder', async function () {
await indexPattern.toDetailedIndexList(1, 2, 'sortOrder');
let id = indexPattern.id;
let field = indexPattern.timeFieldName;
const id = indexPattern.id;
const field = indexPattern.timeFieldName;
expect(calculateIndices.calledWith(id, field, 1, 2, 'sortOrder')).to.be(true);
});
it('is fulfilled by the result of calculateIndices', async function () {
let indexList = await indexPattern.toDetailedIndexList();
const indexList = await indexPattern.toDetailedIndexList();
expect(indexList[0].index).to.equal('foo');
expect(indexList[1].index).to.equal('bar');
});
@ -361,7 +360,7 @@ describe('index pattern', function () {
});
it('is fulfilled by id', async function () {
let indexList = await indexPattern.toDetailedIndexList();
const indexList = await indexPattern.toDetailedIndexList();
expect(indexList.index).to.equal(indexPattern.id);
});
});
@ -372,7 +371,7 @@ describe('index pattern', function () {
});
it('is fulfilled by id', async function () {
let indexList = await indexPattern.toDetailedIndexList();
const indexList = await indexPattern.toDetailedIndexList();
expect(indexList.index).to.equal(indexPattern.id);
});
});
@ -390,11 +389,11 @@ describe('index pattern', function () {
it('invokes interval toIndexList with given start/stop times', async function () {
await indexPattern.toIndexList(1, 2);
let id = indexPattern.id;
const id = indexPattern.id;
expect(intervals.toIndexList.calledWith(id, interval, 1, 2)).to.be(true);
});
it('is fulfilled by the result of interval toIndexList', async function () {
let indexList = await indexPattern.toIndexList();
const indexList = await indexPattern.toIndexList();
expect(indexList[0]).to.equal('foo');
expect(indexList[1]).to.equal('bar');
});
@ -420,13 +419,13 @@ describe('index pattern', function () {
it('invokes calculateIndices with given start/stop times and sortOrder', async function () {
await indexPattern.toIndexList(1, 2, 'sortOrder');
let id = indexPattern.id;
let field = indexPattern.timeFieldName;
const id = indexPattern.id;
const field = indexPattern.timeFieldName;
expect(calculateIndices.calledWith(id, field, 1, 2, 'sortOrder')).to.be(true);
});
it('is fulfilled by the result of calculateIndices', async function () {
let indexList = await indexPattern.toIndexList();
const indexList = await indexPattern.toIndexList();
expect(indexList[0]).to.equal('foo');
expect(indexList[1]).to.equal('bar');
});
@ -442,7 +441,7 @@ describe('index pattern', function () {
});
it('is fulfilled by id', async function () {
let indexList = await indexPattern.toIndexList();
const indexList = await indexPattern.toIndexList();
expect(indexList).to.equal(indexPattern.id);
});
});

View file

@ -13,24 +13,30 @@ import IndexPatternsFieldListProvider from 'ui/index_patterns/_field_list';
import IndexPatternsFlattenHitProvider from 'ui/index_patterns/_flatten_hit';
import IndexPatternsCalculateIndicesProvider from 'ui/index_patterns/_calculate_indices';
import IndexPatternsPatternCacheProvider from 'ui/index_patterns/_pattern_cache';
export default function IndexPatternFactory(Private, timefilter, Notifier, config, kbnIndex, Promise, safeConfirm) {
let fieldformats = Private(RegistryFieldFormatsProvider);
let getIds = Private(IndexPatternsGetIdsProvider);
let mapper = Private(IndexPatternsMapperProvider);
let intervals = Private(IndexPatternsIntervalsProvider);
let DocSource = Private(DocSourceProvider);
let mappingSetup = Private(UtilsMappingSetupProvider);
let FieldList = Private(IndexPatternsFieldListProvider);
let flattenHit = Private(IndexPatternsFlattenHitProvider);
let calculateIndices = Private(IndexPatternsCalculateIndicesProvider);
let patternCache = Private(IndexPatternsPatternCacheProvider);
export default function IndexPatternFactory(Private, Notifier, config, kbnIndex, Promise, safeConfirm) {
const fieldformats = Private(RegistryFieldFormatsProvider);
const getIds = Private(IndexPatternsGetIdsProvider);
const mapper = Private(IndexPatternsMapperProvider);
const intervals = Private(IndexPatternsIntervalsProvider);
const DocSource = Private(DocSourceProvider);
const mappingSetup = Private(UtilsMappingSetupProvider);
const FieldList = Private(IndexPatternsFieldListProvider);
const flattenHit = Private(IndexPatternsFlattenHitProvider);
const calculateIndices = Private(IndexPatternsCalculateIndicesProvider);
const patternCache = Private(IndexPatternsPatternCacheProvider);
const type = 'index-pattern';
const notify = new Notifier();
const configWatchers = new WeakMap();
const docSources = new WeakMap();
const getRoutes = () => ({
edit: '/settings/indices/{{id}}',
addField: '/settings/indices/{{id}}/create-field',
indexedFields: '/settings/indices/{{id}}?_a=(tab:indexedFields)',
scriptedFields: '/settings/indices/{{id}}?_a=(tab:scriptedFields)'
});
let type = 'index-pattern';
let notify = new Notifier();
let mapping = mappingSetup.expandShorthand({
const mapping = mappingSetup.expandShorthand({
title: 'string',
timeFieldName: 'string',
notExpandable: 'boolean',
@ -39,121 +45,168 @@ export default function IndexPatternFactory(Private, timefilter, Notifier, confi
fieldFilters: 'json',
fieldFormatMap: {
type: 'string',
_serialize: function (map) {
if (map == null) return;
let count = 0;
let serialized = _.transform(map, function (flat, format, field) {
if (!format) return;
count++;
flat[field] = format;
});
if (count) return angular.toJson(serialized);
_serialize(map = {}) {
const serialized = _.transform(map, serialize);
return _.isEmpty(serialized) ? undefined : angular.toJson(serialized);
},
_deserialize: function (map) {
if (map == null) return {};
return _.mapValues(angular.fromJson(map), function (mapping) {
let FieldFormat = fieldformats.byId[mapping.id];
return FieldFormat && new FieldFormat(mapping.params);
});
_deserialize(map = '{}') {
return _.mapValues(angular.fromJson(map), deserialize);
}
}
});
function IndexPattern(id) {
let self = this;
function serialize(flat, format, field) {
if (format) {
flat[field] = format;
}
}
setId(id);
if (!self.fieldFilters) {
self.fieldFilters = [];
function deserialize(mapping) {
const FieldFormat = fieldformats.byId[mapping.id];
return FieldFormat && new FieldFormat(mapping.params);
}
function updateFromElasticSearch(indexPattern, response) {
if (!response.found) {
throw new errors.SavedObjectNotFound(type, indexPattern.id);
}
let docSource = new DocSource();
_.forOwn(mapping, (fieldMapping, name) => {
if (!fieldMapping._deserialize) {
return;
}
response._source[name] = fieldMapping._deserialize(
response._source[name], response, name, fieldMapping
);
});
self.init = function () {
// tell the docSource where to find the doc
docSource
// give index pattern all of the values in _source
_.assign(indexPattern, response._source);
indexFields(indexPattern);
// any time index pattern in ES is updated, update index pattern object
docSources
.get(indexPattern)
.onUpdate()
.then(response => updateFromElasticSearch(indexPattern, response), notify.fatal);
}
function indexFields(indexPattern) {
if (!indexPattern.id) {
return;
}
if (!indexPattern.fields) {
return indexPattern.refreshFields();
}
initFields(indexPattern);
}
function setId(indexPattern, id) {
indexPattern.id = id;
return id;
}
function watch(indexPattern) {
if (configWatchers.has(indexPattern)) {
return;
}
const unwatch = config.watchAll(() => {
if (indexPattern.fields) {
initFields(indexPattern); // re-init fields when config changes, but only if we already had fields
}
});
configWatchers.set(indexPattern, { unwatch });
}
function unwatch(indexPattern) {
if (!configWatchers.has(indexPattern)) {
return;
}
configWatchers.get(indexPattern).unwatch();
configWatchers.delete(indexPattern);
}
function initFields(indexPattern, input) {
const oldValue = indexPattern.fields;
const newValue = input || oldValue || [];
indexPattern.fields = new FieldList(indexPattern, newValue);
}
function fetchFields(indexPattern) {
return mapper
.getFieldsForIndexPattern(indexPattern, true)
.then(fields => {
const scripted = indexPattern.getScriptedFields();
const all = fields.concat(scripted);
initFields(indexPattern, all);
});
}
class IndexPattern {
constructor(id) {
setId(this, id);
if (!this.fieldFilters) {
this.fieldFilters = [];
}
docSources.set(this, new DocSource());
this.metaFields = config.get('metaFields');
this.getComputedFields = getComputedFields.bind(this);
this.flattenHit = flattenHit(this);
this.formatHit = formatHit(this, fieldformats.getDefaultInstance('string'));
this.formatField = this.formatHit.formatField;
}
get routes() {
return getRoutes();
}
init() {
docSources
.get(this)
.index(kbnIndex)
.type(type)
.id(self.id);
.id(this.id);
// listen for config changes and update field list
config.watchAll(() => {
if (self.fields) {
// re-init fields when config changes, but only if we already had fields
initFields();
watch(this);
return mappingSetup
.isDefined(type)
.then(defined => {
if (defined) {
return true;
}
});
return mappingSetup.isDefined(type)
.then(function (defined) {
// create mapping for this type if one does not exist
if (defined) return true;
return mappingSetup.setup(type, mapping);
})
.then(function () {
// If there is no id, then there is no document to fetch from elasticsearch
if (!self.id) return;
// fetch the object from ES
return docSource.fetch()
.then(function applyESResp(resp) {
if (!resp.found) throw new errors.SavedObjectNotFound(type, self.id);
// deserialize any json fields
_.forOwn(mapping, function ittr(fieldMapping, name) {
if (fieldMapping._deserialize) {
resp._source[name] = fieldMapping._deserialize(resp._source[name], resp, name, fieldMapping);
}
});
// Give obj all of the values in _source
_.assign(self, resp._source);
self._indexFields();
// Any time obj is updated, re-call applyESResp
docSource.onUpdate().then(applyESResp, notify.fatal);
});
.then(() => {
if (!this.id) {
return; // no id === no elasticsearch document
}
return docSources.get(this)
.fetch()
.then(response => updateFromElasticSearch(this, response));
})
.then(function () {
// return our obj as the result of init()
return self;
});
};
function initFields(fields) {
self.fields = new FieldList(self, fields || self.fields || []);
.then(() => this);
}
self._indexFields = function () {
if (self.id) {
if (!self.fields) {
return self.refreshFields();
} else {
initFields();
}
}
};
// Get the source filtering configuration for that index.
self.getSourceFiltering = function () {
getSourceFiltering() {
return {
exclude: self.fieldFilters.map(filter => filter.value)
};
};
}
self.addScriptedField = function (name, script, type, lang) {
type = type || 'string';
addScriptedField(name, script, type = 'string', lang) {
const scriptedFields = this.getScriptedFields();
const names = _.pluck(scriptedFields, 'name');
let scriptFields = _.pluck(self.getScriptedFields(), 'name');
if (_.contains(scriptFields, name)) {
if (_.contains(names, name)) {
throw new errors.DuplicateField(name);
}
self.fields.push({
this.fields.push({
name: name,
script: script,
type: type,
@ -161,186 +214,164 @@ export default function IndexPatternFactory(Private, timefilter, Notifier, confi
lang: lang
});
self.save();
};
this.save();
}
self.removeScriptedField = function (name) {
let fieldIndex = _.findIndex(self.fields, {
removeScriptedField(name) {
const fieldIndex = _.findIndex(this.fields, {
name: name,
scripted: true
});
this.fields.splice(fieldIndex, 1);
this.save();
}
self.fields.splice(fieldIndex, 1);
self.save();
};
self.popularizeField = function (fieldName, unit) {
if (unit == null) unit = 1;
let field = _.get(self, ['fields', 'byName', fieldName]);
if (!field) return;
let count = Math.max((field.count || 0) + unit, 0);
if (field.count !== count) {
field.count = count;
self.save();
popularizeField(fieldName, unit = 1) {
const field = _.get(this, ['fields', 'byName', fieldName]);
if (!field) {
return;
}
};
const count = Math.max((field.count || 0) + unit, 0);
if (field.count === count) {
return;
}
field.count = count;
this.save();
}
self.getNonScriptedFields = function () {
return _.where(self.fields, { scripted: false });
};
getNonScriptedFields() {
return _.where(this.fields, { scripted: false });
}
self.getScriptedFields = function () {
return _.where(self.fields, { scripted: true });
};
getScriptedFields() {
return _.where(this.fields, { scripted: true });
}
self.getInterval = function () {
getInterval() {
return this.intervalName && _.find(intervals, { name: this.intervalName });
};
}
self.toIndexList = function (start, stop, sortDirection) {
return self
.toDetailedIndexList(start, stop, sortDirection)
.then(function (detailedIndices) {
if (!_.isArray(detailedIndices)) {
return detailedIndices.index;
toIndexList(start, stop, sortDirection) {
return this
.toDetailedIndexList(start, stop, sortDirection)
.then(detailedIndices => {
if (!_.isArray(detailedIndices)) {
return detailedIndices.index;
}
return _.pluck(detailedIndices, 'index');
});
}
toDetailedIndexList(start, stop, sortDirection) {
return Promise.resolve().then(() => {
const interval = this.getInterval();
if (interval) {
return intervals.toIndexList(
this.id, interval, start, stop, sortDirection
);
}
return _.pluck(detailedIndices, 'index');
if (this.isWildcard() && this.hasTimeField() && this.canExpandIndices()) {
return calculateIndices(
this.id, this.timeFieldName, start, stop, sortDirection
);
}
return {
index: this.id,
min: -Infinity,
max: Infinity
};
});
};
}
self.toDetailedIndexList = Promise.method(function (start, stop, sortDirection) {
let interval = self.getInterval();
if (interval) {
return intervals.toIndexList(self.id, interval, start, stop, sortDirection);
}
if (self.isWildcard() && self.hasTimeField() && self.canExpandIndices()) {
return calculateIndices(self.id, self.timeFieldName, start, stop, sortDirection);
}
return {
index: self.id,
min: -Infinity,
max: Infinity,
};
});
self.canExpandIndices = function () {
canExpandIndices() {
return !this.notExpandable;
};
}
self.hasTimeField = function () {
hasTimeField() {
return !!(this.timeFieldName && this.fields.byName[this.timeFieldName]);
};
}
self.isWildcard = function () {
isWildcard() {
return _.includes(this.id, '*');
};
}
self.prepBody = function () {
let body = {};
prepBody() {
const body = {};
// serialize json fields
_.forOwn(mapping, function (fieldMapping, fieldName) {
if (self[fieldName] != null) {
_.forOwn(mapping, (fieldMapping, fieldName) => {
if (this[fieldName] != null) {
body[fieldName] = (fieldMapping._serialize)
? fieldMapping._serialize(self[fieldName])
: self[fieldName];
? fieldMapping._serialize(this[fieldName])
: this[fieldName];
}
});
// ensure that the docSource has the current self.id
docSource.id(self.id);
// ensure that the docSource has the current this.id
docSources.get(this).id(this.id);
// clear the indexPattern list cache
getIds.clearCache();
return body;
};
function setId(id) {
return self.id = id;
}
self.create = function () {
let body = self.prepBody();
return docSource.doCreate(body)
.then(setId)
.catch(function (err) {
if (_.get(err, 'origError.status') === 409) {
let confirmMessage = 'Are you sure you want to overwrite this?';
return safeConfirm(confirmMessage).then(
function () {
return Promise.try(function () {
const cached = patternCache.get(self.id);
if (cached) {
return cached.then(pattern => pattern.destroy());
}
})
.then(() => docSource.doIndex(body))
.then(setId);
},
_.constant(false) // if the user doesn't overwrite, resolve with false
);
create() {
const body = this.prepBody();
return docSources.get(this)
.doCreate(body)
.then(id => setId(this, id))
.catch(err => {
if (_.get(err, 'origError.status') !== 409) {
return Promise.resolve(false);
}
return Promise.resolve(false);
const confirmMessage = 'Are you sure you want to overwrite this?';
return safeConfirm(confirmMessage)
.then(() => Promise
.try(() => {
const cached = patternCache.get(this.id);
if (cached) {
return cached.then(pattern => pattern.destroy());
}
})
.then(() => docSources.get(this).doIndex(body))
.then(id => setId(this, id)),
_.constant(false) // if the user doesn't overwrite, resolve with false
);
});
};
}
self.save = function () {
let body = self.prepBody();
return docSource.doIndex(body).then(setId);
};
save() {
const body = this.prepBody();
return docSources.get(this)
.doIndex(body)
.then(id => setId(this, id));
}
self.refreshFields = function () {
return mapper.clearCache(self)
.then(self._fetchFields)
.then(self.save);
};
refreshFields() {
return mapper
.clearCache(this)
.then(() => fetchFields(this))
.then(() => this.save());
}
self._fetchFields = function () {
return mapper.getFieldsForIndexPattern(self, true)
.then(function (fields) {
// append existing scripted fields
fields = fields.concat(self.getScriptedFields());
toJSON() {
return this.id;
}
// initialize self.field with this field list
initFields(fields);
});
};
toString() {
return '' + this.toJSON();
}
self.toJSON = function () {
return self.id;
};
self.toString = function () {
return '' + self.toJSON();
};
self.destroy = function () {
patternCache.clear(self.id);
docSource.destroy();
};
self.metaFields = config.get('metaFields');
self.getComputedFields = getComputedFields.bind(self);
self.flattenHit = flattenHit(self);
self.formatHit = formatHit(self, fieldformats.getDefaultInstance('string'));
self.formatField = self.formatHit.formatField;
destroy() {
unwatch(this);
patternCache.clear(this.id);
docSources.get(this).destroy();
docSources.delete(this);
}
}
IndexPattern.prototype.routes = {
edit: '/settings/indices/{{id}}',
addField: '/settings/indices/{{id}}/create-field',
indexedFields: '/settings/indices/{{id}}?_a=(tab:indexedFields)',
scriptedFields: '/settings/indices/{{id}}?_a=(tab:scriptedFields)'
};
return IndexPattern;
};

View file

@ -29,7 +29,7 @@ paginate {
a {
text-decoration: none;
background-color: @white;
background-color: @kibanaGray6;
margin-left: 2px;
padding: 8px 11px;
}
@ -42,6 +42,7 @@ paginate {
text-decoration: none !important;
font-weight: bold;
color: @paginate-page-link-active-color;
cursor: default;
}
}
}

View file

@ -37,6 +37,10 @@
margin: 0 10px;
border-bottom: 2px solid transparent;
}
// singular tabs are treated as titles
> li:only-child > a {
color: @kibanaGray1;
}
// Active, hover state for the getTabs
> .active > a,
> .active > a:hover,

View file

@ -1,55 +1,74 @@
module.exports = function (grunt) {
const { resolve } = require('path');
const { indexBy } = require('lodash');
import { resolve } from 'path';
import { indexBy } from 'lodash';
import exec from '../utils/exec';
const { config } = grunt;
const exec = require('../utils/exec');
const targetDir = config.get('target');
const version = config.get('pkg.version');
const userScriptsDir = config.get('userScriptsDir');
const servicesByName = indexBy(config.get('services'), 'name');
export default (grunt) => {
const targetDir = grunt.config.get('target');
const packageScriptsDir = grunt.config.get('packageScriptsDir');
const servicesByName = indexBy(grunt.config.get('services'), 'name');
const config = grunt.config.get('packages');
const fpm = args => exec('fpm', args);
grunt.registerTask('_build:osPackages', function () {
grunt.config.get('platforms').forEach(({ name, buildDir }) => {
// TODO(sissel): Check if `fpm` is available
if (!(/linux-x(86|64)$/.test(name))) return;
const arch = /x64$/.test(name) ? 'x86_64' : 'i386';
const fpm = args => exec('fpm', args);
const args = [
grunt.config.get('platforms')
.filter(({ name }) => /linux-x(86|64)$/.test(name))
.map(({ name, buildDir }) => {
const architecture = /x64$/.test(name) ? 'x86_64' : 'i386';
return {
buildDir,
architecture
};
})
.forEach(({ buildDir, architecture }) => {
const baseOptions = [
'--force',
'--package', targetDir,
'-s', 'dir', // input type
'--name', 'kibana',
'--description', 'Explore\ and\ visualize\ your\ Elasticsearch\ data.',
'--version', version,
'--url', 'https://www.elastic.co',
'--vendor', 'Elasticsearch,\ Inc.',
'--maintainer', 'Kibana Team\ \<info@elastic.co\>',
'--license', 'Apache\ 2.0',
'--after-install', resolve(userScriptsDir, 'installer.sh'),
'--after-remove', resolve(userScriptsDir, 'remover.sh'),
'--config-files', '/opt/kibana/config/kibana.yml'
'--architecture', architecture,
'--name', config.name,
'--description', config.description,
'--version', config.version,
'--url', config.site,
'--vendor', config.vendor,
'--maintainer', config.maintainer,
'--license', config.license,
'--after-install', resolve(packageScriptsDir, 'post_install.sh'),
'--before-install', resolve(packageScriptsDir, 'pre_install.sh'),
'--before-remove', resolve(packageScriptsDir, 'pre_remove.sh'),
'--after-remove', resolve(packageScriptsDir, 'post_remove.sh'),
'--config-files', config.path.kibanaConfig,
'--template-value', `user=${config.user}`,
'--template-value', `group=${config.group}`,
'--template-value', `optimizeDir=${config.path.home}/optimize`,
//config folder is moved to path.conf, exclude {path.home}/config
//uses relative path to --prefix, strip the leading /
'--exclude', `${config.path.home.slice(1)}/config`
];
const debOptions = [
'-t', 'deb',
'--deb-priority', 'optional'
];
const rpmOptions = [
'-t', 'rpm',
'--rpm-os', 'linux'
];
const args = [
`${buildDir}/=${config.path.home}/`,
`${buildDir}/config/=${config.path.conf}/`,
`${servicesByName.sysv.outputDir}/etc/=/etc/`,
`${servicesByName.systemd.outputDir}/lib/=/lib/`
];
const files = buildDir + '/=/opt/kibana';
const sysv = servicesByName.sysv.outputDir + '/etc/=/etc/';
const systemd = servicesByName.systemd.outputDir + '/lib/=/lib/';
//Manually find flags, multiple args without assignment are not entirely parsed
var flags = grunt.option.flags().join(',');
const buildDeb = !!flags.match('deb');
const buildRpm = !!flags.match('rpm');
const noneSpecified = !buildRpm && !buildDeb;
grunt.file.mkdir(targetDir);
if (buildDeb || noneSpecified) {
fpm(args.concat('-t', 'deb', '--deb-priority', 'optional', '-a', arch, files, sysv, systemd));
const flags = grunt.option.flags().filter(flag => /deb|rpm/.test(flag)).join(',');
const buildDeb = flags.includes('deb') || !flags.length;
const buildRpm = flags.includes('rpm') || !flags.length;
if (buildDeb) {
fpm([...baseOptions, ...debOptions, ...args]);
}
if (buildRpm || noneSpecified) {
fpm(args.concat('-t', 'rpm', '-a', arch, '--rpm-os', 'linux', files, sysv, systemd));
if (buildRpm) {
fpm([...baseOptions, ...rpmOptions, ...args]);
}
});

View file

@ -0,0 +1,17 @@
#!/bin/sh
set -e
user_check() {
getent passwd "$1" > /dev/null 2>&1
}
user_create() {
# Create a system user. A system user is one within the system uid range and
# has no expiration
useradd -r "$1"
}
if ! user_check "<%= user %>" ; then
user_create "<%= user %>"
fi
chown -R <%= user %>:<%= group %> <%= optimizeDir %>

View file

@ -0,0 +1,42 @@
#!/bin/sh
set -e
user_check() {
getent passwd "$1" > /dev/null 2>&1
}
user_remove() {
userdel "$1"
}
REMOVE_USER=false
case $1 in
# Includes cases for all valid arguments, exit 1 otherwise
# Debian
purge)
REMOVE_USER=true
;;
remove|failed-upgrade|abort-install|abort-upgrade|disappear|upgrade|disappear)
;;
# Red Hat
0)
REMOVE_USER=true
;;
1)
;;
*)
echo "post remove script called with unknown argument \`$1'" >&2
exit 1
;;
esac
if [ "$REMOVE_USER" = "true" ]; then
if user_check "<%= user %>" ; then
user_remove "<%= user %>"
fi
fi

View file

@ -0,0 +1,14 @@
#!/bin/sh
set -e
if command -v systemctl >/dev/null && systemctl is-active kibana.service >/dev/null; then
systemctl --no-reload stop kibana.service
elif [ -x /etc/init.d/kibana ]; then
if command -v invoke-rc.d >/dev/null; then
invoke-rc.d kibana stop
elif command -v service >/dev/null; then
service kibana stop
else
/etc/init.d/kibana stop
fi
fi

View file

@ -0,0 +1,16 @@
#!/bin/sh
set -e
echo -n "Stopping kibana service..."
if command -v systemctl >/dev/null && systemctl is-active kibana.service >/dev/null; then
systemctl --no-reload stop kibana.service
elif [ -x /etc/init.d/kibana ]; then
if command -v invoke-rc.d >/dev/null; then
invoke-rc.d kibana stop
elif command -v service >/dev/null; then
service kibana stop
else
/etc/init.d/kibana stop
fi
fi
echo " OK"

View file

@ -1,31 +1,29 @@
module.exports = function createServices(grunt) {
const { resolve } = require('path');
const { appendFileSync } = require('fs');
const exec = require('../utils/exec');
import { resolve } from 'path';
import { appendFileSync } from 'fs';
import exec from '../utils/exec';
export default (grunt) => {
const userScriptsDir = grunt.config.get('userScriptsDir');
const { path, user, group, name, description } = grunt.config.get('packages');
grunt.registerTask('_build:pleaseRun', function () {
// TODO(sissel): Detect if 'pleaserun' is found, and provide a useful error
// to the user if it is missing.
grunt.config.get('services').forEach(function (service) {
grunt.config.get('services').forEach((service) => {
grunt.file.mkdir(service.outputDir);
exec('pleaserun', [
'--install',
'--no-install-actions',
'--install-prefix', service.outputDir,
'--overwrite',
'--user', 'kibana',
'--group', 'kibana',
'--sysv-log-path', '/var/log/kibana/',
'--name', name,
'--description', description,
'--user', user,
'--group', group,
'--sysv-log-path', path.logs,
'-p', service.name,
'-v', service.version,
'/opt/kibana/bin/kibana'
path.kibanaBin,
`-c ${path.kibanaConfig}`
]);
});
grunt.file.mkdir(userScriptsDir);
exec('please-manage-user', ['--output', userScriptsDir, 'kibana']);
appendFileSync(resolve(userScriptsDir, 'installer.sh'), 'chown kibana:kibana /opt/kibana/optimize');
});
};

View file

@ -1,20 +1,52 @@
export default (grunt) => {
const version = grunt.config.get('pkg.version');
const productionPath = `kibana/${version.match(/\d\.\d/)[0]}`;
const stagingPath = `kibana/staging/${version.match(/\d\.\d\.\d/)[0]}-XXXXXXX/repos/${version.match(/\d\./)[0]}x`;
const rpmFolder = 'centos';
const debFolder = 'debian';
const VERSION = grunt.config.get('pkg.version');
const FOLDER_STAGING = `kibana/staging/${VERSION.match(/\d\.\d\.\d/)[0]}-XXXXXXX/repos/${VERSION.match(/\d\./)[0]}x`;
const FOLDER_PRODUCTION = `kibana/${VERSION.match(/\d\.\d/)[0]}`;
const FOLDERNAME_DEB = 'debian';
const FOLDERNAME_RPM = 'centos';
const PREFIX_STAGING_DEB = `${FOLDER_STAGING}/${FOLDERNAME_DEB}`;
const PREFIX_STAGING_RPM = `${FOLDER_STAGING}/${FOLDERNAME_RPM}`;
const PREFIX_PRODUCTION_DEB = `${FOLDER_PRODUCTION}/${FOLDERNAME_DEB}`;
const PREFIX_PRODUCTION_RPM = `${FOLDER_PRODUCTION}/${FOLDERNAME_RPM}`;
const FOLDER_CONFIG = '/etc/kibana';
const FOLDER_LOGS = '/var/log/kibana';
const FOLDER_HOME = '/usr/share/kibana';
const FILE_KIBANA_CONF = `${FOLDER_CONFIG}/kibana.yml`;
const FILE_KIBANA_BINARY = `${FOLDER_HOME}/bin/kibana`;
return {
staging: {
bucket: 'download.elasticsearch.org',
debPrefix: `${stagingPath}/${debFolder}`,
rpmPrefix: `${stagingPath}/${rpmFolder}`
publish: {
staging: {
bucket: 'download.elasticsearch.org',
debPrefix: PREFIX_STAGING_DEB,
rpmPrefix: PREFIX_STAGING_RPM
},
production: {
bucket: 'packages.elasticsearch.org',
debPrefix: PREFIX_STAGING_DEB,
rpmPrefix: PREFIX_STAGING_RPM
}
},
production: {
bucket: 'packages.elasticsearch.org',
debPrefix: `${productionPath}/${debFolder}`,
rpmPrefix: `${productionPath}/${rpmFolder}`
user: 'kibana',
group: 'kibana',
name: 'kibana',
description: 'Explore\ and\ visualize\ your\ Elasticsearch\ data',
site: 'https://www.elastic.co',
vendor: 'Elasticsearch,\ Inc.',
maintainer: 'Kibana Team\ \<info@elastic.co\>',
license: 'Apache\ 2.0',
version: VERSION,
path: {
conf: FOLDER_CONFIG,
logs: FOLDER_LOGS,
home: FOLDER_HOME,
kibanaBin: FILE_KIBANA_BINARY,
kibanaConfig: FILE_KIBANA_CONF
}
};
};

View file

@ -4,7 +4,7 @@ import { promisify } from 'bluebird';
import readline from 'readline';
export default (grunt) => {
const packages = grunt.config.get('packages');
const publishConfig = grunt.config.get('packages').publish;
const platforms = grunt.config.get('platforms');
function debS3(deb) {
@ -87,8 +87,8 @@ export default (grunt) => {
if (platform.debPath) {
debS3({
filePath: platform.debPath,
bucket: packages[environment].bucket,
prefix: packages[environment].debPrefix.replace('XXXXXXX', trimmedHash),
bucket: publishConfig[environment].bucket,
prefix: publishConfig[environment].debPrefix.replace('XXXXXXX', trimmedHash),
signatureKeyId: signature.id,
arch: platform.name.match('x64') ? 'amd64' : 'i386',
awsKey: aws.key,
@ -99,8 +99,8 @@ export default (grunt) => {
if (platform.rpmPath) {
rpmS3({
filePath: platform.rpmPath,
bucket: packages[environment].bucket,
prefix: packages[environment].rpmPrefix.replace('XXXXXXX', trimmedHash),
bucket: publishConfig[environment].bucket,
prefix: publishConfig[environment].rpmPrefix.replace('XXXXXXX', trimmedHash),
signingKeyName: signature.name,
awsKey: aws.key,
awsSecret: aws.secret

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{".kibana":{"mappings":{"index-pattern":{"properties":{"fields":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"timeFieldName":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"title":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}}}}}

View file

@ -16,28 +16,23 @@ import {
bdd.before(function () {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
common.debug('Starting dashboard before method');
common.debug('navigateToApp dashboard');
return esClient.delete('.kibana')
.then(function () {
return common.try(function () {
return esClient.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'});
});
})
var logstash = scenarioManager.loadIfEmpty('logstashFunctional');
// delete .kibana index and update configDoc
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
// and load a set of makelogs data
.then(function loadkibana4() {
.then(function loadkibanaVisualizations() {
common.debug('load kibana index with visualizations');
return elasticDump.elasticLoad('dashboard','.kibana');
})
.then(function () {
return scenarioManager.loadIfEmpty('logstashFunctional');
})
.then(function () {
common.debug('navigateToApp dashboard');
return common.navigateToApp('dashboard');
})
// wait for the logstash data load to finish if it hasn't already
.then(function () {
return logstash;
})
.catch(common.handleError(this));
});
@ -91,7 +86,7 @@ import {
bdd.it('should save and load dashboard', function saveAndLoadDashboard() {
var testSubName = 'Dashboard Test 1';
// save time on the dashboard?
// TODO: save time on the dashboard and test it
return dashboardPage.saveDashboard(testSubName)
// click New Dashboard just to clear the one we just created
.then(function () {

View file

@ -5,6 +5,8 @@ import {
headerPage,
scenarioManager,
settingsPage,
esClient,
elasticDump
} from '../../../support';
(function () {
@ -18,20 +20,16 @@ import {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
// start each test with an empty kibana index
return scenarioManager.reload('emptyKibana')
// delete .kibana index and update configDoc
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
.then(function loadkibanaIndexPattern() {
common.debug('load kibana index with default index pattern');
return elasticDump.elasticLoad('visualize','.kibana');
})
// and load a set of makelogs data
.then(function loadIfEmptyMakelogs() {
return scenarioManager.loadIfEmpty('logstashFunctional');
})
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
common.debug('discover');
return common.navigateToApp('discover');

View file

@ -5,6 +5,8 @@ import {
discoverPage,
settingsPage,
headerPage,
esClient,
elasticDump
} from '../../../support';
(function () {
@ -16,20 +18,16 @@ import {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
// start each test with an empty kibana index
return scenarioManager.reload('emptyKibana')
// delete .kibana index and update configDoc
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
.then(function loadkibanaIndexPattern() {
common.debug('load kibana index with default index pattern');
return elasticDump.elasticLoad('visualize','.kibana');
})
// and load a set of makelogs data
.then(function loadIfEmptyMakelogs() {
return scenarioManager.loadIfEmpty('logstashFunctional');
})
.then(function (navigateTo) {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
common.debug('discover');
return common.navigateToApp('discover');
@ -261,6 +259,12 @@ import {
.catch(common.handleError(this));
});
bdd.it('should not show "no results"', () => {
return discoverPage.hasNoResults().then(visible => {
expect(visible).to.be(false);
})
.catch(common.handleError(this));
});
function verifyChartData(expectedBarChartData) {
return common.try(function tryingForTime() {
@ -290,6 +294,69 @@ import {
}
});
bdd.describe('query #2, which has an empty time range', function () {
var fromTime = '1999-06-11 09:22:11.000';
var toTime = '1999-06-12 11:21:04.000';
bdd.before(() => {
common.debug('setAbsoluteRangeForAnotherQuery');
return headerPage
.setAbsoluteRange(fromTime, toTime)
.catch(common.handleError(this));
});
bdd.it('should show "no results"', () => {
return discoverPage.hasNoResults().then(visible => {
expect(visible).to.be(true);
})
.catch(common.handleError(this));
});
bdd.it('should suggest a new time range is picked', () => {
return discoverPage.hasNoResultsTimepicker().then(visible => {
expect(visible).to.be(true);
})
.catch(common.handleError(this));
});
bdd.it('should open and close the time picker', () => {
let i = 0;
return closeTimepicker() // close
.then(() => isTimepickerOpen(false)
.then(el => el.click()) // open
.then(() => isTimepickerOpen(true))
.then(el => el.click()) // close
.then(() => isTimepickerOpen(false))
.catch(common.handleError(this))
);
function closeTimepicker() {
return headerPage.isTimepickerOpen().then(shown => {
if (!shown) {
return;
}
return discoverPage
.getNoResultsTimepicker()
.click(); // close
});
}
function isTimepickerOpen(expected) {
return headerPage.isTimepickerOpen().then(shown => {
common.debug(`expect (#${++i}) timepicker to be ${peek(expected)} (is ${peek(shown)}).`);
expect(shown).to.be(expected);
return discoverPage.getNoResultsTimepicker();
function peek(state) {
return state ? 'open' : 'closed';
}
});
}
});
});
});
}());
}());

View file

@ -4,7 +4,9 @@ import {
discoverPage,
headerPage,
scenarioManager,
settingsPage
settingsPage,
esClient,
elasticDump
} from '../../../support';
(function () {
@ -16,20 +18,16 @@ import {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
// start each test with an empty kibana index
return scenarioManager.reload('emptyKibana')
// delete .kibana index and update configDoc
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
.then(function loadkibanaIndexPattern() {
common.debug('load kibana index with default index pattern');
return elasticDump.elasticLoad('visualize','.kibana');
})
// and load a set of makelogs data
.then(function loadIfEmptyMakelogs() {
return scenarioManager.loadIfEmpty('logstashFunctional');
})
.then(function (navigateTo) {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
common.debug('discover');
return common.navigateToApp('discover');

View file

@ -1,4 +1,13 @@
import { bdd, common, discoverPage, headerPage, settingsPage, scenarioManager } from '../../../support';
import {
bdd,
common,
discoverPage,
headerPage,
settingsPage,
scenarioManager,
esClient,
elasticDump
} from '../../../support';
(function () {
var expect = require('expect.js');
@ -18,20 +27,16 @@ import { bdd, common, discoverPage, headerPage, settingsPage, scenarioManager }
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
// start each test with an empty kibana index
return scenarioManager.reload('emptyKibana')
// delete .kibana index and update configDoc
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
.then(function loadkibanaIndexPattern() {
common.debug('load kibana index with default index pattern');
return elasticDump.elasticLoad('visualize','.kibana');
})
// and load a set of makelogs data
.then(function loadIfEmptyMakelogs() {
return scenarioManager.loadIfEmpty('logstashFunctional');
})
.then(function (navigateTo) {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
common.debug('discover');
return common.navigateToApp('discover');

View file

@ -2,7 +2,8 @@ import {
bdd,
common,
settingsPage,
scenarioManager
scenarioManager,
esClient
} from '../../../support';
(function () {
@ -11,7 +12,8 @@ import {
(function () {
bdd.describe('creating and deleting default index', function describeIndexTests() {
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
// delete .kibana index and then wait for Kibana to re-create it
return esClient.deleteAndUpdateConfigDoc()
.then(function () {
return settingsPage.navigateTo();
});
@ -25,7 +27,7 @@ import {
bdd.it('should allow setting advanced settings', function () {
return settingsPage.clickAdvancedTab()
.then(function TestCallSetAdvancedSettingsForTimezone() {
common.log('calling setAdvancedSetting');
common.debug('calling setAdvancedSetting');
return settingsPage.setAdvancedSettings('dateFormat:tz', 'America/Phoenix');
})
.then(function GetAdvancedSetting() {

View file

@ -3,6 +3,7 @@ import {
common,
settingsPage,
scenarioManager,
esClient
} from '../../../support';
(function () {
@ -11,7 +12,8 @@ import {
(function () {
bdd.describe('user input reactions', function () {
bdd.beforeEach(function () {
return scenarioManager.reload('emptyKibana')
// delete .kibana index and then wait for Kibana to re-create it
return esClient.deleteAndUpdateConfigDoc()
.then(function () {
return settingsPage.navigateTo();
});

View file

@ -3,7 +3,8 @@ import {
common,
remote,
scenarioManager,
settingsPage
settingsPage,
esClient
} from '../../../support';
(function () {
@ -12,7 +13,8 @@ import {
(function () {
bdd.describe('creating and deleting default index', function describeIndexTests() {
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
// delete .kibana index and then wait for Kibana to re-create it
return esClient.deleteAndUpdateConfigDoc()
.then(function () {
return settingsPage.navigateTo();
});

View file

@ -2,7 +2,8 @@ import {
bdd,
common,
scenarioManager,
settingsPage
settingsPage,
esClient
} from '../../../support';
(function () {
@ -11,7 +12,8 @@ import {
(function () {
bdd.describe('index result popularity', function describeIndexTests() {
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
// delete .kibana index and then wait for Kibana to re-create it
return esClient.deleteAndUpdateConfigDoc()
.then(function () {
return settingsPage.navigateTo();
});

View file

@ -4,6 +4,7 @@ import {
defaultTimeout,
settingsPage,
scenarioManager,
esClient
} from '../../../support';
(function () {
@ -12,7 +13,8 @@ import {
(function () {
bdd.describe('index result field sort', function describeIndexTests() {
bdd.before(function () {
return scenarioManager.reload('emptyKibana');
// delete .kibana index and then wait for Kibana to re-create it
return esClient.deleteAndUpdateConfigDoc();
});
var columns = [{

View file

@ -2,7 +2,8 @@ import {
bdd,
common,
scenarioManager,
settingsPage
settingsPage,
esClient
} from '../../../support';
(function () {
@ -11,7 +12,8 @@ import {
(function () {
bdd.describe('initial state', function () {
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
// delete .kibana index and then wait for Kibana to re-create it
return esClient.deleteAndUpdateConfigDoc()
.then(function () {
return settingsPage.navigateTo();
});

View file

@ -1,4 +1,4 @@
import { bdd, defaultTimeout, scenarioManager } from '../../../support';
import { bdd, defaultTimeout, scenarioManager, esClient, common } from '../../../support';
(function () {
bdd.describe('settings app', function () {
@ -7,16 +7,13 @@ import { bdd, defaultTimeout, scenarioManager } from '../../../support';
// on setup, we create an settingsPage instance
// that we will use for all the tests
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
.then(function () {
return scenarioManager.loadIfEmpty('makelogs');
});
return scenarioManager.loadIfEmpty('makelogs');
});
bdd.after(function () {
return scenarioManager.unload('makelogs')
.then(function () {
scenarioManager.unload('emptyKibana');
return esClient.delete('.kibana');
});
});

View file

@ -16,29 +16,8 @@ import {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickAreaChart');
return visualizePage.clickAreaChart();

View file

@ -11,23 +11,11 @@ import {
(function () {
bdd.describe('visualize app', function describeIndexTests() {
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
.catch(common.handleError(this));
});
bdd.before(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
});
bdd.describe('chart types', function indexPatternCreation() {

View file

@ -16,29 +16,8 @@ import {
var toTime = '2015-09-23 18:31:44.000';
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickDataTable');
return visualizePage.clickDataTable();

View file

@ -16,29 +16,8 @@ import {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickLineChart');
return visualizePage.clickLineChart();

View file

@ -21,29 +21,8 @@ import {
common.debug('Start of test' + testSubName + 'Visualization');
var vizName1 = 'Visualization ' + testSubName;
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickMetric');
return visualizePage.clickMetric();

View file

@ -16,29 +16,8 @@ import {
var fromTime = '2015-09-19 06:31:44.000';
var toTime = '2015-09-23 18:31:44.000';
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickPieChart');
return visualizePage.clickPieChart();

View file

@ -17,29 +17,8 @@ import {
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickTileMap');
return visualizePage.clickTileMap();

View file

@ -16,29 +16,8 @@ import {
var toTime = '2015-09-23 18:31:44.000';
bdd.before(function () {
return scenarioManager.reload('emptyKibana')
.then(function () {
common.debug('navigateTo');
return settingsPage.navigateTo();
})
.then(function () {
common.debug('createIndexPattern');
return settingsPage.createIndexPattern();
})
.then(function () {
return settingsPage.clickAdvancedTab();
})
.then(function GetAdvancedSetting() {
common.debug('check for required UTC timezone');
return settingsPage.getAdvancedSettings('dateFormat:tz');
})
.then(function (advancedSetting) {
expect(advancedSetting).to.be('UTC');
})
.then(function () {
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize');
})
common.debug('navigateToApp visualize');
return common.navigateToApp('visualize')
.then(function () {
common.debug('clickVerticalBarChart');
return visualizePage.clickVerticalBarChart();

View file

@ -1,4 +1,12 @@
import { bdd, remote, common, defaultTimeout, scenarioManager } from '../../../support';
import {
bdd,
remote,
common,
defaultTimeout,
scenarioManager,
esClient,
elasticDump
} from '../../../support';
(function () {
bdd.describe('visualize app', function () {
@ -7,14 +15,20 @@ import { bdd, remote, common, defaultTimeout, scenarioManager } from '../../../s
bdd.before(function () {
var self = this;
remote.setWindowSize(1200,800);
// load a set of makelogs data
common.debug('loadIfEmpty logstashFunctional ' + self.timeout);
return scenarioManager.loadIfEmpty('logstashFunctional');
});
bdd.after(function unloadMakelogs() {
return scenarioManager.unload('logstashFunctional');
common.debug('Starting visualize before method');
var logstash = scenarioManager.loadIfEmpty('logstashFunctional');
// delete .kibana index and update configDoc
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
.then(function loadkibanaIndexPattern() {
common.debug('load kibana index with default index pattern');
return elasticDump.elasticLoad('visualize','.kibana');
})
// wait for the logstash data load to finish if it hasn't already
.then(function () {
return logstash;
})
.catch(common.handleError(this));
});
require('./_chart_types');

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -42,16 +42,12 @@ export default (function () {
});
},
/**
* Add fields to the config doc (like setting timezone and defaultIndex)
* @return {Promise} A promise that is resolved when elasticsearch has a response
/*
** Gets configId which is needed when we're going to update the config doc.
** Also used after deleting .kibana index to know Kibana has recreated it.
*/
updateConfigDoc: function (docMap) {
// first we need to get the config doc's id so we can use it in our _update call
var self = this;
getConfigId: function () {
var configId;
var docMapString = JSON.stringify(docMap);
return this.client.search({
index: '.kibana',
@ -70,11 +66,25 @@ export default (function () {
} else {
configId = response.hits.hits[0]._id;
common.debug('config._id =' + configId);
return configId;
}
})
});
},
/**
* Add fields to the config doc (like setting timezone and defaultIndex)
* @return {Promise} A promise that is resolved when elasticsearch has a response
*/
updateConfigDoc: function (docMap) {
// first we need to get the config doc's id so we can use it in our _update call
var self = this;
var configId;
var docMapString = JSON.stringify(docMap);
return this.getConfigId()
// now that we have the id, we can update
// return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'});
.then(function (response) {
.then(function (configId) {
common.debug('updating config with ' + docMapString);
return self.client.update({
index: '.kibana',
@ -89,7 +99,35 @@ export default (function () {
.catch(function (err) {
throw err;
});
},
/**
* Wrap the common 'delete index', 'updateConfigDoc' into one.
* [docMap] is optional.
* @return {Promise} A promise that is resolved when elasticsearch has a response
*/
deleteAndUpdateConfigDoc: function (docMap) {
var self = this;
var configId;
return this.delete('.kibana')
.then(function () {
if (!docMap) {
return common.try(function () {
return self.getConfigId();
});
} else {
var docMapString = JSON.stringify(docMap);
return common.try(function () {
return self.updateConfigDoc(docMap);
});
}
})
.catch(function (err) {
throw err;
});
}
};
return EsClient;

View file

@ -1,11 +1,16 @@
import { config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../';
import fs from 'fs';
import mkdirp from 'mkdirp';
import { promisify } from 'bluebird';
const mkdirpAsync = promisify(mkdirp);
const writeFileAsync = promisify(fs.writeFile);
export default (function () {
var Promise = require('bluebird');
var moment = require('moment');
var testSubjSelector = require('@spalger/test-subj-selector');
var getUrl = require('../../utils/get_url');
var fs = require('fs');
var _ = require('lodash');
var parse = require('url').parse;
var format = require('url').format;
@ -244,34 +249,32 @@ export default (function () {
.then(function () { self.debug('... sleep(' + sleepMilliseconds + ') end'); });
},
handleError: function (testObj) {
var self = this;
var testName = (testObj.parent) ? [testObj.parent.name, testObj.name].join('_') : testObj.name;
handleError(testObj) {
const testName = (testObj.parent) ? [testObj.parent.name, testObj.name].join('_') : testObj.name;
return reason => {
const now = Date.now();
const fileName = `failure_${now}_${testName}.png`;
return function (reason) {
var now = Date.now();
var filename = ['failure', now, testName].join('_') + '.png';
return self.saveScreenshot(filename)
return this.saveScreenshot(fileName, true)
.finally(function () {
throw reason;
});
};
},
saveScreenshot: function saveScreenshot(filename) {
var self = this;
var outDir = path.resolve('test', 'output');
async saveScreenshot(fileName, isFailure = false) {
try {
const directoryName = isFailure ? 'failure' : 'session';
const directoryPath = path.resolve(`test/screenshots/${directoryName}`);
const filePath = path.resolve(directoryPath, fileName);
this.debug(`Taking screenshot "${filePath}"`);
return self.remote.takeScreenshot()
.then(function writeScreenshot(data) {
var filepath = path.resolve(outDir, filename);
self.debug('Taking screenshot "' + filepath + '"');
fs.writeFileSync(filepath, data);
})
.catch(function (err) {
self.log('SCREENSHOT FAILED: ' + err);
});
const screenshot = await this.remote.takeScreenshot();
await mkdirpAsync(directoryPath);
await writeFileAsync(filePath, screenshot);
} catch (err) {
this.log(`SCREENSHOT FAILED: ${err}`);
}
},
findTestSubject: function findTestSubject(selector) {

View file

@ -225,6 +225,24 @@ export default (function () {
return thisTime
.findByClassName('sidebar-list')
.getProperty('clientWidth');
},
hasNoResults: function hasNoResults() {
return common
.findTestSubject('discoverNoResults')
.then(() => true)
.catch(() => false);
},
getNoResultsTimepicker: function getNoResultsTimepicker() {
return common.findTestSubject('discoverNoResultsTimefilter');
},
hasNoResultsTimepicker: function hasNoResultsTimepicker() {
return this
.getNoResultsTimepicker()
.then(() => true)
.catch(() => false);
}
};

View file

@ -48,6 +48,13 @@ export default (function () {
.findDisplayedByClassName('navbar-timepicker-time-desc').click();
},
isTimepickerOpen: function isTimepickerOpen() {
return this.remote.setFindTimeout(defaultFindTimeout)
.findDisplayedByCssSelector('.kbn-timepicker')
.then(() => true)
.catch(() => false);
},
clickAbsoluteButton: function clickAbsoluteButton() {
return this.remote.setFindTimeout(defaultFindTimeout)
.findByLinkText('Absolute').click();

View file

@ -0,0 +1,43 @@
const fs = require('fs');
const path = require('path');
const imageDiff = require('image-diff');
const mkdirp = require('mkdirp');
function compareScreenshots() {
const SCREENSHOTS_DIR = 'test/screenshots';
const BASELINE_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'baseline');
const DIFF_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'diff');
const SESSION_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'session');
// We don't need to create the baseline dir because it's committed.
mkdirp.sync(DIFF_SCREENSHOTS_DIR);
mkdirp.sync(SESSION_SCREENSHOTS_DIR);
fs.readdir(SESSION_SCREENSHOTS_DIR, (readDirError, files) => {
const screenshots = files.filter(file => file.indexOf('.png') !== -1);
screenshots.forEach(screenshot => {
const sessionImagePath = path.resolve(SESSION_SCREENSHOTS_DIR, screenshot);
const baselineImagePath = path.resolve(BASELINE_SCREENSHOTS_DIR, screenshot);
const diffImagePath = path.resolve(DIFF_SCREENSHOTS_DIR, screenshot);
imageDiff.getFullResult({
actualImage: sessionImagePath,
expectedImage: baselineImagePath,
diffImage: diffImagePath,
shadow: true,
}, (comparisonError, result) => {
if (comparisonError) {
throw comparisonError;
}
const change = result.percentage;
const changePercentage = (change * 100).toFixed(2);
console.log(`${screenshot} has changed by ${changePercentage}%`);
});
});
});
}
compareScreenshots();