Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions x-pack/plugins/ml/common/constants/license.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/


export const LICENSE_TYPE = {
BASIC: 0,
FULL: 1,
};
7 changes: 5 additions & 2 deletions x-pack/plugins/ml/public/datavisualizer/datavisualizer.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ <h1>{{indexPattern.title}}</h1>
</div>
</div>

<div class="main-container">
<div
class="main-container"
ng-class="{'no-sidebar': showSidebar===false}"
>

<div class="kuiPanel kuiVerticalRhythm datavisualizer-panel">

Expand Down Expand Up @@ -170,7 +173,7 @@ <h2 class="kuiSubTitle kuiVerticalRhythm">Fields</h2>

</div>

<div class="datavisualizer-sidebar">
<div ng-if="showSidebar" class="datavisualizer-sidebar">
<ng-include src="urlBasePath+'/plugins/ml/datavisualizer/datavisualizer_sidebar.html'"></ng-include>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { decorateQuery, luceneStringToDsl } from 'ui/courier';
import { ML_JOB_FIELD_TYPES, KBN_FIELD_TYPES } from 'plugins/ml/../common/constants/field_types';
import { kbnTypeToMLJobType } from 'plugins/ml/util/field_types_utils';
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
import { checkLicenseExpired } from 'plugins/ml/license/check_license';
import { checkBasicLicense, isFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { createSearchItems } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
import { loadCurrentIndexPattern, loadCurrentSavedSearch, timeBasedIndexCheck } from 'plugins/ml/util/index_utils';
Expand All @@ -37,7 +37,7 @@ uiRoutes
.when('/jobs/new_job/datavisualizer', {
template,
resolve: {
CheckLicense: checkLicenseExpired,
CheckLicense: checkBasicLicense,
privileges: checkGetJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
savedSearch: loadCurrentSavedSearch,
Expand Down Expand Up @@ -93,6 +93,8 @@ module
$scope.fieldFilter = '';
$scope.recognizerResults = { count: 0 };

$scope.showSidebar = isFullLicense();

// Check for a saved query in the AppState or via a savedSearchId in the URL.
$scope.searchQueryText = '';
if (_.has($scope.appState, 'query')) {
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/ml/public/datavisualizer/styles/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
display: inline-block;
padding-right: 10px;
}
.no-sidebar {
width: 100%;
}

.datavisualizer-sidebar {
width: 290px;
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/ml/public/explorer/explorer_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { initPromise } from 'plugins/ml/util/promise';
import template from './explorer.html';

import uiRoutes from 'ui/routes';
import { checkLicense } from 'plugins/ml/license/check_license';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { loadIndexPatterns, getIndexPatterns } from 'plugins/ml/util/index_utils';
import { refreshIntervalWatcher } from 'plugins/ml/util/refresh_interval_watcher';
Expand All @@ -50,7 +50,7 @@ uiRoutes
.when('/explorer/?', {
template,
resolve: {
CheckLicense: checkLicense,
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
indexPatterns: loadIndexPatterns,
initPromise: initPromise(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import 'ngreact';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);

import { checkLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { checkBasicLicense } from 'plugins/ml/license/check_license';
import { checkFindFileStructurePrivilege } from 'plugins/ml/privilege/check_privilege';
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { loadNewJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
import { initPromise } from 'plugins/ml/util/promise';
Expand All @@ -24,8 +24,8 @@ uiRoutes
.when('/filedatavisualizer/?', {
template,
resolve: {
CheckLicense: checkLicense,
privileges: checkGetJobsPrivilege,
CheckLicense: checkBasicLicense,
privileges: checkFindFileStructurePrivilege,
mlNodeCount: getMlNodeCount,
loadNewJobDefaults,
initPromise: initPromise(true)
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/ml/public/jobs/jobs_list/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import React from 'react';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);

import { checkLicense } from 'plugins/ml/license/check_license';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { loadNewJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
Expand All @@ -25,7 +25,7 @@ uiRoutes
.when('/jobs/?', {
template,
resolve: {
CheckLicense: checkLicense,
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
mlNodeCount: getMlNodeCount,
loadNewJobDefaults,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { copyTextToClipboard } from 'plugins/ml/util/clipboard_utils';

import { timefilter } from 'ui/timefilter';
import uiRoutes from 'ui/routes';
import { checkLicense } from 'plugins/ml/license/check_license';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege, checkPermission, createPermissionFailureMessage } from 'plugins/ml/privilege/check_privilege';
import { addItemToRecentlyAccessed } from 'plugins/ml/util/recently_accessed';
import { getMlNodeCount, mlNodesAvailable, permissionToViewMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
Expand All @@ -43,7 +43,7 @@ uiRoutes
.when('/jobs_old/?', {
template,
resolve: {
CheckLicense: checkLicense,
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
mlNodeCount: getMlNodeCount,
loadNewJobDefaults,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { parseInterval } from 'ui/utils/parse_interval';
import { timefilter } from 'ui/timefilter';

import uiRoutes from 'ui/routes';
import { checkLicense } from 'plugins/ml/license/check_license';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import template from './new_job.html';
import saveStatusTemplate from 'plugins/ml/jobs/new_job/advanced/save_status_modal/save_status_modal.html';
Expand All @@ -38,7 +38,7 @@ uiRoutes
.when('/jobs/new_job/advanced', {
template,
resolve: {
CheckLicense: checkLicense,
CheckLicense: checkFullLicense,
privileges: checkCreateJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
indexPatterns: loadIndexPatterns,
Expand All @@ -51,7 +51,7 @@ uiRoutes
.when('/jobs/new_job/advanced/:jobId', {
template,
resolve: {
CheckLicense: checkLicense,
CheckLicense: checkFullLicense,
privileges: checkCreateJobsPrivilege,
indexPattern: loadCurrentIndexPattern,
indexPatterns: loadIndexPatterns,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
*/

import uiRoutes from 'ui/routes';
import { checkLicenseExpired } from 'plugins/ml/license/check_license';
import { checkLicenseExpired, checkBasicLicense } from 'plugins/ml/license/check_license';
import { preConfiguredJobRedirect } from 'plugins/ml/jobs/new_job/wizard/preconfigured_job_redirect';
import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { checkCreateJobsPrivilege, checkFindFileStructurePrivilege } from 'plugins/ml/privilege/check_privilege';
import { loadIndexPatterns, getIndexPatterns } from 'plugins/ml/util/index_utils';
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { initPromise } from 'plugins/ml/util/promise';
Expand All @@ -35,35 +35,47 @@ uiRoutes
indexPatterns: loadIndexPatterns,
preConfiguredJobRedirect,
checkMlNodesAvailable,
initPromise: initPromise(true)
initPromise: initPromise(true),
nextStepPath: () => '#/jobs/new_job/step/job_type',
}
});

uiRoutes
.when('/datavisualizer_index_select', {
template,
resolve: {
CheckLicense: checkBasicLicense,
privileges: checkFindFileStructurePrivilege,
indexPatterns: loadIndexPatterns,
initPromise: initPromise(true),
nextStepPath: () => '#jobs/new_job/datavisualizer',
}
});

import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');

module.controller('MlNewJobStepIndexOrSearch',
function ($scope) {
function ($scope, $route) {

timefilter.disableTimeRangeSelector(); // remove time picker from top of page
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page

$scope.indexPatterns = getIndexPatterns();

const path = $route.current.locals.nextStepPath;

$scope.withIndexPatternUrl = function (pattern) {
if (!pattern) {
return;
}

return '#/jobs/new_job/step/job_type?index=' + encodeURIComponent(pattern.id);
return `${path}?index=${encodeURIComponent(pattern.id)}`;
};

$scope.withSavedSearchUrl = function (savedSearch) {
if (!savedSearch) {
return;
}

return '#/jobs/new_job/step/job_type?savedSearchId=' + encodeURIComponent(savedSearch.id);
return `${path}?savedSearchId=${encodeURIComponent(savedSearch.id)}`;
};

});
116 changes: 78 additions & 38 deletions x-pack/plugins/ml/public/license/check_license.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,74 @@
import React from 'react';
import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info';
import { banners, addAppRedirectMessageToUrl } from 'ui/notify';
import { LICENSE_TYPE } from '../../common/constants/license';

import chrome from 'ui/chrome';
import { EuiCallOut } from '@elastic/eui';

let licenseHasExpired = true;
let licenseType = null;
let expiredLicenseBannerId;

export function checkLicense(Private, kbnBaseUrl) {
const xpackInfo = Private(XPackInfoProvider);
const features = xpackInfo.get('features.ml');

const licenseAllowsToShowThisPage = features.isAvailable;
if (!licenseAllowsToShowThisPage) {
const { message } = features;
const newUrl = addAppRedirectMessageToUrl(chrome.addBasePath(kbnBaseUrl), message);
window.location.href = newUrl;
return Promise.halt();
}
export function checkFullLicense(Private, kbnBaseUrl, kbnUrl) {
const features = getFeatures(Private);
licenseType = features.licenseType;

if (features.isAvailable === false) {
// ML is not enabled
return redirectToKibana(features, kbnBaseUrl);

} else if (features.licenseType === LICENSE_TYPE.BASIC) {

// ML is enabled, but only with a basic or gold license
return redirectToBasic(kbnUrl);

} else {

licenseHasExpired = features.hasExpired || false;

// If the license has expired ML app will still work for 7 days and then
// the job management endpoints (e.g. create job, start datafeed) will be restricted.
// Therefore we need to keep the app enabled but show an info banner to the user.
if(licenseHasExpired) {
const message = features.message;
if (expiredLicenseBannerId === undefined) {
// Only show the banner once with no way to dismiss it
expiredLicenseBannerId = banners.add({
component: (
<EuiCallOut
iconType="iInCircle"
color="warning"
title={message}
/>
),
});
// ML is enabled
licenseHasExpired = (features.hasExpired || false);
// If the license has expired ML app will still work for 7 days and then
// the job management endpoints (e.g. create job, start datafeed) will be restricted.
// Therefore we need to keep the app enabled but show an info banner to the user.
if(licenseHasExpired) {
const message = features.message;
if (expiredLicenseBannerId === undefined) {
// Only show the banner once with no way to dismiss it
expiredLicenseBannerId = banners.add({
component: (
<EuiCallOut
iconType="iInCircle"
color="warning"
title={message}
/>
),
});
}
}
return Promise.resolve(features);
}
}

export function checkBasicLicense(Private, kbnBaseUrl) {
const features = getFeatures(Private);
licenseType = features.licenseType;

return Promise.resolve(features);
if (features.isAvailable === false) {
// ML is not enabled
return redirectToKibana(features, kbnBaseUrl);

} else {

// ML is enabled
return Promise.resolve(features);
}
}

// a wrapper for checkLicense which doesn't resolve if the license has expired.
// a wrapper for checkFullLicense which doesn't resolve if the license has expired.
// this is used by all create jobs pages to redirect back to the jobs list
// if the user's license has expired.
export function checkLicenseExpired(Private, Promise, kbnBaseUrl, kbnUrl) {
return checkLicense(Private, Promise, kbnBaseUrl)
export function checkLicenseExpired(Private, kbnBaseUrl, kbnUrl) {
return checkFullLicense(Private, kbnBaseUrl, kbnUrl)
.then((features) => {
if (features.hasExpired) {
kbnUrl.redirect('/jobs');
Expand All @@ -69,17 +89,37 @@ export function checkLicenseExpired(Private, Promise, kbnBaseUrl, kbnUrl) {
});
}

export function getLicenseHasExpired() {
function getFeatures(Private) {
const xpackInfo = Private(XPackInfoProvider);
return xpackInfo.get('features.ml');
}

function redirectToKibana(features, kbnBaseUrl) {
const { message } = features;
const newUrl = addAppRedirectMessageToUrl(chrome.addBasePath(kbnBaseUrl), (message || ''));
window.location.href = newUrl;
return Promise.halt();
}

function redirectToBasic(kbnUrl) {
kbnUrl.redirect('/filedatavisualizer'); // TODO: update this with the new fancy basic landing page
return Promise.halt();
}

export function hasLicenseExpired() {
return licenseHasExpired;
}

export function isFullLicense() {
return (licenseType === LICENSE_TYPE.FULL);
}

export function xpackFeatureProvider(Private) {
const xpackInfo = Private(XPackInfoProvider);
function isAvailable(feature) {
return xpackInfo.get(`features.${feature}.isAvailable`, false);
}

return {
isAvailable
isAvailable(feature) {
xpackInfo.get(`features.${feature}.isAvailable`, false);
}
};
}
Loading