Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Adding option to create AD jobs without starting the datafeed #77484

Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,13 @@ export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToP
navigateToPath('/jobs/new_job');
}

export function advancedStartDatafeed(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) {
stashCombinedJob(jobCreator, false, false);
export function advancedStartDatafeed(
jobCreator: JobCreatorType | null,
navigateToPath: NavigateToPath
) {
if (jobCreator !== null) {
stashCombinedJob(jobCreator, false, false);
}
navigateToPath('/jobs');
}

Expand Down
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 { StartDatafeedSwitch } from './start_datafeed_switch';
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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, { FC } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSwitch, EuiFormRow, EuiSpacer } from '@elastic/eui';
interface Props {
startDatafeed: boolean;
setStartDatafeed(start: boolean): void;
disabled?: boolean;
}

export const StartDatafeedSwitch: FC<Props> = ({
startDatafeed,
setStartDatafeed,
disabled = false,
}) => {
return (
<>
<EuiSpacer />
<EuiFormRow
helpText={i18n.translate(
'xpack.ml.newJob.wizard.summaryStep.startDatafeedCheckboxHelpText',
{
defaultMessage: 'If unselected, job can be started later from the jobs list.',
}
)}
>
<EuiSwitch
data-test-subj="mlJobWizardStartDatafeedCheckbox"
label={i18n.translate('xpack.ml.newJob.wizard.summaryStep.startDatafeedCheckbox', {
defaultMessage: 'Start immediately',
})}
checked={startDatafeed}
onChange={(e) => setStartDatafeed(e.target.checked)}
disabled={disabled}
/>
</EuiFormRow>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { DatafeedDetails } from './components/datafeed_details';
import { DetectorChart } from './components/detector_chart';
import { JobProgress } from './components/job_progress';
import { PostSaveOptions } from './components/post_save_options';
import { StartDatafeedSwitch } from './components/start_datafeed_switch';
import { toastNotificationServiceProvider } from '../../../../../services/toast_notification_service';
import {
convertToAdvancedJob,
Expand All @@ -50,6 +51,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
const [creatingJob, setCreatingJob] = useState(false);
const [isValid, setIsValid] = useState(jobValidator.validationSummary.basic);
const [jobRunner, setJobRunner] = useState<JobRunner | null>(null);
const [startDatafeed, setStartDatafeed] = useState(true);

const isAdvanced = isAdvancedJobCreator(jobCreator);
const jsonEditorMode = isAdvanced ? EDITOR_MODE.EDITABLE : EDITOR_MODE.READONLY;
Expand All @@ -59,15 +61,17 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
}, []);

async function start() {
setCreatingJob(true);
if (isAdvanced) {
await startAdvanced();
await createAdvancedJob();
} else if (startDatafeed === true) {
await createAndStartJob();
} else {
await startInline();
await createAdvancedJob(false);
}
}

async function startInline() {
setCreatingJob(true);
async function createAndStartJob() {
try {
const jr = await jobCreator.createAndStartJob();
setJobRunner(jr);
Expand All @@ -76,12 +80,11 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
}
}

async function startAdvanced() {
setCreatingJob(true);
async function createAdvancedJob(showStartModal: boolean = true) {
try {
await jobCreator.createJob();
await jobCreator.createDatafeed();
advancedStartDatafeed(jobCreator, navigateToPath);
advancedStartDatafeed(showStartModal ? jobCreator : null, navigateToPath);
} catch (error) {
handleJobCreationError(error);
}
Expand Down Expand Up @@ -131,6 +134,14 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
<EuiSpacer size="m" />
<JobDetails />

{isAdvanced === false && (
<StartDatafeedSwitch
jgowdyelastic marked this conversation as resolved.
Show resolved Hide resolved
startDatafeed={startDatafeed}
setStartDatafeed={setStartDatafeed}
disabled={creatingJob}
/>
)}

{isAdvanced && (
<Fragment>
<EuiHorizontalRule />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 06:01:13.914'
Expand Down Expand Up @@ -230,7 +230,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 06:01:13.914'
Expand Down
1 change: 1 addition & 0 deletions x-pack/test/functional/apps/ml/anomaly_detection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
this.tags(['skipFirefox']);

loadTestFile(require.resolve('./single_metric_job'));
loadTestFile(require.resolve('./single_metric_job_without_datafeed_start'));
loadTestFile(require.resolve('./multi_metric_job'));
loadTestFile(require.resolve('./population_job'));
loadTestFile(require.resolve('./saved_search_job'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down Expand Up @@ -235,7 +235,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
Expand Down Expand Up @@ -261,7 +261,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down Expand Up @@ -212,7 +212,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';

export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');

const jobId = `fq_single_1_${Date.now()}`;
const aggAndFieldIdentifier = 'Mean(responsetime)';
const bucketSpan = '30m';

function getExpectedRow(expectedJobId: string) {
return {
id: expectedJobId,
description: '',
jobGroups: [],
recordCount: '0',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '',
};
}

function getExpectedCounts(expectedJobId: string) {
return {
job_id: expectedJobId,
processed_record_count: '0',
processed_field_count: '0',
input_bytes: '0.0 B',
input_field_count: '0',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '0',
};
}

function getExpectedModelSizeStats(expectedJobId: string) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
total_by_field_count: '0',
total_over_field_count: '0',
total_partition_field_count: '0',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
};
}

describe('single metric without datafeed start', function () {
this.tags(['mlqa']);
before(async () => {
await esArchiver.loadIfNeeded('ml/farequote');
await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp');
await ml.testResources.setKibanaTimeZoneToUTC();

await ml.securityUI.loginAsMlPowerUser();
});

after(async () => {
await ml.api.cleanMlIndices();
});

it('job creation loads the single metric wizard for the source data', async () => {
await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();

await ml.testExecution.logTestStep('job creation loads the new job source selection page');
await ml.jobManagement.navigateToNewJobSourceSelection();

await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_farequote');

await ml.testExecution.logTestStep('job creation loads the single metric job wizard page');
await ml.jobTypeSelection.selectSingleMetricJob();
});

it('job creation navigates through the single metric wizard and sets all needed fields', async () => {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);

await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();

await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();

await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, true);
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');

await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);

await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();

await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);

await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();

await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});

it('job creation runs the job and displays it correctly in the job list', async () => {
darnautov marked this conversation as resolved.
Show resolved Hide resolved
await ml.testExecution.logTestStep('job creation creates the job and finishes processing');

await ml.jobWizardCommon.assertStartDatafeedSwitchExists();
await ml.jobWizardCommon.toggleStartDatafeedSwitch(false);

await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobWithoutDatafeedStart();

await ml.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId, 1);

await ml.testExecution.logTestStep(
'job creation displays details for the created job in the job list'
);
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId));

await ml.jobTable.assertJobRowDetailsCounts(
jobId,
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
});
});
}
Loading