Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
weltenwort committed Oct 8, 2019
1 parent 43c2edf commit 5e00b3d
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 13 deletions.
21 changes: 21 additions & 0 deletions x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,29 @@ export type SetupStatus =
| 'initializing' // acquiring job statuses to determine setup status
| 'unknown' // job status could not be acquired (failed request etc)
| 'required' // jobs are missing
| 'requiredForReconfiguration' // the configurations don't match the source configurations
| 'requiredForUpdate' // the definitions don't match the module definitions
| 'pending' // In the process of setting up the module for the first time or retrying, waiting for response
| 'succeeded' // setup succeeded, notifying user
| 'failed' // setup failed, notifying user
| 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time
| 'skipped'; // setup hidden because the module is in a correct state already

/**
* Maps a job status to the possibility that results have already been produced
* before this state was reached.
*/
export const isJobStatusWithResults = (jobStatus: JobStatus) =>
['started', 'finished', 'stopped', 'failed'].includes(jobStatus);

export const isHealthyJobStatus = (jobStatus: JobStatus) =>
['started', 'finished'].includes(jobStatus);

/**
* Maps a setup status to the possibility that results have already been
* produced before this state was reached.
*/
export const isSetupStatusWithResults = (setupStatus: SetupStatus) =>
['skipped', 'hiddenAfterSuccess', 'requiredForReconfiguration', 'requiredForUpdate'].includes(
setupStatus
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 * from './log_analysis_job_problem_indicator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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.
*/

import React from 'react';

import { isHealthyJobStatus, JobStatus } from '../../../../common/log_analysis';

export const LogAnalysisJobProblemIndicator: React.FC<{
jobStatus: JobStatus;
}> = ({ jobStatus }) => {
if (isHealthyJobStatus(jobStatus)) {
return null;
} else {
return <div>misconfigured</div>;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ const jobStateRT = rt.keyof({
opening: null,
});

export const jobCustomSettingsRT = rt.type({
logs_source_config: rt.type({
indexPattern: rt.string,
timestampField: rt.string,
bucketSpan: rt.number,
}),
});

export const jobSummaryRT = rt.intersection([
rt.type({
id: rt.string,
Expand All @@ -56,6 +64,7 @@ export const jobSummaryRT = rt.intersection([
datafeedIndices: rt.array(rt.string),
datafeedState: datafeedStateRT,
fullJob: rt.partial({
custom_settings: jobCustomSettingsRT,
finished_time: rt.number,
}),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/

import * as rt from 'io-ts';
import { kfetch } from 'ui/kfetch';

import { fold } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';
import { identity } from 'fp-ts/lib/function';
import * as rt from 'io-ts';
import { kfetch } from 'ui/kfetch';

import { jobCustomSettingsRT } from './ml_get_jobs_summary_api';
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
import { getJobIdPrefix } from '../../../../../common/log_analysis';

Expand All @@ -33,25 +34,33 @@ export const callSetupMlModuleAPI = async (
timeField: string,
bucketSpan: number
) => {
const filteredIndexPattern = removeSampleDataIndex(indexPattern);
const response = await kfetch({
method: 'POST',
pathname: `/api/ml/modules/setup/${MODULE_ID}`,
body: JSON.stringify(
setupMlModuleRequestPayloadRT.encode({
start,
end,
indexPatternName: removeSampleDataIndex(indexPattern),
indexPatternName: filteredIndexPattern,
prefix: getJobIdPrefix(spaceId, sourceId),
startDatafeed: true,
jobOverrides: [
{
job_id: 'log-entry-rate',
job_id: 'log-entry-rate' as const,
analysis_config: {
bucket_span: `${bucketSpan}ms`,
},
data_description: {
time_field: timeField,
},
custom_settings: {
logs_source_config: {
indexPattern: filteredIndexPattern,
timestampField: timeField,
bucketSpan,
},
},
},
],
datafeedOverrides: [],
Expand All @@ -70,11 +79,22 @@ const setupMlModuleTimeParamsRT = rt.partial({
end: rt.number,
});

const setupMlModuleLogEntryRateJobOverridesRT = rt.type({
job_id: rt.literal('log-entry-rate'),
analysis_config: rt.type({
bucket_span: rt.string,
}),
data_description: rt.type({
time_field: rt.string,
}),
custom_settings: jobCustomSettingsRT,
});

const setupMlModuleRequestParamsRT = rt.type({
indexPatternName: rt.string,
prefix: rt.string,
startDatafeed: rt.boolean,
jobOverrides: rt.array(rt.object),
jobOverrides: rt.array(setupMlModuleLogEntryRateJobOverridesRT),
datafeedOverrides: rt.array(rt.object),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export const useLogAnalysisJobs = ({
retry,
setupStatus: statusState.setupStatus,
viewResults,
fetchJobStatus,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { useReducer } from 'react';
import {
getDatafeedId,
getJobId,
isHealthyJobStatus,
JobStatus,
JobType,
SetupStatus,
bucketSpan,
} from '../../../../common/log_analysis';
import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api';
import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api';
Expand Down Expand Up @@ -101,9 +103,7 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction):
...state.jobStatus,
'log-entry-rate': getJobStatus(getJobId(spaceId, sourceId, 'log-entry-rate'))(payload),
};
const nextSetupStatus = Object.values(nextJobStatus).every(jobState =>
['started', 'finished'].includes(jobState)
)
const nextSetupStatus = Object.values(nextJobStatus).every(isHealthyJobStatus)
? 'skipped'
: 'required';
return {
Expand Down Expand Up @@ -181,6 +181,30 @@ const getJobStatus = (jobId: string) => (jobSummaries: FetchJobStatusResponsePay
}
)[0] || 'missing';

const isConfigurationConsistent = (
jobId: string,
sourceConfiguration: {
bucketSpan: number;
indexPattern: string;
timestampField: string;
}
) => (jobSummaries: FetchJobStatusResponsePayload): boolean =>
jobSummaries
.filter(jobSummary => jobSummary.id === jobId)
.every(jobSummary => {
if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) {
return false;
}

const jobConfiguration = jobSummary.fullJob.custom_settings.logs_source_config;

return (
jobConfiguration.bucketSpan === sourceConfiguration.bucketSpan &&
jobConfiguration.indexPattern === sourceConfiguration.indexPattern &&
jobConfiguration.timestampField === sourceConfiguration.timestampField
);
});

export const useStatusState = () => {
return useReducer(statusReducer, initialState);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { i18n } from '@kbn/i18n';
import React, { useContext, useEffect } from 'react';

import { isSetupStatusWithResults } from '../../../../common/log_analysis';
import { LoadingPage } from '../../../components/loading_page';
import { LogAnalysisCapabilities, LogAnalysisJobs } from '../../../containers/logs/log_analysis';
import { Source } from '../../../containers/source';
Expand Down Expand Up @@ -39,7 +40,7 @@ export const AnalysisPageContent = () => {
);
} else if (setupStatus === 'unknown') {
return <AnalysisSetupStatusUnknownContent retry={fetchJobStatus} />;
} else if (setupStatus === 'skipped' || setupStatus === 'hiddenAfterSuccess') {
} else if (isSetupStatusWithResults(setupStatus)) {
return (
<AnalysisResultsContent
sourceId={sourceId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ import { useInterval } from '../../../hooks/use_interval';
import { useTrackPageview } from '../../../hooks/use_track_metric';
import { FirstUseCallout } from './first_use';
import { LogRateResults } from './sections/log_rate';
import { LogAnalysisJobProblemIndicator } from '../../../components/logging/log_analysis_job_status';

const JOB_STATUS_POLLING_INTERVAL = 10000;
const JOB_STATUS_POLLING_INTERVAL = 30000;

export const AnalysisResultsContent = ({
sourceId,
Expand Down Expand Up @@ -163,6 +164,7 @@ export const AnalysisResultsContent = ({
<EuiPageContent>
<EuiPageContentBody>
{isFirstUse && !hasResults ? <FirstUseCallout /> : null}
<LogAnalysisJobProblemIndicator jobStatus={jobStatus['log-entry-rate']} />
<LogRateResults
isLoading={isLoading}
results={logEntryRate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
},
"custom_settings": {
"created_by": "ml-module-logs-ui-analysis",
"job_revision": 2
"job_revision": 3
}
}

0 comments on commit 5e00b3d

Please sign in to comment.