Merge branch 'master' of github.com:elastic/kibana into fix/configFileOrder

This commit is contained in:
spalger 2015-09-01 10:35:21 -07:00
commit 1d576f4fd2
109 changed files with 6479 additions and 656 deletions

4
.gitignore vendored
View file

@ -10,8 +10,10 @@ target
.idea
*.iml
*.log
esvm
/esvm
.htpasswd
installedPlugins
disabledPlugins
webpackstats.json
config/kibana.dev.yml
coverage

View file

@ -1 +1 @@
iojs-v2.4
iojs-v2.5

View file

@ -1,6 +1,8 @@
language: node_js
node_js: 'iojs-v2.4'
install: npm install
node_js: 'iojs-v2.5'
install:
- npm install -g npm@3.2
- npm install
script: ./node_modules/.bin/grunt travis
sudo: false
cache:

View file

@ -23,16 +23,22 @@ Please make sure you have signed the [Contributor License Agreement](http://www.
nvm install "$(cat .node-version)"
```
- Install npm 3.2
```sh
npm install -g npm@3.2
```
- Install dependencies
```sh
npm install
```
- Start elasticsearch, you can use [esvm](https://github.com/simianhacker/esvm) to make that easier.
- Start elasticsearch
```sh
grunt esvm:dev:keepalive
npm run elasticsearch
```
- Start the development server.
@ -59,15 +65,35 @@ Here are some hints for getting eslint setup in your favorite editor:
To ensure that your changes will not break other functionality, please run the test suite and build process before submitting your pull request.
Before running the tests you will need to install the projects dependencies as described below.
Before running the tests you will need to install the projects dependencies as described above.
Once that is complete just run:
```sh
./node_modules/.bin/grunt test build
npm run test && npm run build
```
Distributable, built packages can be found in `target/` after the build completes.
Distributable packages can be found in `target/` after the build completes.
#### Debugging test failures
The standard `npm run test` task runs several sub tasks and can take several minutes to complete, making debugging failures pretty painful. In order to ease the pain specialized tasks provide alternate methods for running the tests.
<dl>
<dt><code>npm run test:quick</code></dt>
<dd>Runs both server and browser tests, but skips linting</dd>
<dt><code>npm run test:server</code> or <code>npm run test:browser</code></dt>
<dd>Runs the tests for just the server or browser</dd>
<dt><code>npm run test:dev</code></dt>
<dd>
Initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
<br>
<img src="http://i.imgur.com/DwHxgfq.png">
</dd>
</dl>
### Submit a pull request

View file

@ -1,4 +1,4 @@
require('babel/register')(require('./src/optimize/babelOptions'));
require('babel/register')(require('./src/optimize/babelOptions').node);
module.exports = function (grunt) {
// set the config once before calling load-grunt-config
@ -16,6 +16,10 @@ module.exports = function (grunt) {
configFile: __dirname + '/src/config/kibana.yml',
karmaBrowser: (function () {
if (grunt.option('browser')) {
return grunt.option('browser');
}
switch (require('os').platform()) {
case 'win32':
return 'IE';
@ -41,23 +45,7 @@ module.exports = function (grunt) {
'<%= root %>/tasks/**/*.js',
'<%= src %>/**/*.js',
'!<%= src %>/fixtures/**/*.js'
],
deepModules: {
'caniuse-db': '1.0.30000265',
'chalk': '1.1.0',
'glob': '4.5.3',
'har-validator': '1.8.0',
'json5': '0.4.0',
'loader-utils': '0.2.11',
'micromatch': '2.2.0',
'postcss-normalize-url': '2.1.1',
'postcss-reduce-idents': '1.0.2',
'postcss-unique-selectors': '1.0.0',
'postcss-minify-selectors': '1.4.6',
'postcss-single-charset': '0.3.0',
'regenerator': '0.8.36'
}
]
};
grunt.config.merge(config);

View file

@ -35,4 +35,11 @@ Visit [Elastic.co](http://www.elastic.co/guide/en/kibana/current/index.html) for
## Snapshot Builds
***Snapshots are currently disabled*** until [#4597](https://github.com/elastic/kibana/issues/4597) is complete, the snapshot builds can not be built. Master can be started for development or experimentation by running `./bin/kibana` from the root of the project.
For the daring, snapshot builds are available. These builds are created after each commit to the master branch, and therefore are not something you should run in production.
| platform | | |
| --- | --- | --- |
| OSX | [tar](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-darwin-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-darwin-x64.zip) |
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-linux-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-linux-x64.zip) |
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-linux-x86.tar.gz) | [zip](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-linux-x86.zip) |
| Windows | [tar](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-windows.tar.gz) | [zip](http://download.elastic.co/kibana/kibana/kibana-4.2.0-snapshot-windows.zip) |

View file

@ -2,6 +2,7 @@ This is a collection of style guides for Kibana projects. The include guides for
- [JavaScript](#javascript-style-guide)
- [Kibana Project](#kibana-style-guide)
- [Html](#html-style-guide)
# JavaScript Style Guide
@ -855,6 +856,24 @@ require('ui/routes')
});
```
# Html Style Guide
### Multiple attribute values
When a node has multiple attributes that would cause it to exceed the line character limit, each attribute including the first should be on its own line with a single indent. Also, when a node that is styled in this way has child nodes, there should be a blank line between the openening parent tag and the first child tag.
```
<ul
attribute1="value1"
attribute2="value2"
attribute3="value3">
<li></li>
<li></li>
...
</ul>
```
# Attribution
This JavaScript guide forked from the [node style guide](https://github.com/felixge/node-style-guide) created by [Felix Geisendörfer](http://felixge.de/) and is

View file

@ -20,14 +20,14 @@
# If your Elasticsearch is protected with basic auth, this is the user credentials
# used by the Kibana server to perform maintence on the kibana_index at statup. Your Kibana
# users will still need to authenticate with Elasticsearch (which is proxied thorugh
# users will still need to authenticate with Elasticsearch (which is proxied through
# the Kibana server)
# elasticsearch.username: user
# elasticsearch.password: pass
# SSL for outgoing requests from the Kibana Server to the browser (PEM formatted)
# server.ssl.cert: /path/to/your/server.key
# server.ssl.key: /path/to/your/server.crt
# server.ssl.cert: /path/to/your/server.crt
# server.ssl.key: /path/to/your/server.key
# Optional setting to validate that your Elasticsearch backend uses the same key files (PEM formatted)
# elasticsearch.ssl.cert: /path/to/your/client.crt

View file

@ -81,7 +81,7 @@ If you are using a self-signed certificate for Elasticsearch, set the `ca` prope
[source,text]
----
# If you need to provide a CA certificate for your Elasticsarech instance, put
# If you need to provide a CA certificate for your Elasticsearch instance, put
# the path of the pem file here.
ca: /path/to/your/ca/cacert.pem
----

View file

@ -1,72 +0,0 @@
// Karma configuration
// Generated on Mon Jul 27 2015 04:03:51 GMT-0700 (MST)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
captureTimeout: 30000,
browserNoActivityTimeout: 120000,
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha'],
// list of files / patterns to load in the browser
files: [
'http://localhost:5601/bundles/commons.bundle.js',
'http://localhost:5601/bundles/tests.bundle.js',
'http://localhost:5601/bundles/commons.style.css',
'http://localhost:5601/bundles/tests.style.css'
],
proxies: {
'/tests/': 'http://localhost:5601/tests/',
'/bundles/': 'http://localhost:5601/bundles/'
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'growl'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: [
require('os').platform() === 'win32' ? 'IE' : 'Chrome'
],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
client: {
mocha: {
reporter: 'html', // change Karma's debug.html to the mocha web reporter
timeout: 10000,
slow: 5000
}
}
});
};

5547
npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -13,8 +13,8 @@
"private": false,
"version": "4.2.0-snapshot",
"build": {
"number": 8173,
"sha": "0102888deb393e4141369fbd1644a5d77f8732da"
"number": 8467,
"sha": "6cb7fec4e154faa0a4a3fee4b33dfef91b9870d9"
},
"main": "src/server/KbnServer.js",
"homepage": "https://www.elastic.co/products/kibana",
@ -34,9 +34,16 @@
],
"scripts": {
"test": "grunt test",
"start": "node ./src/server/cli/index.js --dev",
"test:dev": "grunt test:dev",
"test:quick": "grunt test:quick",
"test:browser": "grunt test:browser",
"test:server": "grunt test:server",
"test:coverage": "grunt test:coverage",
"build": "grunt build",
"start": "./bin/kibana --dev",
"precommit": "grunt lintStagedFiles",
"karma": "karma start"
"karma": "karma start",
"elasticsearch": "grunt esvm:dev:keepalive"
},
"repository": {
"type": "git",
@ -44,22 +51,21 @@
},
"dependencies": {
"@spalger/angular-bootstrap": "^0.10.0",
"@spalger/angular-nvd3": "^1.0.0-beta",
"@spalger/filesaver": "^1.1.2",
"@spalger/leaflet-draw": "^0.2.3",
"@spalger/leaflet-heat": "^0.1.3",
"@spalger/nvd3": "^1.8.1",
"@spalger/ui-ace": "^0.2.3",
"Nonsense": "^0.1.2",
"angular": "1.2.28",
"angular-bindonce": "0.3.1",
"angular-elastic": "2.5.0",
"angular-mocks": "1.2.28",
"angular-nvd3": "panda01/angular-nvd3#kibana",
"angular-route": "1.2.28",
"ansicolors": "^0.3.2",
"autoprefixer": "^5.2.0",
"autoprefixer-loader": "^2.0.0",
"babel": "^5.8.21",
"babel-core": "^5.8.21",
"babel-core": "^5.8.22",
"babel-loader": "^5.3.2",
"babel-runtime": "^5.8.20",
"bluebird": "^2.9.27",
@ -67,7 +73,6 @@
"bootstrap": "^3.3.5",
"brace": "^0.5.1",
"bunyan": "^1.2.3",
"chokidar": "^1.0.4",
"commander": "^2.8.1",
"css-loader": "^0.15.1",
"d3": "^3.5.6",
@ -79,32 +84,28 @@
"extract-text-webpack-plugin": "^0.8.2",
"file-loader": "^0.8.4",
"font-awesome": "^4.3.0",
"glob": "^4.3.2",
"good": "^6.2.0",
"good-squeeze": "^2.1.0",
"gridster": "^0.5.6",
"hapi": "^8.6.1",
"imports-loader": "^0.6.4",
"is-array": "^1.0.1",
"jade": "^1.7.2",
"jade-loader": "^0.7.1",
"joi": "^6.4.3",
"jquery": "^2.1.4",
"js-yaml": "^3.2.5",
"json-stringify-safe": "^5.0.1",
"jstimezonedetect": "^1.0.5",
"leaflet": "^0.7.3",
"less": "^2.5.1",
"less-loader": "^2.2.0",
"lodash": "^3.10.0",
"marked": "0.3.3",
"memory-fs": "^0.2.0",
"minimatch": "^2.0.8",
"mkdirp": "^0.5.1",
"moment": "^2.10.3",
"moment-timezone": "^0.4.0",
"ng-clip": "^0.2.6",
"numeral": "^1.5.3",
"nvd3": "panda01/nvd3#kibana",
"raw-loader": "^0.5.1",
"request": "^2.60.0",
"requirefrom": "^0.2.0",
@ -121,37 +122,44 @@
"zeroclipboard": "^2.2.0"
},
"devDependencies": {
"Nonsense": "^0.1.2",
"angular-mocks": "1.2.28",
"auto-release-sinon": "^1.0.3",
"babel-eslint": "^4.0.5",
"chokidar": "^1.0.4",
"eslint": "1.0.x",
"expect.js": "^0.3.1",
"faker": "^1.1.0",
"glob": "^4.3.2",
"grunt": "^0.4.5",
"grunt-babel": "^5.0.1",
"grunt-cli": "0.1.13",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-copy": "^0.8.0",
"grunt-esvm": "^1.1.3",
"grunt-esvm": "^1.1.5",
"grunt-karma": "^0.12.0",
"grunt-run": "spalger/grunt-run#master",
"grunt-run": "^0.4.0",
"grunt-s3": "^0.2.0-alpha.3",
"grunt-simple-mocha": "^0.4.0",
"gruntify-eslint": "^1.0.0",
"html-entities": "^1.1.1",
"husky": "^0.8.1",
"istanbul-instrumenter-loader": "^0.1.3",
"karma": "^0.13.3",
"karma-chrome-launcher": "^0.2.0",
"karma-coverage": "^0.5.0",
"karma-firefox-launcher": "^0.1.6",
"karma-growl-reporter": "^0.1.1",
"karma-ie-launcher": "^0.2.0",
"karma-mocha": "^0.2.0",
"karma-safari-launcher": "^0.1.1",
"libesvm": "^1.0.1",
"license-checker": "^3.1.0",
"load-grunt-config": "^0.7.0",
"marked-text-renderer": "^0.1.0",
"mocha": "^2.2.5",
"nock": "^2.9.0",
"npm": "^2.11.0",
"npm": "3.2",
"portscanner": "^1.0.0",
"simple-git": "^1.3.0",
"sinon": "^1.15.4",
@ -159,6 +167,7 @@
"wreck": "^6.1.0"
},
"engines": {
"node": ">=2"
"node": "2.5",
"npm": "3.2"
}
}

View file

@ -1,6 +1,6 @@
let cluster = require('cluster');
let { join } = require('path');
let { startsWith, debounce, compact, invoke, bindAll, once } = require('lodash');
let { debounce, compact, invoke, bindAll, once } = require('lodash');
let Log = require('../Log');
let Worker = require('./Worker');
@ -64,7 +64,7 @@ module.exports = class ClusterManager {
'installedPlugins'
], {
cwd: fromRoot('.'),
ignored: /[\\\/](\..*|node_modules|bower_components|public)[\\\/]/,
ignored: /[\\\/](\..*|node_modules|bower_components|public|__tests__)[\\\/]/
});
this.watcher.on('add', this.onWatcherAdd);
@ -81,25 +81,33 @@ module.exports = class ClusterManager {
}
setupManualRestart() {
let input = '';
let clear = () => input = '';
let clearSoon = debounce(clear, 1250);
let readline = require('readline');
let rl = readline.createInterface(process.stdin, process.stdout);
process.stdin.on('data', chunk => {
input += chunk.toString('utf8');
let nls = 0;
let clear = () => nls = 0;
let clearSoon = debounce(clear, 2000);
if (input === '\n') {
// wait for final \n
rl.setPrompt('');
rl.prompt();
rl.on('line', line => {
nls = nls + 1;
if (nls >= 2) {
clearSoon.cancel();
clear();
this.server.start();
} else {
clearSoon();
}
else if (startsWith(input, '\n\n')) {
clearSoon.cancel();
this.server.start();
clear();
}
else {
clear();
}
rl.prompt();
});
rl.on('SIGINT', () => {
rl.pause();
process.kill(process.pid, 'SIGINT');
});
}

View file

@ -19,7 +19,9 @@ let dead = fork => {
};
let kill = fork => {
fork.kill('SIGINT'); // make it snappy
// fork.kill() waits for process to disconnect, but causes occasional
// "ipc disconnected" errors and is too slow for the proc's "exit" event
fork.process.kill();
fork.killed = true;
};

View file

@ -6,7 +6,7 @@ var babelOpts = _.defaults({
fromRoot('src'),
/[\\\/](node_modules|bower_components)[\\\/]/
]
}, require('../optimize/babelOptions'));
}, require('../optimize/babelOptions').node);
require('babel/register')(babelOpts);
require('./cli');

View file

@ -1,2 +1,2 @@
require('babel/register')(require('../optimize/babelOptions'));
require('babel/register')(require('../optimize/babelOptions').node);
require('./cli');

View file

@ -1,7 +1,6 @@
var expect = require('expect.js');
var sinon = require('sinon');
var nock = require('nock');
var glob = require('glob');
var rimraf = require('rimraf');
var fs = require('fs');
var { join } = require('path');

View file

@ -1,6 +1,8 @@
var _ = require('lodash');
var zlib = require('zlib');
var Promise = require('bluebird');
var url = require('url');
var fs = require('fs');
var request = require('request');
var tar = require('tar');
var progressReporter = require('./progressReporter');
@ -17,7 +19,7 @@ module.exports = function (settings, logger) {
throw new Error('Not a valid url.');
}
logger.log('attempting to download ' + sourceUrl);
logger.log('Attempting to extract from ' + sourceUrl);
return Promise.try(function () {
return downloadSingle(sourceUrl, settings.workingPath, settings.timeout, logger)
@ -26,7 +28,7 @@ module.exports = function (settings, logger) {
return tryNext();
}
if (err.message === 'EEXTRACT') {
throw (new Error('Error extracting the plugin archive'));
throw (new Error('Error extracting the plugin archive... is this a valid tar.gz file?'));
}
throw (err);
});
@ -54,10 +56,10 @@ module.exports = function (settings, logger) {
}
return wrappedRequest(requestOptions)
.then(function (req) {
var reporter = progressReporter(logger, req);
.then(function (fileStream) {
var reporter = progressReporter(logger, fileStream);
req
fileStream
.on('response', reporter.handleResponse)
.on('data', reporter.handleData)
.on('error', _.partial(reporter.handleError, 'ENOTFOUND'))
@ -73,7 +75,12 @@ module.exports = function (settings, logger) {
function wrappedRequest(requestOptions) {
return Promise.try(function () {
return request.get(requestOptions);
let urlInfo = url.parse(requestOptions.url);
if (/^file/.test(urlInfo.protocol)) {
return fs.createReadStream(urlInfo.path);
} else {
return request.get(requestOptions);
}
})
.catch(function (err) {
if (err.message.match(/invalid uri/i)) {

View file

@ -7,7 +7,7 @@ module.exports = {
};
function install(settings, logger) {
logger.log(`installing ${settings.package}`);
logger.log(`Installing ${settings.package}`);
try {
fs.statSync(settings.pluginPath);

View file

@ -1,9 +1,9 @@
var Promise = require('bluebird');
/*
Responsible for reporting the progress of the file request stream
Responsible for reporting the progress of the file stream
*/
module.exports = function (logger, request) {
module.exports = function (logger, stream) {
var oldDotCount = 0;
var runningTotal = 0;
var totalSize = 0;
@ -22,7 +22,7 @@ module.exports = function (logger, request) {
if (err) logger.error(err);
hasError = true;
request.abort();
if (stream.abort) stream.abort();
_reject(new Error(errorMessage));
}
@ -56,7 +56,7 @@ module.exports = function (logger, request) {
function handleEnd() {
if (hasError) return;
logger.log('Download Complete');
logger.log('Extraction complete');
_resolve();
}

View file

@ -86,7 +86,11 @@ module.exports = function (program) {
let set = _.partial(_.set, settings);
let get = _.partial(_.get, settings);
if (opts.dev) set('env', 'development');
if (opts.dev) {
set('env', 'development');
set('optimize.lazy', true);
}
if (opts.elasticsearch) set('elasticsearch.url', opts.elasticsearch);
if (opts.port) set('server.port', opts.port);
if (opts.host) set('server.host', opts.host);

View file

@ -11,6 +11,7 @@ var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
let utils = require('requirefrom')('src/utils');
let fromRoot = utils('fromRoot');
let babelOptions = require('./babelOptions');
let babelExclude = [/[\/\\](webpackShims|node_modules|bower_components)[\/\\]/];
class BaseOptimizer {
constructor(opts) {
@ -111,26 +112,27 @@ class BaseOptimizer {
{ test: /[\/\\]src[\/\\](plugins|ui)[\/\\].+\.js$/, loader: `rjs-repack${mapQ}` },
{
test: /\.js$/,
exclude: /[\/\\](node_modules|bower_components)[\/\\]/,
exclude: babelExclude.concat(this.env.noParse),
loader: 'babel',
query: babelOptions
query: babelOptions.webpack
},
{
test: /\.jsx$/,
exclude: /[\/\\](node_modules|bower_components)[\/\\]/,
exclude: babelExclude.concat(this.env.noParse),
loader: 'babel',
query: defaults({
nonStandard: true
}, babelOptions)
nonStandard: true,
}, babelOptions.webpack)
}
].concat(this.env.loaders),
postLoaders: this.env.postLoaders || [],
noParse: this.env.noParse,
},
resolve: {
extensions: ['.babel.js', '.js', '.less', ''],
extensions: ['.js', '.jsx', '.less', ''],
postfixes: [''],
modulesDirectories: ['node_modules'],
modulesDirectories: ['webpackShims', 'node_modules'],
loaderPostfixes: ['-loader', ''],
root: fromRoot('.'),
alias: this.env.aliases,

View file

@ -1,4 +1,10 @@
module.exports = {
exports.webpack = {
stage: 1,
nonStandard: false
nonStandard: false,
optional: ['runtime']
};
exports.node = Object.assign({}, exports.webpack, {
optional: ['runtime', 'asyncToGenerator'],
blacklist: ['regenerator']
});

View file

@ -1,20 +0,0 @@
/**
* Optimized application entry file
*
* This is programatically created and updated, do not modify
*
* built using: <%= optimizerTagline %>
* includes code from:
<%
entry.deps.sort().forEach(function (plugin) {
print(` * - ${plugin}\n`);
})
%> *
*/
require('ui/testHarness');
<%
entry.modules.slice(0).reverse().forEach(function (id) {
print(`require('${id.replace(/\\/g, '\\\\')}');\n`);
});
%>require('ui/testHarness').bootstrap();

View file

@ -1,28 +0,0 @@
module.exports = function (kibana) {
let _ = require('lodash');
let fromRoot = require('../../utils/fromRoot');
let { readdirSync } = require('fs');
let { resolve, basename } = require('path');
let modules = {
moment$: fromRoot('node_modules/moment/min/moment.min.js')
};
let metaLibs = resolve(__dirname, 'metaLibs');
readdirSync(metaLibs).forEach(function (file) {
if (file[0] === '.') return;
let name = basename(file, '.js') + '$';
modules[name] = resolve(metaLibs, file);
});
return new kibana.Plugin({
init: false,
uiExports: {
modules: modules,
noParse: [
/node_modules[\/\\](angular|elasticsearch-browser)[\/\\]/,
/node_modules[\/\\](angular-nvd3|mocha|moment)[\/\\]/
]
}
});
};

View file

@ -1,5 +0,0 @@
require('d3');
require('nvd3/build/nv.d3.css');
require('nvd3/build/nv.d3.js');
require('angular-nvd3/dist/angular-nvd3.min.js');
module.exports = window.nv;

View file

@ -1,4 +0,0 @@
{
"name": "bundledLibs",
"version": "1.0.0"
}

View file

@ -23,7 +23,8 @@ module.exports = function (kibana) {
cert: Joi.string(),
key: Joi.string()
}).default(),
minimumVerison: Joi.string().default('1.4.4')
apiVersion: Joi.string().default('master'),
minimumVerison: Joi.string().default('2.0.0')
}).default();
},
@ -34,6 +35,7 @@ module.exports = function (kibana) {
exposeClient(server);
createProxy(server, 'GET', '/{paths*}');
createProxy(server, 'POST', '/_mget');
createProxy(server, 'POST', '/{index}/_search');
createProxy(server, 'POST', '/_msearch');
function noBulkCheck(request, reply) {

View file

@ -13,6 +13,7 @@ describe('plugins/elasticsearch', function () {
var get = sinon.stub().withArgs('elasticserach.minimumVerison').returns('1.4.3');
var config = function () { return { get: get }; };
server = {
log: _.noop,
config: config,
plugins: {
elasticsearch: {

View file

@ -4,6 +4,8 @@ var versionMath = require('./version_math');
var SetupError = require('./setup_error');
module.exports = function (server) {
server.log(['plugin', 'debug'], 'Checking Elasticsearch version');
var client = server.plugins.elasticsearch.client;
var minimumElasticsearchVersion = server.config().get('elasticsearch.minimumVerison');
@ -31,7 +33,6 @@ module.exports = function (server) {
`${minimumElasticsearchVersion} or higher on all nodes. I found ` +
`the following incompatible nodes in your cluster: ${badNodeNames.join(',')}`;
server.plugins.elasticsearch.status.red(message);
throw new SetupError(server, message);
});
};

View file

@ -1,30 +1,32 @@
var url = require('url');
var fs = require('fs');
var _ = require('lodash');
var readFile = _.partialRight(require('fs').readFileSync, 'utf8');
var http = require('http');
var agentOptions;
module.exports = function (server) {
var https = require('https');
module.exports = _.memoize(function (server) {
var config = server.config();
var target = url.parse(config.get('elasticsearch.url'));
if (!agentOptions) {
agentOptions = {
rejectUnauthorized: config.get('elasticsearch.ssl.verify')
};
if (!/^https/.test(target.protocol)) return new http.Agent();
var customCA;
if (/^https/.test(target.protocol) && config.get('elasticsearch.ssl.ca')) {
customCA = fs.readFileSync(config.get('elasticsearch.ssl.ca'), 'utf8');
agentOptions.ca = [customCA];
}
var agentOptions = {
rejectUnauthorized: config.get('elasticsearch.ssl.verify')
};
// Add client certificate and key if required by elasticsearch
if (/^https/.test(target.protocol) &&
config.get('elasticsearch.ssl.cert') &&
config.get('elasticsearch.ssl.key')) {
agentOptions.crt = fs.readFileSync(config.get('elasticsearch.ssl.cert'), 'utf8');
agentOptions.key = fs.readFileSync(config.get('elasticsearch.ssl.key'), 'utf8');
}
if (config.get('elasticsearch.ssl.ca')) {
agentOptions.ca = [readFile(config.get('elasticsearch.ssl.ca'))];
}
return new http.Agent(agentOptions);
};
// Add client certificate and key if required by elasticsearch
if (config.get('elasticsearch.ssl.cert') && config.get('elasticsearch.ssl.key')) {
agentOptions.cert = readFile(config.get('elasticsearch.ssl.cert'));
agentOptions.key = readFile(config.get('elasticsearch.ssl.key'));
}
return new https.Agent(agentOptions);
});
// See https://lodash.com/docs#memoize: We use a Map() instead of the default, because we want the keys in the cache
// to be the server objects, and by default these would be coerced to strings as keys (which wouldn't be useful)
module.exports.cache = new Map();

View file

@ -13,6 +13,7 @@ module.exports = function (server) {
var clientCrt = config.get('elasticsearch.ssl.cert');
var clientKey = config.get('elasticsearch.ssl.key');
var ca = config.get('elasticsearch.ssl.ca');
var apiVersion = config.get('elasticsearch.apiVersion');
if (username && password) {
uri.auth = util.format('%s:%s', username, password);
@ -30,7 +31,7 @@ module.exports = function (server) {
var client = new elasticsearch.Client({
host: url.format(uri),
ssl: ssl,
apiVersion: '1.4',
apiVersion: apiVersion,
log: function () {
this.error = function (err) {
server.log(['error', 'elasticsearch'], err);

View file

@ -15,7 +15,6 @@ module.exports = function (plugin, server) {
plugin.status.yellow('Waiting for Elasticsearch');
function waitForPong() {
return client.ping({ requestTimeout: 1500 }).catch(function (err) {
if (!(err instanceof NoConnections)) throw err;
@ -54,13 +53,12 @@ module.exports = function (plugin, server) {
function check() {
return waitForPong()
.then(_.partial(checkEsVersion, server, plugin))
.then(_.partial(checkEsVersion, server))
.then(waitForShards)
.then(_.partial(migrateConfig, server))
.catch(_.bindKey(server, 'log', 'error'));
.catch(err => plugin.status.red(err));
}
var timeoutId = null;
function scheduleCheck(ms) {

View file

@ -1,6 +1,14 @@
<div class="panel panel-default" ng-switch on="panel.type" ng-if="savedObj || error">
<div class="panel-heading">
<span class="panel-title">{{savedObj.title}}</span>
<span class="panel-title">
<i
class="fa"
ng-class="savedObj.vis.type.icon"
aria-label="{{savedObj.vis.type.title}} Icon"
title="{{savedObj.vis.type.title}}">
</i>
{{savedObj.title}}
</span>
<div class="btn-group">
<a aria-label="Edit" ng-show="chrome.getVisible() && editUrl" ng-href="{{editUrl}}">
<i aria-hidden="true" class="fa fa-pencil"></i>

View file

@ -76,6 +76,12 @@ dashboard-grid {
.ellipsis();
flex: 1 1 auto;
i {
opacity: 0.3;
font-size: 1.2em;
margin-right: 4px;
}
}
a {

View file

@ -5,7 +5,7 @@
(Default: <i>{{conf.defVal == undefined ? 'null' : conf.defVal}}</i>)
</span>
<br>
<span class="smaller">{{conf.description}}</span>
<span class="smaller" ng-bind-html="conf.description | trustAsHtml"></span>
</td>
<td class="value">

View file

@ -5,6 +5,7 @@ define(function (require) {
require('plugins/kibana/settings/sections/indices/index'),
require('plugins/kibana/settings/sections/advanced/index'),
require('plugins/kibana/settings/sections/objects/index'),
require('plugins/kibana/settings/sections/status/index'),
require('plugins/kibana/settings/sections/about/index')
];
});

View file

@ -129,6 +129,8 @@ define(function (require) {
if (_.contains(loadedEditors, editor)) return;
loadedEditors.push(editor);
editor.$blockScrolling = Infinity;
var session = editor.getSession();
var fieldName = editor.container.id;

View file

@ -0,0 +1,10 @@
define(function (require) {
var _ = require('lodash');
return {
order: 3,
name: 'status',
display: 'Status',
url: '/status'
};
});

View file

@ -130,7 +130,8 @@
<div class="vis-editor-canvas" ng-class="{ embedded: !chrome.getVisible() }">
<div class="visualize-info" ng-if="savedVis.id">
<div class="visualize-info-tab">
<div class="visualize-info-tab" title="{{savedVis.vis.type.title}}">
<i class="fa" aria-label="{{savedVis.vis.type.title}} Icon" ng-class="savedVis.vis.type.icon"></i>
<span bindonce bo-bind="savedVis.title"></span>
</div>
</div>

View file

@ -9,10 +9,10 @@ module.exports = function formatNumber(num, which) {
case 'time':
return moment(num).format('HH:mm:ss');
case 'byte':
format += 'b';
format += ' b';
break;
case 'ms':
postfix = 'ms';
postfix = ' ms';
break;
}
return numeral(num).format(format) + postfix;

View file

@ -1,48 +1,39 @@
<div class="container">
<div class="container state_default state_{{ui.serverState}}">
<header>
<h1>
<strong>Kibana</strong>&nbsp;Status Page
Status: <span class="state_color">{{ ui.serverStateMessage }}</span>
<i class="fa state_color state_icon" />
</h1>
</header>
<section class="section">
<h4>What is this page?</h4>
<p>This page is your sanity check, and your savior. You can check for potential problems</p>
<p>Here is the status of your kibana instance and the plugins you have installed along with some, statistics to asses potential problems.</p>
</section>
<div class="system_status_wrapper state_default state_{{ui.serverState}}">
<h3 class="title">
<b>System Status</b> {{ ui.serverStateMessage }}
</h3>
<div class="row metrics_wrapper">
<div ng-repeat="(name, data) in ui.metrics">
<status-page-metric name="{{name}}" data="data"></status-page-metric>
</div>
</div>
<div class="row plugin_status_wrapper">
<h3>Installed Plugins</h3>
<div ng-if="!ui.statuses && ui.loading" class="loading_statuses">
<span class="spinner"></span>
</div>
<h4 ng-if="!ui.statuses && !ui.loading" class="missing_statuses">
No status information available
No plugin status information available
</h4>
<table class="status_breakdown" ng-if="ui.statuses">
<table class="plugin_status_breakdown row" ng-if="ui.statuses">
<tr>
<th class="col-xs-1">Name</th>
<th class="col-xs-11">Description</th>
<th class="col-xs-11">Status</th>
</tr>
<tr ng-repeat="status in ui.statuses" class="status_row state_default state_{{status.state}}">
<tr ng-repeat="status in ui.statuses" class="status_row plugin_state_default plugin_state_{{status.state}}">
<td class="col-xs-1 status_name">{{status.name}}</td>
<td class="col-xs-11 status_message">{{status.message}}</td>
<td class="col-xs-11 status_message">
<i class="fa plugin_state_color plugin_state_icon" />
{{status.message}}
</td>
</tr>
</table>
</div>
<h2>Server Metrics</h2>
<p>Interval of 5 seconds, with a max history of 5 minutes.</p>
<div id="chart_cont" class="row">
<div ng-repeat="(name, data) in ui.metrics">
<status-page-metric name="{{name}}" data="data"></status-page-metric>
</div>
</div>
</div>

View file

@ -9,7 +9,8 @@ require('ui/chrome')
.setTabs([
{
id: '',
title: 'Server Status'
title: 'Server Status',
activeIndicatorColor: '#EFF0F2'
}
])
.setRootTemplate(require('plugins/statusPage/statusPage.html'))
@ -24,6 +25,7 @@ require('ui/chrome')
return $http
.get('/api/status')
.then(function (resp) {
if (ui.fetchError) {
ui.fetchError.clear();
ui.fetchError = null;
@ -36,7 +38,7 @@ require('ui/chrome')
var overall = data.status.overall;
if (!ui.serverState || (ui.serverState !== overall.state)) {
ui.serverState = overall.state;
ui.serverStateMessage = overall.nickname || overall.title;
ui.serverStateMessage = overall.title;
}
})
.catch(function () {
@ -50,9 +52,4 @@ require('ui/chrome')
};
ui.refresh();
// let the browser decide when to slow down requests
setInterval(function () {
$scope.$eval(ui.refresh);
}, 5000);
});

View file

@ -1,126 +1,178 @@
@import "~font-awesome/less/font-awesome";
@status-bg: #eff0f2;
@status-metric-bg: #fff;
@status-metric-border: #aaa;
@status-metric-title-color: #666;
@status-plugins-bg: #fff;
@status-plugins-border: #bbb;
@status-plugins-headings-color: #666;
@status-default: #7c7c7c;
@status-green: #94c63d;
@status-yellow: #edb800;
@status-red: #da1e04;
@icon-default: @fa-var-clock-o;
@icon-green: @fa-var-check;
@icon-yellow: @fa-var-exclamation-circle;
@icon-red: @fa-var-exclamation-triangle;
// background of main page
.content {
background-color: @status-bg;
}
.section {
margin-bottom:15px;
}
.status_breakdown {
margin:0 15px 15px 15px;
// metrics section
.metrics_wrapper {
margin-top: 25px;
.status_metric_wrapper {
padding: 10px;
border: 0;
.status_row {
height:30px;
line-height:30px;
+ .status_row {
border-top:1px solid #ebebeb;
.content {
text-align: right;
padding: 15px;
padding-right: 20px;
background-color: @status-metric-bg;
border-top: 2px solid;
border-top-color: @status-metric-border;
.title {
color: @status-metric-title-color;
margin: 0 0 5px 0;
}
.average {
font-size: 42px;
line-height:45px;
font-weight: normal;
margin:0;
}
}
}
th {
font-size:10px;
color:#a9a9a9;
height:25px;
line-height:25px;
}
.status_name {
font-weight:bold;
padding:0px 5px;
}
.status_message {
border-left:1px solid #ebebeb;
padding:0;
padding-left:15px;
}
}
.system_status_wrapper {
// plugin status table section
.plugin_status_wrapper {
margin-top: 25px;
margin-left: -5px;
margin-right: -5px;
border-top:2px solid;
background-color: @status-plugins-bg;
padding: 10px;
h3 {
margin-top: 3px;
margin-bottom: 3px;
}
.missing_statuses,
.loading_statuses {
padding: 20px;
text-align: center;
}
}
.status_chart_wrapper {
border-top:1px solid #ebebeb;
border-left:1px solid #ebebeb;
.average {
font-size: 42px;
line-height:45px;
margin-top:0;
font-weight:bold;
}
.title {
margin:0 0 5px 0;
text-transform:capitalize;
}
}
.plugin_status_breakdown {
margin-left: 0;
margin-right: 0;
#chart_cont {
margin-top:35px;
}
.status_row {
height:30px;
line-height:30px;
border-bottom:1px solid;
border-bottom-color: @status-plugins-border;
}
.status_chart_wrapper:nth-child(2), .status_chart_wrapper:nth-child(3) {
border-top:0 none transparent;
}
th {
color:@status-plugins-headings-color;
font-weight: normal;
height:25px;
line-height:25px;
border-bottom:1px solid;
border-bottom-color: @status-plugins-border;
}
.status_chart_wrapper:first-child {
border-top:0 none transparent;
border-left:0 none transparent;
}
.status_name {
padding:0px 5px;
border-left: 2px solid;
}
.status_chart_wrapper:nth-child(3n + 1) {
border-left:0 none transparent;
}
.status_chart_wrapper:nth-child(n + 4) {
padding-top:20px;
}
.nv-axis.nv-x .tick line {
display:none;
}
.state(@primary, @secondary) {
&.system_status_wrapper {
border:1px solid @primary;
border-radius:5px;
overflow: hidden;
.title {
color:#ffffff;
height:50px;
line-height:50px;
margin:0 0 10px 0;
padding:0 15px;
border-color:@primary;
background:@primary;
background:-moz-linear-gradient(left,@primary 0%,@secondary 100%);
background:-webkit-gradient(linear,left top,right top,color-stop(0%,@primary),color-stop(100%,@secondary));
background:-webkit-linear-gradient(left,@primary 0%,@secondary 100%);
background:-o-linear-gradient(left,@primary 0%,@secondary 100%);
background:-ms-linear-gradient(left,@primary 0%,@secondary 100%);
background:linear-gradient(to right,@primary 0%,@secondary 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=@primary,endColorstr=@secondary,GradientType=1);
.status_message {
padding:0;
padding-left:15px;
border-right: 2px solid;
}
}
}
&.status_row {
color: @primary;
//plugin state
.plugin_state(@color, @icon) {
.plugin_state_color {
color: @color;
}
.plugin_state_icon:before {
content: @icon;
}
.status_name {
border-left-color: @color !important;
}
.status_message {
border-right-color: @color !important;
}
}
.plugin_state_default {
.plugin_state(@status-default, @icon-default);
}
.plugin_state_green {
.plugin_state(@status-green, @icon-green);
}
.plugin_state_yellow {
.plugin_state(@status-yellow, @icon-yellow);
}
.plugin_state_red {
.plugin_state(@status-red, @icon-red);
}
//server state
.state(@color, @icon) {
.state_color {
color: @color;
}
.state_icon:before {
content: @icon;
}
.plugin_status_wrapper {
border-top-color: @color;
}
}
.state_default {
.state(#7C7C7C, #CFCFCF);
.state(@status-default, @icon-default);
}
.state_green {
.state(#0a8e03, #96f501);
.state(@status-green, @icon-green);
}
.state_yellow {
.state(#fdee00, #c16f00);
.state(@status-yellow, @icon-yellow);
}
.state_red {
.state(#da1e04, #ff730f);
.state(@status-red, @icon-red);
}

View file

@ -1,5 +1,6 @@
<div class="status_chart_wrapper col-md-4">
<h3 class="title">{{metric.title}}</h3>
<h4 class="average">{{ metric.averages.join(', ') }}</h4>
<nvd3 options="metric.chartOptions" data="metric.chartData"></nvd3>
<div class="status_metric_wrapper col-md-4">
<div class="content">
<h3 class="title">{{metric.extendedTitle}}</h3>
<h4 class="average">{{ metric.averages.join(', ') }}</h4>
</div>
</div>

View file

@ -5,7 +5,6 @@ require('angular-nvd3');
var toTitleCase = require('./lib/toTitleCase');
var formatNumber = require('./lib/formatNumber');
var getChartOptions = _.memoize(require('./lib/makeChartOptions'));
var readStatData = require('./lib/readStatData');
function calcAvg(metricList, metricNumberType) {
@ -33,17 +32,16 @@ require('ui/modules')
self.name = $scope.name;
self.title = toTitleCase(self.name);
self.extendedTitle = self.title;
self.numberType = 'precise';
self.seriesNames = [];
switch (self.name) {
case 'heapTotal':
case 'heapUsed':
case 'rss':
self.numberType = 'byte';
break;
case 'delay':
case 'responseTimeAvg':
case 'responseTimeMax':
self.numberType = 'ms';
@ -54,12 +52,22 @@ require('ui/modules')
break;
}
self.chartOptions = getChartOptions(self.numberType);
$scope.$watch('data', function (data) {
self.rawData = data;
self.chartData = readStatData(self.rawData, self.seriesNames);
self.averages = calcAvg(self.chartData, self.numberType);
var unit = '';
self.averages = self.averages.map(function (average) {
var parts = average.split(' ');
var value = parts.shift();
unit = parts.join(' ');
return value;
});
self.extendedTitle = self.title;
if (unit) {
self.extendedTitle = `${self.extendedTitle} (${unit})`;
}
});
}
};

View file

@ -1,10 +1,9 @@
let { chain, memoize } = require('lodash');
let { resolve } = require('path');
let { map, fromNode } = require('bluebird');
let fromRoot = require('./fromRoot');
let { Glob } = require('glob');
let fromRoot = require('../../utils/fromRoot');
let findSourceFiles = async (patterns, cwd = fromRoot('.')) => {
patterns = [].concat(patterns || []);

View file

@ -1,17 +1,22 @@
module.exports = (kibana) => {
if (!kibana.config.get('optimize.tests')) return;
let { union } = require('lodash');
let utils = require('requirefrom')('src/utils');
let fromRoot = utils('fromRoot');
let findSourceFiles = utils('findSourceFiles');
let findSourceFiles = require('./findSourceFiles');
return new kibana.Plugin({
config: (Joi) => {
return Joi.object({
enabled: Joi.boolean().default(true),
instrument: Joi.boolean().default(false)
}).default();
},
uiExports: {
bundle: async (UiBundle, env, apps) => {
let modules = [];
let config = kibana.config;
// add the modules from all of the apps
for (let app of apps) {
@ -25,6 +30,14 @@ module.exports = (kibana) => {
for (let f of testFiles) modules.push(f);
if (config.get('testsBundle.instrument')) {
env.addPostLoader({
test: /\.jsx?$/,
exclude: /[\/\\](__tests__|node_modules|bower_components|webpackShims)[\/\\]/,
loader: 'istanbul-instrumenter'
});
}
return new UiBundle({
id: 'tests',
modules: modules,

View file

@ -1,4 +1,4 @@
{
"name": "tests_bundle",
"name": "testsBundle",
"version": "0.0.0"
}

View file

@ -1,37 +1,61 @@
let Promise = require('bluebird');
let Joi = require('joi');
let _ = require('lodash');
let { zipObject } = require('lodash');
let override = require('./override');
let pkg = require('requirefrom')('src/utils')('packageJson');
const schema = Symbol('Joi Schema');
const schemaKeys = Symbol('Schema Extensions');
const vals = Symbol('config values');
const pendingSets = Symbol('Pending Settings');
module.exports = class Config {
constructor(schema, defaults) {
this.schema = Joi.object({}).default();
this.config = {};
this.unappliedDefaults = _.cloneDeep(defaults || {});
if (schema) this.extendSchema(schema);
constructor(initialSchema, initialSettings) {
this[schemaKeys] = new Map();
this[vals] = Object.create(null);
this[pendingSets] = new Map(_.pairs(_.cloneDeep(initialSettings || {})));
if (initialSchema) this.extendSchema(initialSchema);
}
extendSchema(key, schema) {
getPendingSets() {
return this[pendingSets];
}
extendSchema(key, extension) {
if (key && key.isJoi) {
return _.each(key._inner.children, function (child) {
return _.each(key._inner.children, (child) => {
this.extendSchema(child.key, child.schema);
}, this);
});
}
if (this.has(key)) {
throw new Error(`Config schema already has key ${key}`);
throw new Error(`Config schema already has key: ${key}`);
}
this.schema = this.schema.keys(_.set({}, key, schema));
this[schemaKeys].set(key, extension);
this[schema] = null;
if (this.unappliedDefaults[key]) {
this.set(key, this.unappliedDefaults[key]);
this.unappliedDefaults[key] = null;
let initialVals = this[pendingSets].get(key);
if (initialVals) {
this.set(key, initialVals);
this[pendingSets].delete(key);
} else {
this._commit(this.config);
this._commit(this[vals]);
}
}
removeSchema(key) {
if (!this[schemaKeys].has(key)) {
throw new TypeError(`Unknown schema key: ${key}`);
}
this[schema] = null;
this[schemaKeys].delete(key);
this[pendingSets].delete(key);
delete this[vals][key];
}
resetTo(obj) {
@ -40,7 +64,7 @@ module.exports = class Config {
set(key, value) {
// clone and modify the config
let config = _.cloneDeep(this.config);
let config = _.cloneDeep(this[vals]);
if (_.isPlainObject(key)) {
config = override(config, key);
} else {
@ -51,10 +75,10 @@ module.exports = class Config {
this._commit(config);
}
_commit(newConfig) {
_commit(newVals) {
// resolve the current environment
let env = newConfig.env;
delete newConfig.env;
let env = newVals.env;
delete newVals.env;
if (_.isObject(env)) env = env.name;
if (!env) env = process.env.NODE_ENV || 'production';
@ -79,23 +103,21 @@ module.exports = class Config {
);
}
let results = Joi.validate(newConfig, this.schema, {
context: context
});
let results = Joi.validate(newVals, this.getSchema(), { context });
if (results.error) {
throw results.error;
}
this.config = results.value;
this[vals] = results.value;
}
get(key) {
if (!key) {
return _.cloneDeep(this.config);
return _.cloneDeep(this[vals]);
}
let value = _.get(this.config, key);
let value = _.get(this[vals], key);
if (value === undefined) {
if (!this.has(key)) {
throw new Error('Unknown config key: ' + key);
@ -130,6 +152,15 @@ module.exports = class Config {
key = key.join('.');
}
return !!has(key, this.schema);
return !!has(key, this.getSchema());
}
getSchema() {
if (!this[schema]) {
let objKeys = zipObject([...this[schemaKeys]]);
this[schema] = Joi.object().keys(objKeys).default();
}
return this[schema];
}
};

View file

@ -208,6 +208,24 @@ describe('lib/config/config', function () {
});
describe('#removeSchema(key)', function () {
it('should completely remove the key', function () {
var config = new Config(Joi.object().keys({
a: Joi.number().default(1)
}));
expect(config.get('a')).to.be(1);
config.removeSchema('a');
expect(() => config.get('a')).to.throwException('Unknown config key');
});
it('only removes existing keys', function () {
var config = new Config(Joi.object());
expect(() => config.removeSchema('b')).to.throwException('Unknown schema');
});
});
});
});

View file

@ -1,16 +1,11 @@
module.exports = function (kbnServer, server, config) {
let _ = require('lodash');
server.decorate('server', 'config', function () {
return kbnServer.config;
});
_.forOwn(config.unappliedDefaults, function (val, key) {
if (val === null) return;
server.log(['warning', 'config'], {
tmpl: 'Settings for "<%= key %>" were not applied, check for spelling errors and ensure the plugin is loaded.',
key: key,
val: val
});
});
let tmpl = 'Settings for "<%= key %>" were not applied, check for spelling errors and ensure the plugin is loaded.';
for (let [key, val] of config.getPendingSets()) {
server.log(['warning', 'config'], { key, val, tmpl });
}
};

View file

@ -79,20 +79,12 @@ module.exports = Joi.object({
optimize: Joi.object({
enabled: Joi.boolean().default(true),
bundleFilter: Joi.string().when('tests', {
is: true,
then: Joi.default('tests'),
otherwise: Joi.default('*')
}),
bundleFilter: Joi.string().default('!tests'),
bundleDir: Joi.string().default(fromRoot('optimize/bundles')),
viewCaching: Joi.boolean().default(Joi.ref('$prod')),
lazy: Joi.boolean().when('$dev', {
is: true,
then: Joi.default(true),
otherwise: Joi.default(false)
}),
lazy: Joi.boolean().default(false),
lazyPort: Joi.number().default(5602),
lazyHost: Joi.string().hostname().default('0.0.0.0'),
lazyHost: Joi.string().hostname().default('localhost'),
lazyPrebuild: Joi.boolean().default(false),
lazyProxyTimeout: Joi.number().default(5 * 60000),
unsafeCache: Joi
@ -109,8 +101,7 @@ module.exports = Joi.object({
Joi.boolean()
)
.default(Joi.ref('$dev')),
profile: Joi.boolean().default(false),
tests: Joi.boolean().default(false),
profile: Joi.boolean().default(false)
}).default()
}).default();

View file

@ -1,5 +1,6 @@
module.exports = function (kbnServer, server, config) {
let _ = require('lodash');
let fs = require('fs');
let Boom = require('boom');
let Hapi = require('hapi');
let parse = require('url').parse;
@ -10,13 +11,23 @@ module.exports = function (kbnServer, server, config) {
server = kbnServer.server = new Hapi.Server();
// Create a new connection
server.connection({
var connectionOptions = {
host: config.get('server.host'),
port: config.get('server.port'),
routes: {
cors: config.get('server.cors')
}
});
};
// enable tls if ssl key and cert are defined
if (config.get('server.ssl.key') && config.get('server.ssl.cert')) {
connectionOptions.tls = {
key: fs.readFileSync(config.get('server.ssl.key')),
cert: fs.readFileSync(config.get('server.ssl.cert'))
};
}
server.connection(connectionOptions);
// provide a simple way to expose static directories
server.decorate('server', 'exposeStaticDir', function (routePath, dirPath) {

View file

@ -14,7 +14,7 @@ module.exports = class KbnLogger {
this.dest = process.stdout;
} else {
this.dest = writeStr(config.dest, {
mode: 'a',
flags: 'a',
encoding: 'utf8'
});
}

View file

@ -33,10 +33,17 @@ module.exports = class Plugin {
};
}
async setupConfig() {
let { config } = this.kbnServer;
async readConfig() {
let schema = await this.getConfigSchema(Joi);
this.kbnServer.config.extendSchema(this.id, schema || defaultConfigSchema);
let { config } = this.kbnServer;
config.extendSchema(this.id, schema || defaultConfigSchema);
if (config.get([this.id, 'enabled'])) {
return true;
} else {
config.removeSchema(this.id);
return false;
}
}
async init() {

View file

@ -1,4 +1,4 @@
let _ = require('lodash');
let { get, indexBy } = require('lodash');
let inspect = require('util').inspect;
let PluginApi = require('./PluginApi');
@ -14,22 +14,32 @@ module.exports = class Plugins extends Collection {
}
async new(path) {
var api = new PluginApi(this.kbnServer, path);
let api = new PluginApi(this.kbnServer, path);
let output = [].concat(require(path)(api) || []);
let config = this.kbnServer.config;
if (!output.length) return;
// clear the byIdCache
this[byIdCache] = null;
for (let product of output) {
if (product instanceof api.Plugin) {
this[byIdCache] = null;
this.add(product);
await product.setupConfig();
} else {
throw new TypeError('unexpected plugin export ' + inspect(product));
let plugin = product;
this.add(plugin);
let enabled = await plugin.readConfig();
if (!enabled) this.delete(plugin);
continue;
}
throw new TypeError('unexpected plugin export ' + inspect(product));
}
}
get byId() {
return this[byIdCache] || (this[byIdCache] = _.indexBy([...this], 'id'));
return this[byIdCache] || (this[byIdCache] = indexBy([...this], 'id'));
}
};

View file

@ -7,18 +7,10 @@ module.exports = async function (kbnServer, server, config) {
}
let { plugins } = kbnServer;
let enabledPlugins = {};
// setup config and filter out disabled plugins
for (let plugin of plugins) {
if (config.get([plugin.id, 'enabled'])) {
enabledPlugins[plugin.id] = plugin;
}
}
let path = [];
let initialize = async id => {
let plugin = enabledPlugins[id];
async function initialize(id) {
let plugin = plugins.byId[id];
if (includes(path, id)) {
throw new Error(`circular dependencies found: "${path.concat(id).join(' -> ')}"`);
@ -27,13 +19,10 @@ module.exports = async function (kbnServer, server, config) {
path.push(id);
for (let reqId of plugin.requiredIds) {
if (!enabledPlugins[reqId]) {
if (plugins.byId[reqId]) {
throw new Error(`Requirement "${reqId}" for plugin "${plugin.id}" is disabled.`);
} else {
throw new Error(`Unmet requirement "${reqId}" for plugin "${plugin.id}"`);
}
if (!plugins.byId[reqId]) {
throw new Error(`Unmet requirement "${reqId}" for plugin "${id}"`);
}
await initialize(reqId);
}
@ -42,5 +31,7 @@ module.exports = async function (kbnServer, server, config) {
path.pop();
};
for (let id of keys(enabledPlugins)) await initialize(id);
for (let {id} of plugins) {
await initialize(id);
}
};

View file

@ -53,7 +53,6 @@ module.exports = async (kbnServer, server, config) => {
continue;
}
require(modulePath);
await plugins.new(path);
debug({ tmpl: 'Found plugin at <%= path %>', path: modulePath });
}

View file

@ -13,7 +13,10 @@ class Status extends EventEmitter {
this.on('change', function (previous, previousMsg) {
this.since = new Date();
server.log(['status', name, 'info'], {
var tags = ['status', name];
tags.push(this.state === 'red' ? 'error' : 'info');
server.log(tags, {
tmpl: 'Status changed from <%= prevState %> to <%= state %><% message && print(` - ${message}`) %>',
name: name,
state: this.state,
@ -42,8 +45,14 @@ states.all.forEach(function (state) {
let previous = this.state;
let previousMsg = this.message;
this.state = state.id;
this.error = null;
this.message = message || state.title;
this.state = state.id;
if (message instanceof Error) {
this.error = message;
this.message = message.message;
}
if (previous === this.state && previousMsg === this.message) {
// noop

View file

@ -1,27 +1,12 @@
module.exports = function (kbnServer, server, config) {
var _ = require('lodash');
var Samples = require('./Samples');
var ServerStatus = require('./ServerStatus');
var { join } = require('path');
kbnServer.status = new ServerStatus(kbnServer.server);
kbnServer.metrics = new Samples(60);
if (server.plugins.good) {
server.plugins.good.monitor.on('ops', function (event) {
var port = config.get('server.port');
kbnServer.metrics.add({
rss: event.psmem.rss,
heapTotal: event.psmem.heapTotal,
heapUsed: event.psmem.heapUsed,
load: event.osload,
delay: event.psdelay,
concurrency: _.get(event, ['concurrents', port]),
responseTimeAvg: _.get(event, ['responseTimes', port, 'avg']),
responseTimeMax: _.get(event, ['responseTimes', port, 'max']),
requests: _.get(event, ['requests', port, 'total'], 0)
});
});
kbnServer.mixin(require('./metrics'));
}
server.route({

View file

@ -0,0 +1,27 @@
module.exports = function (kbnServer, server, config) {
var _ = require('lodash');
var Samples = require('./Samples');
let lastReport = Date.now();
kbnServer.metrics = new Samples(12);
server.plugins.good.monitor.on('ops', function (event) {
let now = Date.now();
let secSinceLast = (now - lastReport) / 1000;
lastReport = now;
var port = config.get('server.port');
let requests = _.get(event, ['requests', port, 'total'], 0);
let requestsPerSecond = requests / secSinceLast;
kbnServer.metrics.add({
heapTotal: _.get(event, 'psmem.heapTotal'),
heapUsed: _.get(event, 'psmem.heapUsed'),
load: event.osload,
responseTimeAvg: _.get(event, ['responseTimes', port, 'avg']),
responseTimeMax: _.get(event, ['responseTimes', port, 'max']),
requestsPerSecond: requestsPerSecond
});
});
};

View file

@ -28,13 +28,13 @@ class UiApp {
}
getModules() {
return _([
return _.chain([
this.autoloadOverrides || autoload.require,
this.uiExports.find(_.get(this, 'spec.uses', [])),
])
.flatten()
.uniq()
.push(this.main)
.unshift(this.main)
.value();
}

View file

@ -6,7 +6,14 @@ let fromRoot = require('../utils/fromRoot');
let asRegExp = flow(
escapeRegExp,
function (path) {
return path + '(?:\\.js)?$';
let last = path.slice(-1);
if (last === '/' || last === '\\') {
// match a directory explicitly
return path + '.*';
} else {
// match a directory or files or just the absolute path
return path + '(?:\\.js$|$|\\\\|\\/)?';
}
},
RegExp
);
@ -31,7 +38,10 @@ module.exports = class UiBundlerEnv {
this.pluginInfo = [];
// regular expressions which will prevent webpack from parsing the file
this.noParse = [];
this.noParse = [
/node_modules[\/\\](angular|elasticsearch-browser)[\/\\]/,
/node_modules[\/\\](angular-nvd3|mocha|moment)[\/\\]/
];
// webpack aliases, like require paths, mapping a prefix to a directory
this.aliases = {
@ -44,6 +54,7 @@ module.exports = class UiBundlerEnv {
// webpack loaders map loader configuration to regexps
this.loaders = [];
this.postLoaders = [];
}
consumePlugin(plugin) {
@ -64,6 +75,11 @@ module.exports = class UiBundlerEnv {
for (let loader of arr(spec)) this.addLoader(loader);
};
case 'postLoaders':
return (plugin, spec) => {
for (let loader of arr(spec)) this.addPostLoader(loader);
};
case 'noParse':
return (plugin, spec) => {
for (let re of arr(spec)) this.addNoParse(re);
@ -84,6 +100,10 @@ module.exports = class UiBundlerEnv {
this.loaders.push(loader);
}
addPostLoader(loader) {
this.postLoaders.push(loader);
}
addNoParse(regExp) {
this.noParse.push(regExp);
}
@ -129,7 +149,7 @@ module.exports = class UiBundlerEnv {
if (exports) loader.push(`exports?${exports}`);
if (expose) loader.push(`expose?${expose}`);
if (loader.length) this.loaders.push({ test: asRegExp(path), loader: loader.join('!') });
if (!parse) this.noParse.push(asRegExp(path));
if (!parse) this.addNoParse(path);
}
claim(id, pluginId) {

View file

@ -260,7 +260,8 @@ define(function (require) {
AggConfig.prototype.makeLabel = function () {
if (!this.type) return '';
return this.type.makeLabel(this);
var pre = (_.get(this.vis, 'params.mode') === 'percentage') ? 'Percentage of ' : '';
return pre += this.type.makeLabel(this);
};
AggConfig.prototype.field = function () {

View file

@ -223,7 +223,10 @@ describe('buildHierarchicalData', function () {
type: 'pie',
aggs: [
{ type: 'count', schema: 'metric' },
{ type: 'filters', schema: 'segment', params: {
{
type: 'filters',
schema: 'segment',
params: {
filters: [
{ input: { query: { query_string: { query: '_type:apache' } } } },
{ input: { query: { query_string: { query: '_type:nginx' } } } }
@ -256,7 +259,10 @@ describe('buildHierarchicalData', function () {
type: 'pie',
aggs: [
{ type: 'count', schema: 'metric' },
{ type: 'filters', schema: 'split', params: {
{
type: 'filters',
schema: 'split',
params: {
filters: [
{ input: { query: { query_string: { query: '_type:apache' } } } },
{ input: { query: { query_string: { query: '_type:nginx' } } } }

View file

@ -38,8 +38,7 @@ describe('AggConfig Filters', function () {
expect(filter.range).to.have.property('bytes');
expect(filter.range.bytes).to.have.property('gte', 2048);
expect(filter.range.bytes).to.have.property('lt', 3072);
expect(filter.meta).to.have.property('formattedValue', '2,048');
});
});
});

View file

@ -41,7 +41,7 @@ describe('AggConfig Filters', function () {
expect(filter.range).to.have.property('bytes');
expect(filter.range.bytes).to.have.property('gte', 1024.0);
expect(filter.range.bytes).to.have.property('lt', 2048.0);
expect(filter.meta).to.have.property('formattedValue', '1,024 to 2,048');
});
});
});

View file

@ -5,10 +5,12 @@ define(function (require) {
return function (aggConfig, key) {
var value = parseInt(key, 10);
return buildRangeFilter(aggConfig.params.field, {
gte: value,
lt: value + aggConfig.params.interval
}, aggConfig.vis.indexPattern);
return buildRangeFilter(
aggConfig.params.field,
{gte: value, lt: value + aggConfig.params.interval},
aggConfig.vis.indexPattern,
aggConfig.fieldFormatter()(key)
);
};
};
});

View file

@ -2,11 +2,12 @@ define(function (require) {
return function DateHistogramAggType(timefilter, config, Private) {
var _ = require('lodash');
var moment = require('moment');
var tzDetect = require('jstimezonedetect').jstz;
var BucketAggType = Private(require('ui/agg_types/buckets/_bucket_agg_type'));
var TimeBuckets = Private(require('ui/time_buckets'));
var createFilter = Private(require('ui/agg_types/buckets/create_filter/date_histogram'));
var intervalOptions = Private(require('ui/agg_types/buckets/_interval_options'));
var timeZone = tzDetect.determine().name();
var tzOffset = moment().format('Z');
function getInterval(agg) {
@ -93,7 +94,7 @@ define(function (require) {
var interval = agg.buckets.getInterval();
output.bucketInterval = interval;
output.params.interval = interval.expression;
output.params.time_zone = tzOffset;
output.params.time_zone = timeZone || tzOffset;
var scaleMetrics = interval.scaled && interval.scale < 1;
if (scaleMetrics) {

View file

@ -12,7 +12,7 @@ var parse = _.wrap(require('url').parse, function (parse, path) {
function TabCollection() {
var tabs = null;
var tabs = [];
var specs = null;
var defaults = null;
var activeTab = null;

View file

@ -1,5 +1,5 @@
<kbn-notifications list="notifList"></kbn-notifications>
<div class="content" style="display: none;" chrome-context >
<div class="content" chrome-context >
<nav
ng-style="{ background: chrome.getNavBackground() }"
ng-class="{ show: chrome.getVisible() }"

View file

@ -42,7 +42,6 @@ require('./api/theme')(chrome, internals);
chrome.bootstrap = function () {
chrome.setupAngular();
angular.bootstrap(document, ['kibana']);
$(document.body).children(':not(style-compile)').show();
};
module.exports = chrome;

View file

@ -31,8 +31,9 @@ define(function () {
']',
description: 'Values that define the format used in situations where timebased' +
' data is rendered in order, and formatted timestamps should adapt to the' +
' interval between measurements. Keys are ISO 8601 intervals:' +
' http://en.wikipedia.org/wiki/ISO_8601#Time_intervals'
' interval between measurements. Keys are' +
' <a href="http://en.wikipedia.org/wiki/ISO_8601#Time_intervals" target="_blank">' +
'ISO8601 intervals.</a>'
},
'defaultIndex': {
value: null,
@ -71,8 +72,10 @@ define(function () {
'visualization:tileMap:maxPrecision': {
value: 7,
description: 'The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high, ' +
'12 is the max. Explanation of cell dimensions: http://www.elastic.co/guide/en/elasticsearch/reference/current/' +
'search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator',
'12 is the max. ' +
'<a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/' +
'search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator" target="_blank">' +
'Explanation of cell dimensions.</a>',
},
'csv:separator': {
value: ',',

View file

@ -1,14 +1,16 @@
<th width="1%"></th>
<th ng-if="indexPattern.timeFieldName">
<span>Time <i ng-class="headerClass(indexPattern.timeFieldName)" ng-click="sort(indexPattern.timeFieldName)" tooltip="Sort by time"></i></span>
</th>
<th ng-repeat="name in columns">
<span class="table-header-name">
{{name | shortDots}} <i ng-class="headerClass(name)" ng-click="sort(name)" tooltip="{{tooltip(name)}}" tooltip-append-to-body="1"></i>
</span>
<span class="table-header-move">
<i ng-click="toggleColumn(name)" ng-show="canRemove(name)" class="fa fa-remove" tooltip="Remove column" tooltip-append-to-body="1"></i>
<i ng-click="moveLeft(name)" class="fa fa-angle-double-left" ng-show="!$first" tooltip="Move column to the left" tooltip-append-to-body="1"></i>
<i ng-click="moveRight(name)" class="fa fa-angle-double-right" ng-show="!$last" tooltip="Move column to the right" tooltip-append-to-body="1"></i>
</span>
</th>
<tr>
<th width="1%"></th>
<th ng-if="indexPattern.timeFieldName">
<span>Time <i ng-class="headerClass(indexPattern.timeFieldName)" ng-click="sort(indexPattern.timeFieldName)" tooltip="Sort by time"></i></span>
</th>
<th ng-repeat="name in columns">
<span class="table-header-name">
{{name | shortDots}} <i ng-class="headerClass(name)" ng-click="sort(name)" tooltip="{{tooltip(name)}}" tooltip-append-to-body="1"></i>
</span>
<span class="table-header-move">
<i ng-click="toggleColumn(name)" ng-show="canRemove(name)" class="fa fa-remove" tooltip="Remove column" tooltip-append-to-body="1"></i>
<i ng-click="moveLeft(name)" class="fa fa-angle-double-left" ng-show="!$first" tooltip="Move column to the left" tooltip-append-to-body="1"></i>
<i ng-click="moveRight(name)" class="fa fa-angle-double-right" ng-show="!$last" tooltip="Move column to the right" tooltip-append-to-body="1"></i>
</span>
</th>
</tr>

View file

@ -2,7 +2,6 @@
doc-table {
overflow: auto;
margin: 5px;
flex: 1 1 100%;
.discover-table-datafield {

View file

@ -62,6 +62,7 @@
readonly
ui-ace="{
useWrapMode: true,
onLoad: aceLoaded,
advanced: {
highlightActiveLine: false
},

View file

@ -17,24 +17,31 @@ define(function (require) {
filter: '=?',
columns: '=?'
},
link: function ($scope, $el, attr) {
// If a field isn't in the mapping, use this
$scope.mode = 'table';
$scope.mapping = $scope.indexPattern.fields.byName;
$scope.flattened = $scope.indexPattern.flattenHit($scope.hit);
$scope.hitJson = angular.toJson($scope.hit, true);
$scope.formatted = $scope.indexPattern.formatHit($scope.hit);
$scope.fields = _.keys($scope.flattened).sort();
link: {
pre($scope) {
$scope.aceLoaded = (editor) => {
editor.$blockScrolling = Infinity;
};
},
$scope.toggleColumn = function (fieldName) {
_.toggleInOut($scope.columns, fieldName);
};
post($scope, $el, attr) {
// If a field isn't in the mapping, use this
$scope.mode = 'table';
$scope.mapping = $scope.indexPattern.fields.byName;
$scope.flattened = $scope.indexPattern.flattenHit($scope.hit);
$scope.hitJson = angular.toJson($scope.hit, true);
$scope.formatted = $scope.indexPattern.formatHit($scope.hit);
$scope.fields = _.keys($scope.flattened).sort();
$scope.showArrayInObjectsWarning = function (row, field) {
var value = $scope.flattened[field];
return _.isArray(value) && typeof value[0] === 'object';
};
$scope.toggleColumn = function (fieldName) {
_.toggleInOut($scope.columns, fieldName);
};
$scope.showArrayInObjectsWarning = function (row, field) {
var value = $scope.flattened[field];
return _.isArray(value) && typeof value[0] === 'object';
};
}
}
};
});

View file

@ -42,5 +42,26 @@ describe('Filter Bar Directive', function () {
$rootScope.$apply();
});
it('should return a value for a range/histogram filter from a scripted field', (done) => {
let filter = {
meta: {
index: 'logstash-*',
formattedValue: '1,000.00 to 2,000.00',
field: 'script number'
},
script: {
params: {
gte: 1000,
lt: 2000,
value: '>=1,000.00 <2,000.00'
}
}
};
mapScript(filter).then((result) => {
expect(result).to.have.property('value', filter.meta.formattedValue);
done();
});
$rootScope.$apply();
});
});
});

View file

@ -0,0 +1,20 @@
describe('Date Format', function () {
var fieldFormats;
var expect = require('expect.js');
var ngMock = require('ngMock');
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
fieldFormats = Private(require('ui/registry/field_formats'));
}));
it('decoding an undefined or null date should return an empty string', function () {
var DateFormat = fieldFormats.getType('date');
var date = new DateFormat({
pattern: 'dd-MM-yyyy'
});
expect(date.convert(null)).to.be('');
expect(date.convert(undefined)).to.be('');
});
});

View file

@ -4,4 +4,5 @@ describe('Stringify Component', function () {
require('./_source');
require('./_string');
require('./_url');
require('./_date');
});

View file

@ -45,6 +45,9 @@ define(function (require) {
if (this._memoizedPattern !== pattern) {
this._memoizedPattern = pattern;
this._memoizedConverter = _.memoize(function converter(val) {
if (val === null || val === undefined) {
return '';
}
return moment(val).format(pattern);
});
}

View file

@ -70,11 +70,11 @@ module.exports = function (grunt) {
grunt.log.ok(`downloading ${platform.name} - ${mb} mb`);
};
grunt.registerTask('_build:downloadNodes:start', function () {
grunt.registerTask('_build:downloadNodeBuilds:start', function () {
map(platforms, start).nodeify(this.async());
});
grunt.registerTask('_build:downloadNodes:finish', function () {
grunt.registerTask('_build:downloadNodeBuilds:finish', function () {
map(activeDownloads, async (platform) => {
await platform.downloadPromise;
grunt.log.ok(`${platform.name} download complete`);

View file

@ -2,22 +2,24 @@ module.exports = function (grunt) {
let { flatten } = require('lodash');
grunt.registerTask('build', flatten([
'_build:shrinkwrap:ensureExists:true',
'_build:getProps',
'clean:build',
'clean:target',
'_build:downloadNodes:start',
'_build:downloadNodeBuilds:start',
'copy:devSource',
'babel:build',
'_build:cliIndex',
'_build:installedPlugins',
'_build:packageJson',
'_build:readme',
'_build:shrinkwrap:copyToBuild',
'_build:shrinkwrap:cleanup',
'_build:installNpmDeps',
'clean:testsFromModules',
'clean:deepModules',
'run:optimizeBuild',
'stop:optimizeBuild',
'_build:downloadNodes:finish',
'_build:downloadNodeBuilds:finish',
'_build:versionedLinks',
'_build:archives',
!grunt.option('os-packages') ? [] : [

View file

@ -2,7 +2,6 @@ module.exports = function (grunt) {
let { defaults } = require('lodash');
let pkg = grunt.config.get('pkg');
let deepModules = grunt.config.get('deepModules');
grunt.registerTask('_build:packageJson', function () {
@ -18,7 +17,7 @@ module.exports = function (grunt) {
sha: grunt.config.get('buildSha')
},
repository: pkg.repository,
dependencies: defaults({}, pkg.dependencies, deepModules)
dependencies: pkg.dependencies
}, null, ' ')
);
});

41
tasks/build/shrinkwrap.js Normal file
View file

@ -0,0 +1,41 @@
module.exports = function (grunt) {
let { config } = grunt;
let { statSync } = require('fs');
let { join } = require('path');
let exec = (...args) => require('../utils/exec')(...args, { cwd: config.get('root') });
let newFiles = [];
let shrinkwrapFile = join(config.get('root'), 'npm-shrinkwrap.json');
grunt.registerTask('_build:shrinkwrap:ensureExists', function (createIfMissing) {
try {
statSync(shrinkwrapFile);
} catch (e) {
if (e.code !== 'ENOENT') throw e;
if (createIfMissing) {
exec('npm', ['shrinkwrap', '--dev', '--logLevel', 'error']);
newFiles.push(shrinkwrapFile);
}
else grunt.fail.warn('Releases require an npm-shrinkwrap.json file to exist');
}
});
grunt.registerTask('_build:shrinkwrap:copyToBuild', function () {
// this.requires(['_build:shrinkwrap:ensureExists', 'copy:devSource']);
// backup shrinkwrap and copy to build
exec('cp', ['npm-shrinkwrap.json', 'npm-shrinkwrap.dev']);
exec('cp', ['npm-shrinkwrap.json', join(config.get('root'), 'build', 'kibana', 'npm-shrinkwrap.build.json')]);
// create shrinkwrap without dev dependencies and copy to build
exec('npm', ['shrinkwrap', '--logLevel', 'error']);
exec('cp', ['npm-shrinkwrap.json', join(config.get('root'), 'build', 'kibana', 'npm-shrinkwrap.json')]);
// restore the dev shrinkwrap
exec('mv', ['npm-shrinkwrap.dev', 'npm-shrinkwrap.json']);
});
grunt.registerTask('_build:shrinkwrap:cleanup', function () {
if (newFiles.length) exec('rm', newFiles.splice(0));
});
};

View file

@ -3,9 +3,7 @@ let babelOptions = require('requirefrom')('src')('optimize/babelOptions');
module.exports = {
build: {
options: defaults({
optional: ['runtime']
}, babelOptions),
options: babelOptions.node,
src: [
'build/kibana/**/*.js',
'!**/public/**',

View file

@ -1,9 +1,7 @@
module.exports = function (grunt) {
let modules = Object.keys(grunt.config.get('deepModules'));
return {
build: 'build',
target: 'target',
testsFromModules: 'build/kibana/node_modules/**/*test*/**',
deepModules: 'build/kibana/node_modules/*/node_modules/**/{' + modules.join(',') + '}/**'
testsFromModules: 'build/kibana/node_modules/**/*test*/**'
};
};

View file

@ -5,6 +5,7 @@ module.exports = function (grunt) {
src: [
'src/**',
'bin/**',
'webpackShims/**',
'config/kibana.yml',
'!src/**/__tests__/**',
'!src/testUtils/**',

View file

@ -1,12 +1,54 @@
module.exports = function (grunt) {
return {
unit: {
configFile: 'karma.conf.js',
options: {
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
captureTimeout: 30000,
browserNoActivityTimeout: 120000,
frameworks: ['mocha'],
port: 9876,
colors: true,
logLevel: grunt.option('debug') || grunt.option('verbose') ? 'DEBUG' : 'INFO',
autoWatch: false,
browsers: ['<%= karmaBrowser %>'],
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'growl'],
// list of files / patterns to load in the browser
files: [
'http://localhost:5610/bundles/commons.bundle.js',
'http://localhost:5610/bundles/tests.bundle.js',
'http://localhost:5610/bundles/commons.style.css',
'http://localhost:5610/bundles/tests.style.css'
],
proxies: {
'/tests/': 'http://localhost:5610/tests/',
'/bundles/': 'http://localhost:5610/bundles/'
},
client: {
mocha: {
reporter: 'html', // change Karma's debug.html to the mocha web reporter
timeout: 10000,
slow: 5000
}
}
},
dev: { singleRun: false },
unit: { singleRun: true },
coverage: {
singleRun: true,
reporters: 'dots',
browsers: [
'<%= karmaBrowser %>'
]
reporters: ['coverage'],
coverageReporter: {
reporters: [
{ type: 'html', dir: 'coverage' },
{ type: 'text-summary' },
]
}
}
};
};

View file

@ -45,7 +45,6 @@ module.exports = function (grunt) {
'inherits@1.0.0': ['ISC'],
'jsonpointer@1.1.0': ['MIT'],
'leaflet@0.7.2': ['BSD-2-Clause'],
'moment-timezone@0.0.6': ['MIT'],
'Nonsense@0.1.2': ['Public-Domain'],
'pkginfo@0.2.3': ['MIT'],
'uglify-js@2.2.5': ['BSD'],

View file

@ -1,4 +1,5 @@
module.exports = function (grunt) {
let platform = require('os').platform();
let {resolve} = require('path');
let root = p => resolve(__dirname, '../../', p);
@ -10,12 +11,51 @@ module.exports = function (grunt) {
quiet: false,
failOnError: false
},
cmd: './bin/kibana',
cmd: /^win/.test(platform) ? '.\\bin\\kibana.bat' : './bin/kibana',
args: [
'--server.port=5610',
'--env.name=development',
'--logging.json=false',
'--optimize.tests=true',
'--optimize.lazy=false'
'--optimize.bundleFilter=tests',
'--plugins.initialize=false'
]
},
testCoverageServer: {
options: {
wait: false,
ready: /Server running/,
quiet: false,
failOnError: false
},
cmd: /^win/.test(platform) ? '.\\bin\\kibana.bat' : './bin/kibana',
args: [
'--server.port=5610',
'--env.name=development',
'--logging.json=false',
'--optimize.bundleFilter=tests',
'--plugins.initialize=false',
'--testsBundle.instrument=true'
]
},
devTestServer: {
options: {
wait: false,
ready: /Server running/,
quiet: false,
failOnError: false
},
cmd: './bin/kibana',
args: [
'--dev',
'--no-watch',
'--server.port=5610',
'--optimize.lazyPort=5611',
'--optimize.lazyPrebuild=true',
'--logging.json=false',
'--optimize.bundleFilter=tests',
'--plugins.initialize=false'
]
},

View file

@ -1,8 +1,10 @@
module.exports = function (grunt) {
let { compact } = require('lodash');
grunt.registerTask('jenkins', 'Jenkins build script', [
grunt.registerTask('jenkins', 'Jenkins build script', compact([
'esvm:dev',
'test'
]);
'test',
process.env.JOB_NAME === 'kibana_core' ? 'build' : null
]));
};

View file

@ -1,58 +0,0 @@
module.exports = function (grunt) {
var maybeStartServer = function (options) {
return function () {
var http = require('http');
var opts = {
method: 'HEAD',
path: '/api/status',
host: 'localhost',
port: options.port
};
grunt.log.debug('checking for server', JSON.stringify(opts));
var req = http.request(opts);
var done = (function (cb) {
return function (res) {
req.removeListener('error', onError);
req.removeListener('response', onResponse);
if (res) res.socket.destroy();
cb();
};
}(this.async()));
function onResponse(res) {
grunt.log.debug('Server responded with', res.statusCode);
if (res.statusCode === 200 && res.headers['x-app-name'] === 'kibana') {
grunt.log.ok('Kibana server already started on port', options.port);
} else {
grunt.log.error('Another server is already running on port', options.port);
process.exit(1); // eslint-disable-line no-process-exit
}
done(res);
}
function onError(err) {
if (err.code !== 'ECONNREFUSED') {
grunt.log.error('Kibana server check failed', err);
}
grunt.config.set(options.name, true);
grunt.task.run(options.tasks);
done();
}
req.on('error', onError);
req.on('response', onResponse);
req.end();
};
};
grunt.registerTask('maybeStartTestServer', maybeStartServer({
name: 'kibana-server',
port: grunt.option('port') || 5601,
tasks: ['run:testServer']
}));
};

View file

@ -1,9 +1,9 @@
module.exports = function (grunt) {
var readline = require('readline');
// build, then zip and upload to s3
grunt.registerTask('release', [
'_build:shrinkwrap:ensureExists',
'_release:confirmUpload',
'_release:loadS3Config',
'build',

View file

@ -1,29 +1,27 @@
var _ = require('lodash');
module.exports = function (grunt) {
grunt.registerTask('test', function () {
if (grunt.option('quick')) {
grunt.task.run('quick-test');
return;
}
grunt.registerTask('test:server', [ 'simplemocha:all' ]);
grunt.registerTask('test:browser', [ 'run:testServer', 'karma:unit' ]);
grunt.registerTask('test:coverage', [ 'run:testCoverageServer', 'karma:coverage' ]);
grunt.registerTask('test:quick', [
'test:server',
'test:browser'
]);
grunt.registerTask('test:dev', [
'run:devTestServer',
'karma:dev'
]);
grunt.registerTask('test', function (subTask) {
if (subTask) grunt.fail.fatal(`invalid task "test:${subTask}"`);
grunt.task.run(_.compact([
'eslint:source',
'simplemocha:all',
'maybeStartTestServer',
'karma:unit'
!grunt.option('quick') && 'eslint:source',
'test:quick'
]));
});
grunt.registerTask('quick-test', function () {
grunt.task.run([
'simplemocha:all',
'maybeStartTestServer',
'karma:unit'
]);
});
grunt.registerTask('test:watch', [
'maybeStartTestServer',
'watch:test'
]);
grunt.registerTask('quick-test', ['test:quick']); // historical alias
};

5
webpackShims/angular-nvd3.js vendored Normal file
View file

@ -0,0 +1,5 @@
require('d3');
require('@spalger/nvd3/build/nv.d3.css');
require('@spalger/nvd3/build/nv.d3.js');
require('@spalger/angular-nvd3/dist/angular-nvd3.min.js');
module.exports = window.nv;

Some files were not shown because too many files have changed in this diff Show more