mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Merge branch 'master' of github.com:elastic/kibana into migrate/timelion
This commit is contained in:
commit
589ea55f7e
26 changed files with 526 additions and 251 deletions
|
@ -1,8 +1,8 @@
|
|||
[[setup-repositories]]
|
||||
=== Install Kibana using a Linux Package Manager
|
||||
=== Installing Kibana with apt and yum
|
||||
|
||||
Binary packages for Kibana are available for Unix distributions that support the `apt` and `yum` tools. We also have
|
||||
repositories available for APT and YUM based distributions.
|
||||
Binary packages for Kibana are available for Unix distributions that support the `apt` and `yum` tools.
|
||||
We also have repositories available for APT and YUM based distributions.
|
||||
|
||||
NOTE: Since the packages are created as part of the Kibana build, source packages are not available.
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ The {version} release of Kibana requires Elasticsearch {esversion} or later.
|
|||
* {k4issue}6531[Issue 6531]: Improved warning for URL lengths that approach browser limits.
|
||||
* {k4issue}6602[Issue 6602]: Improves dark theme support.
|
||||
* {k4issue}6791[Issue 6791]: Enables composition of custom user toast notifications in Advanced Settings.
|
||||
* {k4pull}8014[Pull Request 8014]: Changes the UUID config setting from `uuid` to `server.uuid`, and puts UUID storage into data file instead of Elasticsearch.
|
||||
|
||||
[float]
|
||||
[[bugfixes]]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[[setup]]
|
||||
== Installing Kibana
|
||||
You can set up Kibana and start exploring your Elasticsearch indices in minutes.
|
||||
== Setting Up Kibana
|
||||
You can install Kibana and start exploring your Elasticsearch indices in minutes.
|
||||
All you need is:
|
||||
|
||||
* Elasticsearch {esversion}
|
||||
|
@ -9,114 +9,70 @@ All you need is:
|
|||
** URL of the Elasticsearch instance you want to connect to.
|
||||
** Which Elasticsearch indices you want to search.
|
||||
|
||||
NOTE: If your Elasticsearch installation is protected by http://www.elastic.co/overview/shield/[{scyld}], see
|
||||
{shield}/kibana.html#using-kibana4-with-shield[{scyld} with Kibana] for additional setup instructions.
|
||||
|
||||
=== Upgrading Kibana
|
||||
|
||||
Your existing Kibana version is generally compatible with the next minor version release of Elasticsearch.
|
||||
This means you should upgrade your Elasticsearch cluster(s) before or at the same time as Kibana.
|
||||
We cannot guarantee compatibility between major version releases so in those cases both Elasticsearch and Kibana must be upgraded together.
|
||||
|
||||
To upgrade Kibana:
|
||||
|
||||
. Create a https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html[snapshot] of the existing `.kibana` index.
|
||||
. Back up the `kibana.yml` configuration file.
|
||||
. Take note of the Kibana plugins that are installed:
|
||||
* `bin/kibana plugin --list` on 4.x versions of Kibana.
|
||||
* `bin/kibana-plugin list` on 5.x versions of Kibana.
|
||||
. To upgrade from an Archive File:
|
||||
.. Extract the new version of Kibana into a different directory. See steps below.
|
||||
.. Migrate any custom configuration from your old kibana.yml to your new one
|
||||
.. Follow other steps below to complete the new installation.
|
||||
.. Once the new version is fully configured and working with required plugins, remove the previous version of Kibana
|
||||
. To upgrade using a Linux Package Manager:
|
||||
.. Uninstall the existing Kibana package: `apt-get remove kibana` or `yum remove kibana`
|
||||
.. Install the new Kibana package. There have been some installer issues between various version of Kibana so the uninstall and install process is safer than an upgrade.
|
||||
|
||||
|
||||
[float]
|
||||
[[install]]
|
||||
=== Install and Start Kibana from an Archive File
|
||||
=== Install Kibana
|
||||
To install and start Kibana:
|
||||
|
||||
To get Kibana up and running:
|
||||
|
||||
. Download the https://www.elastic.co/downloads/kibana[Kibana {version} binary package] for your platform.
|
||||
. Download the https://www.elastic.co/downloads/kibana[Kibana 4 binary package] for your platform.
|
||||
. Extract the `.zip` or `tar.gz` archive file.
|
||||
. If you're upgrading, migrate any configuration changes from the previous `kibana.yml` to the new version.
|
||||
. Install Kibana plugins (optional).
|
||||
. Run Kibana from the install directory: `bin/kibana` (Linux/MacOSX) or `bin\kibana.bat` (Windows).
|
||||
|
||||
On Unix, you can instead run the package manager suited for your distribution.
|
||||
|
||||
////
|
||||
[float]
|
||||
include::kibana-repositories.asciidoc[]
|
||||
////
|
||||
|
||||
That's it! Kibana is now running on port 5601.
|
||||
|
||||
[float]
|
||||
[[kibana-dynamic-mapping]]
|
||||
==== Kibana and Elasticsearch Dynamic Mapping
|
||||
By default, Elasticsearch enables {ref}dynamic-mapping.html[dynamic mapping] for fields. Kibana needs dynamic mapping
|
||||
to use fields in visualizations correctly, as well as to manage the `.kibana` index where saved searches,
|
||||
visualizations, and dashboards are stored.
|
||||
On Unix, you can also install Kibana using the package manager suited for your distribution. For more
|
||||
information, see <<setup-repositories, Installing Kibana with apt and yum>>.
|
||||
|
||||
If your Elasticsearch use case requires you to disable dynamic mapping, you need to manually provide mappings for
|
||||
fields that Kibana uses to create visualizations. You also need to manually enable dynamic mapping for the `.kibana`
|
||||
index.
|
||||
|
||||
The following procedure assumes that the `.kibana` index does not already exist in Elasticsearch and that the
|
||||
`index.mapper.dynamic` setting in `elasticsearch.yml` is set to `false`:
|
||||
|
||||
. Start Elasticsearch.
|
||||
. Create the `.kibana` index with dynamic mapping enabled just for that index:
|
||||
+
|
||||
[source,shell]
|
||||
PUT .kibana
|
||||
{
|
||||
"index.mapper.dynamic": true
|
||||
}
|
||||
+
|
||||
. Start Kibana and navigate to the web UI and verify that there are no error messages related to dynamic mapping.
|
||||
IMPORTANT: If your Elasticsearch installation is protected by http://www.elastic.co/overview/shield/[Shield]
|
||||
see {shield}/kibana.html#using-kibana4-with-shield[Using Kibana with Shield] for additional setup
|
||||
instructions.
|
||||
|
||||
[float]
|
||||
[[connect]]
|
||||
=== Connect Kibana with Elasticsearch
|
||||
Before you can start using Kibana, you need to tell it which Elasticsearch indices you want to explore. The first time
|
||||
you access Kibana, you are prompted to define an _index pattern_ that matches the name of one or more of your indices.
|
||||
That's it. That's all you need to configure to start using Kibana. You can add index patterns at any time from the
|
||||
<<settings-create-pattern,Settings tab>>.
|
||||
Before you can start using Kibana, you need to tell it which Elasticsearch indices you want to explore.
|
||||
The first time you access Kibana, you are prompted to define an _index pattern_ that matches the name of
|
||||
one or more of your indices. That's it. That's all you need to configure to start using Kibana. You can
|
||||
add index patterns at any time from the <<settings-create-pattern,Settings tab>>.
|
||||
|
||||
TIP: By default, Kibana connects to the Elasticsearch instance running on `localhost`. To connect to a different
|
||||
Elasticsearch instance, modify the Elasticsearch URL in the `kibana.yml` configuration file and restart Kibana. For
|
||||
information about using Kibana with your production nodes, see <<production>>.
|
||||
TIP: By default, Kibana connects to the Elasticsearch instance running on `localhost`. To connect to a
|
||||
different Elasticsearch instance, modify the Elasticsearch URL in the `kibana.yml` configuration file and
|
||||
restart Kibana. Forninformation about using Kibana with your production nodes, see <<production>>.
|
||||
|
||||
To configure the Elasticsearch indices you want to access with Kibana:
|
||||
|
||||
. Point your browser at port 5601 to access the Kibana UI. For example, `localhost:5601` or `http://YOURDOMAIN.com:5601`.
|
||||
. Point your browser at port 5601 to access the Kibana UI. For example, `localhost:5601` or
|
||||
`http://YOURDOMAIN.com:5601`.
|
||||
+
|
||||
image:images/Start-Page.png[Kibana start page]
|
||||
+
|
||||
. Specify an index pattern that matches the name of one or more of your Elasticsearch indices. By default, Kibana
|
||||
guesses that you're working with data being fed into Elasticsearch by Logstash. If that's the case, you can use the
|
||||
default `logstash-*` as your index pattern. The asterisk (*) matches zero or more characters in an index's name. If
|
||||
your Elasticsearch indices follow some other naming convention, enter an appropriate pattern. The "pattern" can also
|
||||
simply be the name of a single index.
|
||||
. Select the index field that contains the timestamp that you want to use to perform time-based comparisons. Kibana
|
||||
reads the index mapping to list all of the fields that contain a timestamp. If your index doesn't have time-based data,
|
||||
disable the *Index contains time-based events* option.
|
||||
. Specify an index pattern that matches the name of one or more of your Elasticsearch indices. By default,
|
||||
Kibana guesses that you're working with data being fed into Elasticsearch by Logstash. If that's the case,
|
||||
you can use the default `logstash-*` as your index pattern. The asterisk (*) matches zero or more
|
||||
characters in an index's name. If your Elasticsearch indices follow some other naming convention, enter
|
||||
an appropriate pattern. The "pattern" can also simply be the name of a single index.
|
||||
. Select the index field that contains the timestamp that you want to use to perform time-based
|
||||
comparisons. Kibana reads the index mapping to list all of the fields that contain a timestamp. If your
|
||||
index doesn't have time-based data, disable the *Index contains time-based events* option.
|
||||
+
|
||||
WARNING: Using event times to create index names is *deprecated* in this release of Kibana. Starting in the 2.1
|
||||
release, Elasticsearch includes sophisticated date parsing APIs that Kibana uses to determine date information,
|
||||
removing the need to specify dates in the index pattern name.
|
||||
WARNING: Using event times to create index names is *deprecated* in this release of Kibana. Support for
|
||||
this functionality will be removed entirely in the next major Kibana release. Elasticsearch 2.1 includes
|
||||
sophisticated date parsing APIs that Kibana uses to determine date information, removing the need to
|
||||
specify dates in the index pattern name.
|
||||
+
|
||||
. Click *Create* to add the index pattern. This first pattern is automatically configured as the default.
|
||||
When you have more than one index pattern, you can designate which one to use as the default from *Settings > Indices*.
|
||||
When you have more than one index pattern, you can designate which one to use as the default from
|
||||
*Settings > Indices*.
|
||||
|
||||
All done! Kibana is now connected to your Elasticsearch data. Kibana displays a read-only list of fields configured for
|
||||
the matching index.
|
||||
All done! Kibana is now connected to your Elasticsearch data. Kibana displays a read-only list of fields
|
||||
configured for the matching index.
|
||||
|
||||
NOTE: Kibana relies on dynamic mapping to use fields in visualizations and manage the
|
||||
`.kibana` index. If you have disabled dynamic mapping, you need to manually provide
|
||||
mappings for the fields that Kibana uses to create visualizations. For more information, see
|
||||
<<kibana-dynamic-mapping, Kibana and Elasticsearch Dynamic Mapping>>.
|
||||
|
||||
[float]
|
||||
[[explore]]
|
||||
|
@ -127,5 +83,60 @@ You're ready to dive in to your data:
|
|||
* Chart and map your data from the <<visualize, Visualize>> page.
|
||||
* Create and view custom dashboards from the <<dashboard, Dashboard>> page.
|
||||
|
||||
For a tutorial that explores these core Kibana concepts, take a look at the <<getting-started, Getting
|
||||
Started>> page.
|
||||
For a step-by-step introduction to these core Kibana concepts, see the <<getting-started,
|
||||
Getting Started>> tutorial.
|
||||
|
||||
[float]
|
||||
[[kibana-dynamic-mapping]]
|
||||
=== Kibana and Elasticsearch Dynamic Mapping
|
||||
By default, Elasticsearch enables {ref}dynamic-mapping.html[dynamic mapping] for fields. Kibana needs
|
||||
dynamic mapping to use fields in visualizations correctly, as well as to manage the `.kibana` index
|
||||
where saved searches, visualizations, and dashboards are stored.
|
||||
|
||||
If your Elasticsearch use case requires you to disable dynamic mapping, you need to manually provide
|
||||
mappings for fields that Kibana uses to create visualizations. You also need to manually enable dynamic
|
||||
mapping for the `.kibana` index.
|
||||
|
||||
The following procedure assumes that the `.kibana` index does not already exist in Elasticsearch and
|
||||
that the `index.mapper.dynamic` setting in `elasticsearch.yml` is set to `false`:
|
||||
|
||||
. Start Elasticsearch.
|
||||
. Create the `.kibana` index with dynamic mapping enabled just for that index:
|
||||
+
|
||||
[source,shell]
|
||||
PUT .kibana
|
||||
{
|
||||
"index.mapper.dynamic": true
|
||||
}
|
||||
+
|
||||
. Start Kibana and navigate to the web UI and verify that there are no error messages related to dynamic
|
||||
mapping.
|
||||
|
||||
include::kibana-repositories.asciidoc[]
|
||||
|
||||
[[upgrading-kibana]]
|
||||
=== Upgrading Kibana
|
||||
|
||||
Your existing Kibana version is generally compatible with the next minor version release of Elasticsearch.
|
||||
This means you should upgrade your Elasticsearch cluster(s) before or at the same time as Kibana.
|
||||
We cannot guarantee compatibility between major version releases so in those cases both Elasticsearch and
|
||||
Kibana must be upgraded together.
|
||||
|
||||
To upgrade Kibana:
|
||||
|
||||
. Create a https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html[snapshot]
|
||||
of the existing `.kibana` index.
|
||||
. Back up the `kibana.yml` configuration file.
|
||||
. Take note of the Kibana plugins that are installed:
|
||||
* `bin/kibana plugin --list` on 4.x versions of Kibana.
|
||||
* `bin/kibana-plugin list` on 5.x versions of Kibana.
|
||||
. To upgrade from an Archive File:
|
||||
.. Extract the new version of Kibana into a different directory. See steps below.
|
||||
.. Migrate any custom configuration from your old kibana.yml to your new one
|
||||
.. Follow other steps below to complete the new installation.
|
||||
.. Once the new version is fully configured and working with required plugins, remove the previous version
|
||||
of Kibana
|
||||
. To upgrade using a Linux Package Manager:
|
||||
.. Uninstall the existing Kibana package: `apt-get remove kibana` or `yum remove kibana`
|
||||
.. Install the new Kibana package. There have been some installer issues between various version of
|
||||
Kibana so the uninstall and install process is safer than an upgrade.
|
|
@ -5,7 +5,6 @@ import exposeClient from './expose_client';
|
|||
import migrateConfig from './migrate_config';
|
||||
import createKibanaIndex from './create_kibana_index';
|
||||
import checkEsVersion from './check_es_version';
|
||||
import manageUuid from './manage_uuid';
|
||||
const NoConnections = elasticsearch.errors.NoConnections;
|
||||
import util from 'util';
|
||||
const format = util.format;
|
||||
|
@ -19,7 +18,6 @@ const REQUEST_DELAY = 2500;
|
|||
module.exports = function (plugin, server) {
|
||||
const config = server.config();
|
||||
const client = server.plugins.elasticsearch.client;
|
||||
const uuidManagement = manageUuid(server);
|
||||
|
||||
plugin.status.yellow('Waiting for Elasticsearch');
|
||||
|
||||
|
@ -89,7 +87,6 @@ module.exports = function (plugin, server) {
|
|||
return waitForPong()
|
||||
.then(_.partial(checkEsVersion, server))
|
||||
.then(waitForShards)
|
||||
.then(uuidManagement)
|
||||
.then(setGreenStatus)
|
||||
.then(_.partial(migrateConfig, server))
|
||||
.catch(err => plugin.status.red(err));
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
import uuid from 'node-uuid';
|
||||
import { hostname } from 'os';
|
||||
const serverHostname = hostname();
|
||||
|
||||
/* Handle different scenarios:
|
||||
* - config uuid exists, data uuid exists and matches
|
||||
* - nothing to do
|
||||
* - config uuid missing, data uuid exists
|
||||
* - set uuid from data as config uuid
|
||||
* - config uuid exists, data uuid exists but mismatches
|
||||
* - update data uuid with config uuid
|
||||
* - config uuid missing, data uuid missing
|
||||
* - generate new uuid, set in config and insert in data
|
||||
* ("config uuid" = uuid in server.config,
|
||||
* "data uuid" = uuid in .kibana index)
|
||||
*/
|
||||
export default function manageUuid(server) {
|
||||
const TYPE = 'server';
|
||||
const config = server.config();
|
||||
const serverPort = server.info.port;
|
||||
const client = server.plugins.elasticsearch.client;
|
||||
|
||||
return function uuidManagement() {
|
||||
const fieldId = `${serverHostname}-${serverPort}`;
|
||||
const kibanaIndex = config.get('kibana.index');
|
||||
let kibanaUuid = config.get('uuid');
|
||||
|
||||
function logToServer(msg) {
|
||||
server.log(['server', 'uuid', fieldId], msg);
|
||||
}
|
||||
|
||||
return client.get({
|
||||
index: kibanaIndex,
|
||||
ignore: [404],
|
||||
type: TYPE,
|
||||
id: fieldId
|
||||
}).then(result => {
|
||||
if (result.found) {
|
||||
if (kibanaUuid === result._source.uuid) {
|
||||
// config uuid exists, data uuid exists and matches
|
||||
logToServer(`Kibana instance UUID: ${kibanaUuid}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!kibanaUuid) {
|
||||
// config uuid missing, data uuid exists
|
||||
kibanaUuid = result._source.uuid;
|
||||
logToServer(`Resuming persistent Kibana instance UUID: ${kibanaUuid}`);
|
||||
config.set('uuid', kibanaUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kibanaUuid !== result._source.uuid) {
|
||||
// config uuid exists, data uuid exists but mismatches
|
||||
logToServer(`Updating Kibana instance UUID to: ${kibanaUuid} (was: ${result._source.uuid})`);
|
||||
return client.update({
|
||||
index: kibanaIndex,
|
||||
type: TYPE,
|
||||
id: fieldId,
|
||||
body: { doc: { uuid: kibanaUuid } }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// data uuid missing
|
||||
if (!kibanaUuid) {
|
||||
// config uuid missing
|
||||
kibanaUuid = uuid.v4();
|
||||
config.set('uuid', kibanaUuid);
|
||||
}
|
||||
|
||||
logToServer(`Setting new Kibana instance UUID: ${kibanaUuid}`);
|
||||
return client.index({
|
||||
index: kibanaIndex,
|
||||
type: TYPE,
|
||||
id: fieldId,
|
||||
body: { uuid: kibanaUuid }
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import manageUuid from './server/lib/manage_uuid';
|
||||
import ingest from './server/routes/api/ingest';
|
||||
import search from './server/routes/api/search';
|
||||
import settings from './server/routes/api/settings';
|
||||
import scripts from './server/routes/api/scripts';
|
||||
import * as systemApi from './server/lib/system_api';
|
||||
|
||||
module.exports = function (kibana) {
|
||||
const kbnBaseUrl = '/app/kibana';
|
||||
|
@ -84,10 +86,15 @@ module.exports = function (kibana) {
|
|||
},
|
||||
|
||||
init: function (server, options) {
|
||||
// uuid
|
||||
manageUuid(server);
|
||||
// routes
|
||||
ingest(server);
|
||||
search(server);
|
||||
settings(server);
|
||||
scripts(server);
|
||||
|
||||
server.expose('systemApi', systemApi);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -13,18 +13,13 @@ import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logsta
|
|||
|
||||
let $parentScope;
|
||||
let $scope;
|
||||
let config;
|
||||
let hits;
|
||||
let indexPattern;
|
||||
let indexPatternList;
|
||||
let shortDotsValue;
|
||||
|
||||
// Sets up the directive, take an element, and a list of properties to attach to the parent scope.
|
||||
const init = function ($elem, props) {
|
||||
ngMock.inject(function ($rootScope, $compile, $timeout, _config_) {
|
||||
shortDotsValue = _config_.get('shortDots:enable');
|
||||
config = _config_;
|
||||
config.set('shortDots:enable', false);
|
||||
ngMock.inject(function ($rootScope, $compile, $timeout) {
|
||||
$parentScope = $rootScope;
|
||||
_.assign($parentScope, props);
|
||||
$compile($elem)($parentScope);
|
||||
|
@ -39,7 +34,6 @@ const init = function ($elem, props) {
|
|||
const destroy = function () {
|
||||
$scope.$destroy();
|
||||
$parentScope.$destroy();
|
||||
config.set('shortDots:enable', shortDotsValue);
|
||||
};
|
||||
|
||||
describe('discover field chooser directives', function () {
|
||||
|
@ -56,7 +50,21 @@ describe('discover field chooser directives', function () {
|
|||
'</disc-field-chooser>'
|
||||
);
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.module('kibana', ($provide) => {
|
||||
$provide.decorator('config', ($delegate) => {
|
||||
// disable shortDots for these tests
|
||||
$delegate.get = _.wrap($delegate.get, function (origGet, name) {
|
||||
if (name === 'shortDots:enable') {
|
||||
return false;
|
||||
} else {
|
||||
return origGet.call(this, name);
|
||||
}
|
||||
});
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
hits = Private(FixturesHitsProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import Joi from 'joi';
|
||||
import * as kbnTestServer from '../../../../../test/utils/kbn_server.js';
|
||||
import fromRoot from '../../../../utils/from_root';
|
||||
import * as kbnTestServer from '../../../../../../test/utils/kbn_server.js';
|
||||
import fromRoot from '../../../../../utils/from_root';
|
||||
import manageUuid from '../manage_uuid';
|
||||
|
||||
describe('plugins/elasticsearch', function () {
|
||||
describe('core_plugins/kibana/server/lib', function () {
|
||||
describe('manage_uuid', function () {
|
||||
const testUuid = 'c4add484-0cba-4e05-86fe-4baa112d9e53';
|
||||
let kbnServer;
|
||||
|
@ -23,7 +23,6 @@ describe('plugins/elasticsearch', function () {
|
|||
});
|
||||
|
||||
await kbnServer.ready();
|
||||
await kbnServer.server.plugins.elasticsearch.waitUntilReady();
|
||||
});
|
||||
|
||||
// clear uuid stuff from previous test runs
|
||||
|
@ -38,53 +37,48 @@ describe('plugins/elasticsearch', function () {
|
|||
});
|
||||
|
||||
it('ensure config uuid is validated as a guid', async function () {
|
||||
config.set('uuid', testUuid);
|
||||
expect(config.get('uuid')).to.be(testUuid);
|
||||
config.set('server.uuid', testUuid);
|
||||
expect(config.get('server.uuid')).to.be(testUuid);
|
||||
|
||||
expect(() => {
|
||||
config.set('uuid', 'foouid');
|
||||
config.set('server.uuid', 'foouid');
|
||||
}).to.throwException((e) => {
|
||||
expect(e.name).to.be('ValidationError');
|
||||
});
|
||||
});
|
||||
|
||||
it('finds the previously set uuid with config match', async function () {
|
||||
const uuidManagement = manageUuid(kbnServer.server);
|
||||
const msg = `Kibana instance UUID: ${testUuid}`;
|
||||
config.set('uuid', testUuid);
|
||||
config.set('server.uuid', testUuid);
|
||||
|
||||
await uuidManagement();
|
||||
await uuidManagement();
|
||||
await manageUuid(kbnServer.server);
|
||||
await manageUuid(kbnServer.server);
|
||||
|
||||
expect(kbnServer.server.log.lastCall.args[1]).to.be.eql(msg);
|
||||
});
|
||||
|
||||
it('updates the previously set uuid with config value', async function () {
|
||||
const uuidManagement = manageUuid(kbnServer.server);
|
||||
config.set('uuid', testUuid);
|
||||
config.set('server.uuid', testUuid);
|
||||
|
||||
await uuidManagement();
|
||||
await manageUuid(kbnServer.server);
|
||||
|
||||
const newUuid = '5b2de169-2785-441b-ae8c-186a1936b17d';
|
||||
const msg = `Updating Kibana instance UUID to: ${newUuid} (was: ${testUuid})`;
|
||||
|
||||
config.set('uuid', newUuid);
|
||||
await uuidManagement();
|
||||
config.set('server.uuid', newUuid);
|
||||
await manageUuid(kbnServer.server);
|
||||
|
||||
expect(kbnServer.server.log.lastCall.args[1]).to.be(msg);
|
||||
});
|
||||
|
||||
it('resumes the uuid stored in data and sets it to the config', async function () {
|
||||
const uuidManagement = manageUuid(kbnServer.server);
|
||||
const partialMsg = 'Resuming persistent Kibana instance UUID';
|
||||
config.set('uuid'); // set to undefined
|
||||
config.set('server.uuid'); // set to undefined
|
||||
|
||||
await uuidManagement();
|
||||
await manageUuid(kbnServer.server);
|
||||
|
||||
expect(config.get('uuid')).to.be.ok(); // not undefined any more
|
||||
expect(config.get('server.uuid')).to.be.ok(); // not undefined any more
|
||||
expect(kbnServer.server.log.lastCall.args[1]).to.match(new RegExp(`^${partialMsg}`));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
22
src/core_plugins/kibana/server/lib/__tests__/system_api.js
Normal file
22
src/core_plugins/kibana/server/lib/__tests__/system_api.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import expect from 'expect.js';
|
||||
import { isSystemApiRequest } from '../system_api';
|
||||
|
||||
describe('system_api', () => {
|
||||
describe('#isSystemApiRequest', () => {
|
||||
it ('returns true for a system API HTTP request', () => {
|
||||
const mockHapiRequest = {
|
||||
headers: {
|
||||
'kbn-system-api': true
|
||||
}
|
||||
};
|
||||
expect(isSystemApiRequest(mockHapiRequest)).to.be(true);
|
||||
});
|
||||
|
||||
it ('returns false for a non-system API HTTP request', () => {
|
||||
const mockHapiRequest = {
|
||||
headers: {}
|
||||
};
|
||||
expect(isSystemApiRequest(mockHapiRequest)).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
72
src/core_plugins/kibana/server/lib/manage_uuid.js
Normal file
72
src/core_plugins/kibana/server/lib/manage_uuid.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
import uuid from 'node-uuid';
|
||||
import Promise from 'bluebird';
|
||||
import { join as pathJoin } from 'path';
|
||||
import { readFile as readFileCallback, writeFile as writeFileCallback } from 'fs';
|
||||
|
||||
const FILE_ENCODING = 'utf8';
|
||||
|
||||
export default async function manageUuid(server) {
|
||||
const config = server.config();
|
||||
const serverPort = server.info.port;
|
||||
const serverHostname = config.get('server.host');
|
||||
const fileName = `${serverHostname}:${serverPort}`;
|
||||
const uuidFile = pathJoin(config.get('path.data'), fileName);
|
||||
|
||||
async function detectUuid() {
|
||||
const readFile = Promise.promisify(readFileCallback);
|
||||
try {
|
||||
const result = await readFile(uuidFile);
|
||||
return result.toString(FILE_ENCODING);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function writeUuid(uuid) {
|
||||
const writeFile = Promise.promisify(writeFileCallback);
|
||||
try {
|
||||
return await writeFile(uuidFile, uuid, { encoding: FILE_ENCODING });
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// detect if uuid exists already from before a restart
|
||||
const logToServer = (msg) => server.log(['server', 'uuid', fileName], msg);
|
||||
const dataFileUuid = await detectUuid();
|
||||
let serverConfigUuid = config.get('server.uuid'); // check if already set in config
|
||||
|
||||
if (dataFileUuid) {
|
||||
// data uuid found
|
||||
if (serverConfigUuid === dataFileUuid) {
|
||||
// config uuid exists, data uuid exists and matches
|
||||
logToServer(`Kibana instance UUID: ${dataFileUuid}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!serverConfigUuid) {
|
||||
// config uuid missing, data uuid exists
|
||||
serverConfigUuid = dataFileUuid;
|
||||
logToServer(`Resuming persistent Kibana instance UUID: ${serverConfigUuid}`);
|
||||
config.set('server.uuid', serverConfigUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverConfigUuid !== dataFileUuid) {
|
||||
// config uuid exists, data uuid exists but mismatches
|
||||
logToServer(`Updating Kibana instance UUID to: ${serverConfigUuid} (was: ${dataFileUuid})`);
|
||||
return writeUuid(serverConfigUuid);
|
||||
}
|
||||
}
|
||||
|
||||
// data uuid missing
|
||||
|
||||
if (!serverConfigUuid) {
|
||||
// config uuid missing
|
||||
serverConfigUuid = uuid.v4();
|
||||
config.set('server.uuid', serverConfigUuid);
|
||||
}
|
||||
|
||||
logToServer(`Setting new Kibana instance UUID: ${serverConfigUuid}`);
|
||||
return writeUuid(serverConfigUuid);
|
||||
}
|
11
src/core_plugins/kibana/server/lib/system_api.js
Normal file
11
src/core_plugins/kibana/server/lib/system_api.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
const SYSTEM_API_HEADER_NAME = 'kbn-system-api';
|
||||
|
||||
/**
|
||||
* Checks on the *server-side*, if an HTTP request is a system API request
|
||||
*
|
||||
* @param request HAPI request object
|
||||
* @return true if request is a system API request; false, otherwise
|
||||
*/
|
||||
export function isSystemApiRequest(request) {
|
||||
return !!request.headers[SYSTEM_API_HEADER_NAME];
|
||||
}
|
|
@ -30,9 +30,9 @@ module.exports = () => Joi.object({
|
|||
exclusive: Joi.boolean().default(false)
|
||||
}).default(),
|
||||
|
||||
uuid: Joi.string().guid().default(),
|
||||
|
||||
server: Joi.object({
|
||||
uuid: Joi.string().guid().default(),
|
||||
name: Joi.string().default(os.hostname()),
|
||||
host: Joi.string().hostname().default('localhost'),
|
||||
port: Joi.number().default(5601),
|
||||
|
|
|
@ -18,7 +18,7 @@ export default function (kbnServer, server, config) {
|
|||
handler: function (request, reply) {
|
||||
return reply({
|
||||
name: config.get('server.name'),
|
||||
uuid: config.get('uuid'),
|
||||
uuid: config.get('server.uuid'),
|
||||
status: kbnServer.status.toJSON(),
|
||||
metrics: kbnServer.metrics
|
||||
});
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import $ from 'jquery';
|
||||
import { remove } from 'lodash';
|
||||
|
||||
import './kbn_chrome.less';
|
||||
import UiModules from 'ui/modules';
|
||||
import { isSystemApiRequest } from 'ui/system_api';
|
||||
|
||||
export default function (chrome, internals) {
|
||||
|
||||
|
@ -41,6 +43,10 @@ export default function (chrome, internals) {
|
|||
$rootScope.$on('$routeUpdate', onRouteChange);
|
||||
onRouteChange();
|
||||
|
||||
const allPendingHttpRequests = () => $http.pendingRequests;
|
||||
const removeSystemApiRequests = (pendingHttpRequests = []) => remove(pendingHttpRequests, isSystemApiRequest);
|
||||
$scope.$watchCollection(allPendingHttpRequests, removeSystemApiRequests);
|
||||
|
||||
// and some local values
|
||||
chrome.httpActive = $http.pendingRequests;
|
||||
$scope.notifList = require('ui/notify')._notifs;
|
||||
|
|
|
@ -118,6 +118,15 @@ describe('Notifier', function () {
|
|||
it('includes stack', function () {
|
||||
expect(notify('error').stack).to.be.defined;
|
||||
});
|
||||
|
||||
it('has css class helper functions', function () {
|
||||
expect(notify('error').getIconClass()).to.equal('fa fa-warning');
|
||||
expect(notify('error').getButtonClass()).to.equal('btn-danger');
|
||||
expect(notify('error').getAlertClassStack()).to.equal('toast-stack alert alert-danger');
|
||||
expect(notify('error').getAlertClass()).to.equal('toast alert alert-danger');
|
||||
expect(notify('error').getButtonGroupClass()).to.equal('toast-controls');
|
||||
expect(notify('error').getToastMessageClass()).to.equal('toast-message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#warning', function () {
|
||||
|
@ -156,6 +165,15 @@ describe('Notifier', function () {
|
|||
it('does not include stack', function () {
|
||||
expect(notify('warning').stack).not.to.be.defined;
|
||||
});
|
||||
|
||||
it('has css class helper functions', function () {
|
||||
expect(notify('warning').getIconClass()).to.equal('fa fa-warning');
|
||||
expect(notify('warning').getButtonClass()).to.equal('btn-warning');
|
||||
expect(notify('warning').getAlertClassStack()).to.equal('toast-stack alert alert-warning');
|
||||
expect(notify('warning').getAlertClass()).to.equal('toast alert alert-warning');
|
||||
expect(notify('warning').getButtonGroupClass()).to.equal('toast-controls');
|
||||
expect(notify('warning').getToastMessageClass()).to.equal('toast-message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#info', function () {
|
||||
|
@ -194,6 +212,15 @@ describe('Notifier', function () {
|
|||
it('does not include stack', function () {
|
||||
expect(notify('info').stack).not.to.be.defined;
|
||||
});
|
||||
|
||||
it('has css class helper functions', function () {
|
||||
expect(notify('info').getIconClass()).to.equal('fa fa-info-circle');
|
||||
expect(notify('info').getButtonClass()).to.equal('btn-info');
|
||||
expect(notify('info').getAlertClassStack()).to.equal('toast-stack alert alert-info');
|
||||
expect(notify('info').getAlertClass()).to.equal('toast alert alert-info');
|
||||
expect(notify('info').getButtonGroupClass()).to.equal('toast-controls');
|
||||
expect(notify('info').getToastMessageClass()).to.equal('toast-message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#custom', function () {
|
||||
|
@ -251,6 +278,18 @@ describe('Notifier', function () {
|
|||
expect(customNotification.customActions).to.have.length(customParams.actions.length);
|
||||
});
|
||||
|
||||
it('custom actions have getButtonClass method', function () {
|
||||
customNotification.customActions.forEach((action, idx) => {
|
||||
expect(action).to.have.property('getButtonClass');
|
||||
expect(action.getButtonClass).to.be.a('function');
|
||||
if (idx === 0) {
|
||||
expect(action.getButtonClass()).to.be('btn-primary btn-info');
|
||||
} else {
|
||||
expect(action.getButtonClass()).to.be('btn-default btn-info');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('gives a default action if none are provided', function () {
|
||||
// destroy the default custom notification, avoid duplicate handling
|
||||
customNotification.clear();
|
||||
|
@ -357,6 +396,15 @@ describe('Notifier', function () {
|
|||
it('does not include stack', function () {
|
||||
expect(notify('banner').stack).not.to.be.defined;
|
||||
});
|
||||
|
||||
it('has css class helper functions', function () {
|
||||
expect(notify('banner').getIconClass()).to.equal('');
|
||||
expect(notify('banner').getButtonClass()).to.equal('btn-banner');
|
||||
expect(notify('banner').getAlertClassStack()).to.equal('toast-stack alert alert-banner');
|
||||
expect(notify('banner').getAlertClass()).to.equal('alert alert-banner');
|
||||
expect(notify('banner').getButtonGroupClass()).to.equal('toast-controls-banner');
|
||||
expect(notify('banner').getToastMessageClass()).to.equal('toast-message-banner');
|
||||
});
|
||||
});
|
||||
|
||||
function notify(fnName) {
|
||||
|
|
|
@ -85,6 +85,27 @@ function restartNotifTimer(notif, cb) {
|
|||
startNotifTimer(notif, cb);
|
||||
}
|
||||
|
||||
const typeToButtonClassMap = {
|
||||
danger: 'btn-danger', // NOTE: `error` type is internally named as `danger`
|
||||
warning: 'btn-warning',
|
||||
info: 'btn-info',
|
||||
banner: 'btn-banner'
|
||||
};
|
||||
const buttonHierarchyClass = (index) => {
|
||||
if (index === 0) {
|
||||
// first action: primary className
|
||||
return 'btn-primary';
|
||||
}
|
||||
// subsequent actions: secondary/default className
|
||||
return 'btn-default';
|
||||
};
|
||||
const typeToAlertClassMap = {
|
||||
danger: `alert-danger`,
|
||||
warning: `alert-warning`,
|
||||
info: `alert-info`,
|
||||
banner: `alert-banner`,
|
||||
};
|
||||
|
||||
function add(notif, cb) {
|
||||
_.set(notif, 'info.version', version);
|
||||
_.set(notif, 'info.buildNum', buildNum);
|
||||
|
@ -97,10 +118,14 @@ function add(notif, cb) {
|
|||
});
|
||||
} else if (notif.customActions) {
|
||||
// wrap all of the custom functions in a close
|
||||
notif.customActions = notif.customActions.map(action => {
|
||||
notif.customActions = notif.customActions.map((action, index) => {
|
||||
return {
|
||||
key: action.text,
|
||||
callback: closeNotif(notif, action.callback, action.text)
|
||||
callback: closeNotif(notif, action.callback, action.text),
|
||||
getButtonClass() {
|
||||
const buttonTypeClass = typeToButtonClassMap[notif.type];
|
||||
return `${buttonHierarchyClass(index)} ${buttonTypeClass}`;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -111,6 +136,18 @@ function add(notif, cb) {
|
|||
return notif.timerId ? true : false;
|
||||
};
|
||||
|
||||
// decorate the notification with helper functions for the template
|
||||
notif.getButtonClass = () => typeToButtonClassMap[notif.type];
|
||||
notif.getAlertClassStack = () => `toast-stack alert ${typeToAlertClassMap[notif.type]}`;
|
||||
notif.getIconClass = () => (notif.type === 'banner') ? '' : `fa fa-${notif.icon}`;
|
||||
notif.getToastMessageClass = () => (notif.type === 'banner') ? 'toast-message-banner' : 'toast-message';
|
||||
notif.getAlertClass = () => (notif.type === 'banner') ?
|
||||
`alert ${typeToAlertClassMap[notif.type]}` : // not including `.toast` class leaves out the flex properties for banner
|
||||
`toast alert ${typeToAlertClassMap[notif.type]}`;
|
||||
notif.getButtonGroupClass = () => (notif.type === 'banner') ?
|
||||
'toast-controls-banner' :
|
||||
'toast-controls';
|
||||
|
||||
let dup = null;
|
||||
if (notif.content) {
|
||||
dup = _.find(notifs, function (item) {
|
||||
|
|
|
@ -8,21 +8,21 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
&-countdown {
|
||||
.toaster-countdown {
|
||||
.badge {
|
||||
color: @white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.badge {
|
||||
color: @white;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.badge {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.badge:after {
|
||||
font-size: @font-size-small;
|
||||
content: attr(hover-text);
|
||||
}
|
||||
.badge:after {
|
||||
font-size: @font-size-small;
|
||||
content: attr(hover-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,11 +91,29 @@
|
|||
background: darken(@alert-danger-bg, 25%);
|
||||
}
|
||||
.alert-banner {
|
||||
background-color: #c0c0c0;
|
||||
background-color: white;
|
||||
padding: 10px 15px;
|
||||
|
||||
a {
|
||||
color: #328caa;
|
||||
}
|
||||
|
||||
.toaster-countdown {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: #328caa;
|
||||
}
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
// toast message class for banner needs to be display:block because banner does
|
||||
// not have a flex layout, and the styled element could be any kind of element
|
||||
.toast-message-banner {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -1,75 +1,76 @@
|
|||
<div class="toaster-container">
|
||||
<ul class="toaster">
|
||||
<li ng-repeat="notif in list" kbn-toast notif="notif">
|
||||
<div class="toast alert" ng-class="'alert-' + notif.type">
|
||||
<div ng-class="notif.getAlertClass()">
|
||||
|
||||
<span ng-show="notif.count > 1" class="badge">{{ notif.count }}</span>
|
||||
|
||||
<i class="fa" ng-class="'fa-' + notif.icon" tooltip="{{notif.title}}"></i>
|
||||
<i ng-class="notif.getIconClass()" tooltip="{{notif.title}}"></i>
|
||||
|
||||
<kbn-truncated
|
||||
ng-if="notif.content"
|
||||
source="{{notif.content | markdown}}"
|
||||
is-html="true"
|
||||
length="250"
|
||||
class="toast-message"
|
||||
ng-class="notif.getToastMessageClass()"
|
||||
></kbn-truncated>
|
||||
|
||||
<render-directive
|
||||
ng-if="notif.directive"
|
||||
definition="notif.directive"
|
||||
notif="notif"
|
||||
class="toast-message"
|
||||
ng-class="notif.getToastMessageClass()"
|
||||
></render-directive>
|
||||
|
||||
<div class="btn-group pull-right toast-controls">
|
||||
<button
|
||||
type="button"
|
||||
ng-if="notif.isTimed()"
|
||||
class="btn toaster-countdown"
|
||||
ng-class="'btn-' + notif.type"
|
||||
ng-click="notif.cancelTimer()"
|
||||
><span class="badge" hover-text="stop">{{notif.timeRemaining}}s</span></button>
|
||||
<div class="btn-group" ng-class="notif.getButtonGroupClass()">
|
||||
<button
|
||||
type="button"
|
||||
ng-if="notif.stack && !notif.showStack"
|
||||
class="btn"
|
||||
ng-class="'btn-' + notif.type"
|
||||
ng-class="notif.getButtonClass()"
|
||||
ng-click="notif.cancelTimer(); notif.showStack = true"
|
||||
>More Info</button>
|
||||
<button
|
||||
type="button"
|
||||
ng-if="notif.stack && notif.showStack"
|
||||
class="btn"
|
||||
ng-class="'btn-' + notif.type"
|
||||
ng-class="notif.getButtonClass()"
|
||||
ng-click="notif.showStack = false"
|
||||
>Less Info</button>
|
||||
<button
|
||||
type="button"
|
||||
ng-if="notif.accept"
|
||||
class="btn"
|
||||
ng-class="'btn-' + notif.type"
|
||||
ng-class="notif.getButtonClass()"
|
||||
ng-click="notif.accept()"
|
||||
>OK</button>
|
||||
<button
|
||||
type="button"
|
||||
ng-if="notif.address"
|
||||
class="btn"
|
||||
ng-class="'btn-' + notif.type"
|
||||
ng-class="notif.getButtonClass()"
|
||||
ng-click="notif.address()"
|
||||
>Fix it</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
ng-repeat="action in notif.customActions"
|
||||
ng-class="'btn-' + notif.type"
|
||||
ng-class="action.getButtonClass()"
|
||||
ng-click="action.callback()"
|
||||
ng-bind="action.key"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
ng-if="notif.isTimed()"
|
||||
class="toaster-countdown"
|
||||
ng-class="notif.getButtonClass()"
|
||||
ng-click="notif.cancelTimer()"
|
||||
><span class="badge" hover-text="stop">{{notif.timeRemaining}}s</span></button>
|
||||
</div>
|
||||
|
||||
<div ng-if="notif.stack && notif.showStack" class="toast-stack alert" ng-class="'alert-' + notif.type">
|
||||
<div ng-if="notif.stack && notif.showStack" ng-class="notif.getAlertClassStack()">
|
||||
<pre ng-repeat="stack in notif.stacks" ng-bind="stack"></pre>
|
||||
</div>
|
||||
|
||||
|
|
38
src/ui/public/system_api/__tests__/system_api.js
Normal file
38
src/ui/public/system_api/__tests__/system_api.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { addSystemApiHeader, isSystemApiRequest } from '../system_api';
|
||||
|
||||
describe('system_api', () => {
|
||||
describe('#addSystemApiHeader', () => {
|
||||
it ('adds the correct system API header', () => {
|
||||
const headers = {
|
||||
'kbn-version': '4.6.0'
|
||||
};
|
||||
const newHeaders = addSystemApiHeader(headers);
|
||||
|
||||
expect(newHeaders).to.have.property('kbn-system-api');
|
||||
expect(newHeaders['kbn-system-api']).to.be(true);
|
||||
|
||||
expect(newHeaders).to.have.property('kbn-version');
|
||||
expect(newHeaders['kbn-version']).to.be('4.6.0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isSystemApiRequest', () => {
|
||||
it ('returns true for a system API HTTP request', () => {
|
||||
const mockRequest = {
|
||||
headers: {
|
||||
'kbn-system-api': true
|
||||
}
|
||||
};
|
||||
expect(isSystemApiRequest(mockRequest)).to.be(true);
|
||||
});
|
||||
|
||||
it ('returns false for a non-system API HTTP request', () => {
|
||||
const mockRequest = {
|
||||
headers: {}
|
||||
};
|
||||
expect(isSystemApiRequest(mockRequest)).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
26
src/ui/public/system_api/system_api.js
Normal file
26
src/ui/public/system_api/system_api.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
const SYSTEM_API_HEADER_NAME = 'kbn-system-api';
|
||||
|
||||
/**
|
||||
* Adds a custom header designating request as system API
|
||||
* @param originalHeaders Object representing set of headers
|
||||
* @return Object representing set of headers, with system API header added in
|
||||
*/
|
||||
export function addSystemApiHeader(originalHeaders) {
|
||||
const systemApiHeaders = {
|
||||
[SYSTEM_API_HEADER_NAME]: true
|
||||
};
|
||||
return {
|
||||
...originalHeaders,
|
||||
...systemApiHeaders
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if request is a system API request; false otherwise
|
||||
*
|
||||
* @param request Object Request object created by $http service
|
||||
* @return true if request is a system API request; false otherwise
|
||||
*/
|
||||
export function isSystemApiRequest(request) {
|
||||
return !!request.headers[SYSTEM_API_HEADER_NAME];
|
||||
}
|
|
@ -41,7 +41,7 @@ bdd.describe('discover tab', function describeIndexTests() {
|
|||
return PageObjects.discover.getSidebarWidth()
|
||||
.then(function (width) {
|
||||
PageObjects.common.debug('expanded sidebar width = ' + width);
|
||||
expect(width > 180).to.be(true);
|
||||
expect(width > 20).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -66,7 +66,7 @@ bdd.describe('discover tab', function describeIndexTests() {
|
|||
})
|
||||
.then(function (width) {
|
||||
PageObjects.common.debug('expanded sidebar width = ' + width);
|
||||
expect(width > 180).to.be(true);
|
||||
expect(width > 20).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -142,7 +142,7 @@ bdd.describe('visualize app', function describeIndexTests() {
|
|||
});
|
||||
});
|
||||
|
||||
bdd.it('"Fit data bounds" should zoom to level 3', function pageHeader() {
|
||||
bdd.it('Fit data bounds should zoom to level 3', function pageHeader() {
|
||||
var expectedPrecision2ZoomCircles = [ { color: '#750000', radius: 192 },
|
||||
{ color: '#750000', radius: 191 },
|
||||
{ color: '#750000', radius: 177 },
|
||||
|
|
|
@ -17,7 +17,7 @@ bdd.describe('visualize app', function () {
|
|||
|
||||
bdd.before(function () {
|
||||
var self = this;
|
||||
remote.setWindowSize(1200,800);
|
||||
remote.setWindowSize(1280,800);
|
||||
|
||||
PageObjects.common.debug('Starting visualize before method');
|
||||
var logstash = scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
|
|
|
@ -15,17 +15,19 @@ import {
|
|||
import util from 'util';
|
||||
|
||||
import getUrl from '../../utils/get_url';
|
||||
|
||||
import {
|
||||
config,
|
||||
defaultTryTimeout,
|
||||
defaultFindTimeout,
|
||||
remote,
|
||||
shieldPage
|
||||
shieldPage,
|
||||
esClient
|
||||
} from '../index';
|
||||
|
||||
import {
|
||||
Log,
|
||||
Try,
|
||||
Try
|
||||
} from '../utils';
|
||||
|
||||
const mkdirpAsync = promisify(mkdirp);
|
||||
|
@ -83,8 +85,23 @@ export default class Common {
|
|||
function navigateTo(url) {
|
||||
return self.try(function () {
|
||||
// since we're using hash URLs, always reload first to force re-render
|
||||
self.debug('navigate to: ' + url);
|
||||
return self.remote.get(url)
|
||||
return esClient.getDefaultIndex()
|
||||
.then(function (defaultIndex) {
|
||||
if (appName === 'discover' || appName === 'visualize' || appName === 'dashboard') {
|
||||
if (!defaultIndex) {
|
||||
// https://github.com/elastic/kibana/issues/7496
|
||||
// Even though most tests are using esClient to set the default index, sometimes Kibana clobbers
|
||||
// that change. If we got here, fix it.
|
||||
self.debug(' >>>>>>>> WARNING Navigating to [' + appName + '] with defaultIndex=' + defaultIndex);
|
||||
self.debug(' >>>>>>>> Setting defaultIndex to "logstash-*""');
|
||||
return esClient.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'});
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
self.debug('navigate to: ' + url);
|
||||
return self.remote.get(url);
|
||||
})
|
||||
.then(function () {
|
||||
return self.sleep(700);
|
||||
})
|
||||
|
|
|
@ -51,6 +51,9 @@ export default class DiscoverPage {
|
|||
return this.clickLoadSavedSearchButton()
|
||||
.then(() => {
|
||||
this.findTimeout.findByLinkText(searchName).click();
|
||||
})
|
||||
.then(() => {
|
||||
return PageObjects.header.getSpinnerDone();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -79,8 +82,11 @@ export default class DiscoverPage {
|
|||
}
|
||||
|
||||
getBarChartData() {
|
||||
return this.findTimeout
|
||||
.findAllByCssSelector('rect[data-label="Count"]')
|
||||
return PageObjects.header.getSpinnerDone()
|
||||
.then(() => {
|
||||
return this.findTimeout
|
||||
.findAllByCssSelector('rect[data-label="Count"]');
|
||||
})
|
||||
.then(function (chartData) {
|
||||
|
||||
function getChartData(chart) {
|
||||
|
@ -128,13 +134,19 @@ export default class DiscoverPage {
|
|||
return this.findTimeout
|
||||
.findByCssSelector('option[label="' + interval + '"]')
|
||||
.click();
|
||||
})
|
||||
.then(() => {
|
||||
return PageObjects.header.getSpinnerDone();
|
||||
});
|
||||
}
|
||||
|
||||
getHitCount() {
|
||||
return this.findTimeout
|
||||
.findByCssSelector('strong.discover-info-hits')
|
||||
.getVisibleText();
|
||||
return PageObjects.header.getSpinnerDone()
|
||||
.then(() => {
|
||||
return this.findTimeout
|
||||
.findByCssSelector('strong.discover-info-hits')
|
||||
.getVisibleText();
|
||||
});
|
||||
}
|
||||
|
||||
query(queryString) {
|
||||
|
@ -146,6 +158,9 @@ export default class DiscoverPage {
|
|||
return this.findTimeout
|
||||
.findByCssSelector('button[aria-label="Search"]')
|
||||
.click();
|
||||
})
|
||||
.then(() => {
|
||||
return PageObjects.header.getSpinnerDone();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -68,12 +68,39 @@ export default (function () {
|
|||
);
|
||||
} else {
|
||||
configId = response.hits.hits[0]._id;
|
||||
Log.debug('config._id =' + configId);
|
||||
Log.debug('config._id = ' + configId);
|
||||
return configId;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
** Gets defaultIndex from the config doc.
|
||||
*/
|
||||
getDefaultIndex: function () {
|
||||
var defaultIndex;
|
||||
|
||||
return this.client.search({
|
||||
index: '.kibana',
|
||||
type: 'config'
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.errors) {
|
||||
throw new Error(
|
||||
'get config failed\n' +
|
||||
response.items
|
||||
.map(i => i[Object.keys(i)[0]].error)
|
||||
.filter(Boolean)
|
||||
.map(err => ' ' + JSON.stringify(err))
|
||||
.join('\n')
|
||||
);
|
||||
} else {
|
||||
defaultIndex = response.hits.hits[0]._source.defaultIndex;
|
||||
Log.debug('config.defaultIndex = ' + defaultIndex);
|
||||
return defaultIndex;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add fields to the config doc (like setting timezone and defaultIndex)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue