Skip to content

Commit

Permalink
Merge branch '7.x' into ua/7.x/panel-error-states
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Sep 21, 2021
2 parents de9b677 + 4be8505 commit 9808566
Show file tree
Hide file tree
Showing 9 changed files with 725 additions and 394 deletions.
2 changes: 1 addition & 1 deletion src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ export class DocLinksService {
},
snapshotRestore: {
guide: `${KIBANA_DOCS}snapshot-repositories.html`,
changeIndexSettings: `${ELASTICSEARCH_DOCS}snapshots-restore-snapshot.html#change-index-settings-during-restore`,
changeIndexSettings: `${ELASTICSEARCH_DOCS}index-modules.html`,
createSnapshot: `${ELASTICSEARCH_DOCS}snapshots-take-snapshot.html`,
getSnapshot: `${ELASTICSEARCH_DOCS}get-snapshot-api.html`,
registerSharedFileSystem: `${ELASTICSEARCH_DOCS}snapshots-register-repository.html#snapshots-filesystem-repository`,
Expand Down
7 changes: 2 additions & 5 deletions x-pack/plugins/monitoring/public/application/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { GlobalStateProvider } from './global_state_context';
import { ExternalConfigContext, ExternalConfig } from './external_config_context';
import { createPreserveQueryHistory } from './preserve_query_history';
import { RouteInit } from './route_init';
import { NoDataPage } from './pages/no_data';
import { ElasticsearchOverviewPage } from './pages/elasticsearch/overview';
import { CODE_PATH_ELASTICSEARCH } from '../../common/constants';
import { MonitoringTimeContainer } from './hooks/use_monitoring_time';
Expand Down Expand Up @@ -54,7 +55,7 @@ const MonitoringApp: React.FC<{
<BreadcrumbContainer.Provider history={history}>
<Router history={history}>
<Switch>
<Route path="/no-data" component={NoData} />
<Route path="/no-data" component={NoDataPage} />
<Route path="/loading" component={LoadingPage} />
<RouteInit
path="/license"
Expand Down Expand Up @@ -98,10 +99,6 @@ const MonitoringApp: React.FC<{
);
};

const NoData: React.FC<{}> = () => {
return <div>No data page</div>;
};

const Home: React.FC<{}> = () => {
return <div>Home page (Cluster listing)</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/enabler.js
export class Enabler {
http: any;
updateModel: any;

constructor(http: any, updateModel: (properties: any) => void) {
this.http = http;
this.updateModel = updateModel;
}

async enableCollectionInterval() {
try {
this.updateModel({ isCollectionIntervalUpdating: true });

await this.http.fetch('../api/monitoring/v1/elasticsearch_settings/set/collection_interval', {
method: 'PUT',
});
this.updateModel({
isCollectionIntervalUpdated: true,
isCollectionIntervalUpdating: false,
});
} catch (err) {
this.updateModel({
errors: (err as any).data,
isCollectionIntervalUpdated: false,
isCollectionIntervalUpdating: false,
});
}
}

async enableCollectionEnabled() {
try {
this.updateModel({ isCollectionEnabledUpdating: true });
await this.http.fetch('../api/monitoring/v1/elasticsearch_settings/set/collection_enabled', {
method: 'PUT',
});

this.updateModel({
isCollectionEnabledUpdated: true,
isCollectionEnabledUpdating: false,
});
} catch (err) {
this.updateModel({
errors: (err as any).data,
isCollectionEnabledUpdated: false,
isCollectionEnabledUpdating: false,
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { NoDataPage } from './no_data_page';
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { useCallback, useContext, useState } from 'react';
import { Redirect } from 'react-router-dom';

import { i18n } from '@kbn/i18n';
// @ts-ignore
import { NoData } from '../../../components/no_data';
import { PageTemplate } from '../page_template';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { CODE_PATH_LICENSE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants';
import { Legacy } from '../../../legacy_shims';
import { Enabler } from './enabler';
import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs';
import { initSetupModeState } from '../../setup_mode/setup_mode';
import { GlobalStateContext } from '../../global_state_context';

const CODE_PATHS = [CODE_PATH_LICENSE];

interface NoDataPageSetupDeps {
http: any;
data: any;
}

interface SettingsChecker {
message: string;
api: string;
next?: SettingsChecker;
}

const clusterCheckers: SettingsChecker[] = [
{
message: i18n.translate('xpack.monitoring.noData.checker.clusterSettings', {
defaultMessage: 'Checking cluster settings API on production cluster',
}),
api: '../api/monitoring/v1/elasticsearch_settings/check/cluster',
},
{
message: i18n.translate('xpack.monitoring.noData.checker.nodesSettings', {
defaultMessage: 'Checking nodes settings API on production cluster',
}),
api: '../api/monitoring/v1/elasticsearch_settings/check/nodes',
},
];

export const NoDataPage = () => {
const title = i18n.translate('xpack.monitoring.noData.routeTitle', {
defaultMessage: 'Setup Monitoring',
});

const { services } = useKibana<NoDataPageSetupDeps>();
const [shouldRedirect, setShouldRedirect] = useState(false);

const [model, setModel] = useState({
errors: [], // errors can happen from trying to check or set ES settings
checkMessage: null, // message to show while waiting for api response
isLoading: true, // flag for in-progress state of checking for no data reason
isCollectionEnabledUpdating: false, // flags to indicate whether to show a spinner while waiting for ajax
isCollectionEnabledUpdated: false,
isCollectionIntervalUpdating: false,
isCollectionIntervalUpdated: false,
} as any);

const { update: updateBreadcrumbs } = useContext(BreadcrumbContainer.Context);
updateBreadcrumbs([
{
'data-test-subj': 'breadcrumbClusters',
text: 'Clusters',
href: '#/home',
ignoreGlobalState: true,
},
]);

const globalState = useContext(GlobalStateContext);
initSetupModeState(globalState, services.http);

// From x-pack/plugins/monitoring/public/views/no_data/model_updater.js
const updateModel = useCallback(
(properties: any) => {
setModel((previousModel: any) => {
const updated = { ...previousModel };
const keys = Object.keys(properties);

keys.forEach((key) => {
if (Array.isArray(updated[key])) {
updated[key].push(properties[key]);
} else {
updated[key] = properties[key];
}
});

return updated;
});
},
[setModel]
);

const getPageData = useCallback(async () => {
let catchReason;
try {
const clusters = await getClusters(services);

if (clusters && clusters.length) {
setShouldRedirect(true);
return;
}
} catch (err) {
if (err && err.status === 503) {
catchReason = {
property: 'custom',
message: err.data.message,
};
}
}

if (catchReason) {
updateModel({ reason: catchReason });
} else {
await startChecks(clusterCheckers, services.http, updateModel);
}
}, [services, updateModel]);

const enabler = new Enabler(services.http, updateModel);

return (
<PageTemplate title={title} getPageData={getPageData}>
{shouldRedirect ? (
<Redirect to="/home" />
) : (
<NoData {...model} enabler={enabler} isCloudEnabled={Legacy.shims.isCloud} />
)}
</PageTemplate>
);
};

async function getClusters(services: NoDataPageSetupDeps): Promise<any[]> {
const url = '../api/monitoring/v1/clusters';
const bounds = services.data?.query.timefilter.timefilter.getBounds();
const min = bounds.min.toISOString();
const max = bounds.max.toISOString();

const response = await services.http?.fetch(url, {
method: 'POST',
body: JSON.stringify({
css: undefined,
timeRange: {
min,
max,
},
codePaths: CODE_PATHS,
}),
});

return formatClusters(response);
}

// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js
const mapCheckers = (_checkers: SettingsChecker[]) => {
return _checkers.map((current, checkerIndex) => {
const next = _checkers[checkerIndex + 1];
if (next !== undefined) {
current.next = next;
}

return current;
});
};

// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js
function startChecks(
checkers: SettingsChecker[],
http: { fetch: any },
updateModel: (properties: any) => void
) {
const runCheck = async (currentChecker: SettingsChecker): Promise<any> => {
updateModel({ checkMessage: currentChecker.message });

const { found, reason, error, errorReason } = await executeCheck(currentChecker, http);

if (error) {
updateModel({ errors: errorReason });
if (currentChecker.next) {
return runCheck(currentChecker.next);
}
} else if (found) {
return updateModel({
reason,
isLoading: false,
checkMessage: null,
});
} else if (currentChecker.next) {
return runCheck(currentChecker.next);
}

// dead end
updateModel({
reason: null,
isLoading: false,
checkMessage: null,
});
};

const _checkers = mapCheckers(checkers);
return runCheck(_checkers[0]);
}

async function executeCheck(checker: SettingsChecker, http: { fetch: any }): Promise<any> {
try {
const response = await http.fetch(checker.api, {
method: 'GET',
});
const { found, reason } = response;

return { found, reason };
} catch (err: any) {
const { data } = err;

return {
error: true,
found: false,
errorReason: data,
};
}
}

function formatClusters(clusters: any): any[] {
return clusters.map(formatCluster);
}

function formatCluster(cluster: any) {
if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) {
cluster.cluster_name = 'Standalone Cluster';
}
return cluster;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ import { CloudDeployment } from './blurbs';
import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link';

function NoDataMessage(props) {
const { isLoading, reason, checkMessage } = props;
const { isLoading, reason, checkMessage, isCollectionEnabledUpdated } = props;

if (isLoading) {
if ((isCollectionEnabledUpdated && !reason) || isLoading) {
return <CheckingSettings checkMessage={checkMessage} />;
}

Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/monitoring/public/lib/setup_mode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ export const disableElasticsearchInternalCollection = async () => {
};

export const toggleSetupMode = (inSetupMode: boolean) => {
if (isReactMigrationEnabled()) return setupModeReact.toggleSetupMode(inSetupMode);

checkAngularState();

const globalState = angularState.injector.get('globalState');
Expand Down
Loading

0 comments on commit 9808566

Please sign in to comment.