fixing conflicts (#29120)

This commit is contained in:
James Gowdy 2019-01-22 17:53:22 +00:00 committed by GitHub
parent be1026fe43
commit 9c850fa427
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 205 additions and 130 deletions

View file

@ -18,7 +18,7 @@ import {
import { MODE as DATAVISUALIZER_MODE } from '../file_datavisualizer_view';
export function BottomBar({ showBar, mode, changeMode, onCancel }) {
export function BottomBar({ showBar, mode, changeMode, onCancel, disableImport }) {
if (showBar) {
if (mode === DATAVISUALIZER_MODE.READ) {
return (
@ -27,6 +27,7 @@ export function BottomBar({ showBar, mode, changeMode, onCancel }) {
<EuiFlexItem grow={false}>
<EuiButton
fill
isDisabled={(disableImport === true)}
onClick={() => changeMode(DATAVISUALIZER_MODE.IMPORT)}
>
<FormattedMessage

View file

@ -29,7 +29,8 @@ import {
createUrlOverrides,
processResults,
reduceData,
} from './utils';
hasImportPermission,
} from '../utils';
export const MODE = {
READ: 0,
@ -55,6 +56,7 @@ export class FileDataVisualizerView extends Component {
mode: MODE.READ,
isEditFlyoutVisible: false,
bottomBarVisible: false,
hasPermissionToImport: false,
};
this.overrides = {};
@ -62,6 +64,14 @@ export class FileDataVisualizerView extends Component {
this.originalSettings = {};
}
async componentDidMount() {
// check the user has the correct permission to import data.
// note, calling hasImportPermission with no arguments just checks the
// cluster privileges, the user will still need index privileges to create and ingest
const hasPermissionToImport = await hasImportPermission();
this.setState({ hasPermissionToImport });
}
onFilePickerChange = (files) => {
this.overrides = {};
@ -242,6 +252,7 @@ export class FileDataVisualizerView extends Component {
mode,
isEditFlyoutVisible,
bottomBarVisible,
hasPermissionToImport,
} = this.state;
const fields = (results !== undefined && results.field_stats !== undefined) ? Object.keys(results.field_stats) : [];
@ -302,6 +313,7 @@ export class FileDataVisualizerView extends Component {
mode={MODE.READ}
changeMode={this.changeMode}
onCancel={this.onCancel}
disableImport={(hasPermissionToImport === false)}
/>
<BottomPadding />

View file

@ -69,6 +69,13 @@ function title(statuses) {
defaultMessage="Error creating index pattern"
/>
);
case statuses.permissionCheckStatus:
return (
<FormattedMessage
id="xpack.ml.fileDatavisualizer.importErrors.checkingPermissionErrorMessage"
defaultMessage="Import permissions error"
/>
);
default:
return (
<FormattedMessage

View file

@ -17,6 +17,7 @@ import {
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { importerFactory } from './importer';
import { ResultsLinks } from '../results_links';
import { ImportProgress, IMPORT_STATUS } from '../import_progress';
@ -26,6 +27,7 @@ import { ImportSettings } from '../import_settings';
import { ExperimentalBadge } from '../experimental_badge';
import { getIndexPatternNames, refreshIndexPatterns } from '../../../util/index_utils';
import { ml } from '../../../services/ml_api_service';
import { hasImportPermission } from '../utils';
const DEFAULT_TIME_FIELD = '@timestamp';
const CONFIG_MODE = { SIMPLE: 0, ADVANCED: 1 };
@ -41,6 +43,7 @@ const DEFAULT_STATE = {
indexCreatedStatus: IMPORT_STATUS.INCOMPLETE,
indexPatternCreatedStatus: IMPORT_STATUS.INCOMPLETE,
ingestPipelineCreatedStatus: IMPORT_STATUS.INCOMPLETE,
permissionCheckStatus: IMPORT_STATUS.INCOMPLETE,
uploadProgress: 0,
uploadStatus: IMPORT_STATUS.INCOMPLETE,
createIndexPattern: true,
@ -111,128 +114,151 @@ export class ImportView extends Component {
if (index !== '') {
this.setState({
importing: true,
imported: false,
reading: true,
initialized: true,
}, () => {
this.props.hideBottomBar();
setTimeout(async () => {
let success = false;
const createPipeline = (pipelineString !== '');
let indexCreationSettings = {};
try {
const settings = JSON.parse(indexSettingsString);
const mappings = JSON.parse(mappingsString);
indexCreationSettings = {
settings,
mappings,
};
if (createPipeline) {
indexCreationSettings.pipeline = JSON.parse(pipelineString);
errors,
}, async () => {
// check to see if the user has permission to create and ingest data into the specified index
if (await hasImportPermission(index) === false) {
errors.push(i18n.translate('xpack.ml.fileDatavisualizer.importView.importPermissionError', {
defaultMessage: 'You do not have permission to create or import data into index {index}.',
values: {
index
}
}));
this.setState({
permissionCheckStatus: IMPORT_STATUS.FAILED,
importing: false,
imported: false,
errors
});
return;
}
// if an @timestamp field has been added to the
// mappings, use this field as the time field.
// This relies on the field being populated by
// the ingest pipeline on ingest
if (mappings[DEFAULT_TIME_FIELD] !== undefined) {
timeFieldName = DEFAULT_TIME_FIELD;
this.setState({ timeFieldName });
}
this.setState({
importing: true,
imported: false,
reading: true,
initialized: true,
permissionCheckStatus: IMPORT_STATUS.COMPLETE,
}, () => {
this.props.hideBottomBar();
setTimeout(async () => {
let success = false;
const createPipeline = (pipelineString !== '');
success = true;
} catch (error) {
success = false;
errors.push(error);
}
if (success) {
const importer = importerFactory(format, results, indexCreationSettings);
if (importer !== undefined) {
const readResp = await importer.read(fileContents, this.setReadProgress);
success = readResp.success;
this.setState({
readStatus: success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
reading: false,
});
if (readResp.success === false) {
console.error(readResp.error);
errors.push(readResp.error);
let indexCreationSettings = {};
try {
const settings = JSON.parse(indexSettingsString);
const mappings = JSON.parse(mappingsString);
indexCreationSettings = {
settings,
mappings,
};
if (createPipeline) {
indexCreationSettings.pipeline = JSON.parse(pipelineString);
}
if (success) {
const initializeImportResp = await importer.initializeImport(index);
// if an @timestamp field has been added to the
// mappings, use this field as the time field.
// This relies on the field being populated by
// the ingest pipeline on ingest
if (mappings[DEFAULT_TIME_FIELD] !== undefined) {
timeFieldName = DEFAULT_TIME_FIELD;
this.setState({ timeFieldName });
}
const indexCreated = (initializeImportResp.index !== undefined);
success = true;
} catch (error) {
success = false;
errors.push(error);
}
if (success) {
const importer = importerFactory(format, results, indexCreationSettings);
if (importer !== undefined) {
const readResp = await importer.read(fileContents, this.setReadProgress);
success = readResp.success;
this.setState({
indexCreatedStatus: indexCreated ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
readStatus: success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
reading: false,
});
if (createPipeline) {
const pipelineCreated = (initializeImportResp.pipelineId !== undefined);
if (indexCreated) {
this.setState({
ingestPipelineCreatedStatus: pipelineCreated ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
ingestPipelineId: pipelineCreated ? initializeImportResp.pipelineId : '',
});
}
success = (indexCreated && pipelineCreated);
} else {
success = indexCreated;
if (readResp.success === false) {
console.error(readResp.error);
errors.push(readResp.error);
}
if (success) {
const importId = initializeImportResp.id;
const pipelineId = initializeImportResp.pipelineId;
const importResp = await importer.import(importId, index, pipelineId, this.setImportProgress);
success = importResp.success;
const initializeImportResp = await importer.initializeImport(index);
const indexCreated = (initializeImportResp.index !== undefined);
this.setState({
uploadStatus: importResp.success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
importFailures: importResp.failures,
docCount: importResp.docCount,
indexCreatedStatus: indexCreated ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
});
if (success) {
if (createIndexPattern) {
const indexPatternName = (indexPattern === '') ? index : indexPattern;
const indexPatternResp = await createKibanaIndexPattern(
indexPatternName,
indexPatterns,
timeFieldName,
kibanaConfig,
);
success = indexPatternResp.success;
if (createPipeline) {
const pipelineCreated = (initializeImportResp.pipelineId !== undefined);
if (indexCreated) {
this.setState({
indexPatternCreatedStatus: indexPatternResp.success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
indexPatternId: indexPatternResp.id,
ingestPipelineCreatedStatus: pipelineCreated ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
ingestPipelineId: pipelineCreated ? initializeImportResp.pipelineId : '',
});
if (indexPatternResp.success === false) {
errors.push(indexPatternResp.error);
}
success = (indexCreated && pipelineCreated);
} else {
success = indexCreated;
}
if (success) {
const importId = initializeImportResp.id;
const pipelineId = initializeImportResp.pipelineId;
const importResp = await importer.import(importId, index, pipelineId, this.setImportProgress);
success = importResp.success;
this.setState({
uploadStatus: importResp.success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
importFailures: importResp.failures,
docCount: importResp.docCount,
});
if (success) {
if (createIndexPattern) {
const indexPatternName = (indexPattern === '') ? index : indexPattern;
const indexPatternResp = await createKibanaIndexPattern(
indexPatternName,
indexPatterns,
timeFieldName,
kibanaConfig,
);
success = indexPatternResp.success;
this.setState({
indexPatternCreatedStatus: indexPatternResp.success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED,
indexPatternId: indexPatternResp.id,
});
if (indexPatternResp.success === false) {
errors.push(indexPatternResp.error);
}
}
} else {
errors.push(importResp.error);
}
} else {
errors.push(importResp.error);
errors.push(initializeImportResp.error);
}
} else {
errors.push(initializeImportResp.error);
}
}
}
}
showBottomBar();
showBottomBar();
this.setState({
importing: false,
imported: success,
errors,
});
this.setState({
importing: false,
imported: success,
errors,
});
}, 500);
}, 500);
});
});
}
}
@ -322,6 +348,7 @@ export class ImportView extends Component {
indexCreatedStatus,
ingestPipelineCreatedStatus,
indexPatternCreatedStatus,
permissionCheckStatus,
uploadProgress,
uploadStatus,
createIndexPattern,
@ -344,6 +371,7 @@ export class ImportView extends Component {
indexCreatedStatus,
ingestPipelineCreatedStatus,
indexPatternCreatedStatus,
permissionCheckStatus,
uploadProgress,
uploadStatus,
createIndexPattern,
@ -466,21 +494,20 @@ export class ImportView extends Component {
</EuiPanel>
{
(errors.length > 0) &&
<React.Fragment>
<EuiSpacer size="m" />
<ImportErrors
errors={errors}
statuses={statuses}
/>
</React.Fragment>
}
</React.Fragment>
}
{
(errors.length > 0) &&
<React.Fragment>
<EuiSpacer size="m" />
<ImportErrors
errors={errors}
statuses={statuses}
/>
</React.Fragment>
}
</React.Fragment>
);
}

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './utils';

View file

@ -6,6 +6,7 @@
import { overrideDefaults } from './overrides';
import { isEqual } from 'lodash';
import { ml } from '../../../services/ml_api_service';
export function readFile(file) {
return new Promise((resolve, reject) => {
@ -105,3 +106,30 @@ export function processResults(results) {
grokPattern: results.grok_pattern,
};
}
// a check for the minimum privileges needed to create and ingest data into an index.
// if called with no indexName, the check will just look for the minimum cluster privileges.
export async function hasImportPermission(indexName) {
const priv = {
cluster: [
'cluster:monitor/nodes/info',
'cluster:admin/ingest/pipeline/put',
]
};
if (indexName !== undefined) {
priv.index = [
{
names: [indexName],
privileges: [
'indices:data/write/bulk',
'indices:data/write/index',
'indices:admin/create',
]
}
];
}
const resp = await ml.checkPrivilege(priv);
return (resp.securityDisabled === true || resp.has_all_requested === true);
}

View file

@ -41,7 +41,7 @@ export function importDataProvider(callWithRequest) {
}
let failures = [];
if (data.length && indexExits(index)) {
if (data.length) {
const resp = await indexData(index, createdPipelineId, data);
if (resp.success === false) {
if (resp.ingestError) {
@ -78,26 +78,22 @@ export function importDataProvider(callWithRequest) {
}
async function createIndex(index, settings, mappings) {
if (await indexExits(index) === false) {
const body = {
mappings: {
_doc: {
_meta: {
created_by: INDEX_META_DATA_CREATED_BY
},
properties: mappings
const body = {
mappings: {
_doc: {
_meta: {
created_by: INDEX_META_DATA_CREATED_BY
},
}
};
if (settings && Object.keys(settings).length) {
body.settings = settings;
properties: mappings
},
}
};
await callWithRequest('indices.create', { index, body });
} else {
throw `${index} already exists.`;
if (settings && Object.keys(settings).length) {
body.settings = settings;
}
await callWithRequest('indices.create', { index, body });
}
async function indexData(index, pipelineId, data) {
@ -148,10 +144,6 @@ export function importDataProvider(callWithRequest) {
}
async function indexExits(index) {
return await callWithRequest('indices.exists', { index });
}
async function createPipeline(id, pipeline) {
return await callWithRequest('ingest.putPipeline', { id, body: pipeline });
}