Skip to content

Commit

Permalink
Add button for adding index.query.default_field setting to metricbe…
Browse files Browse the repository at this point in the history
…at indices (elastic#32829)

* Add button for adding `index.query.default_field` setting to metricbeat indices

* Add button to 'group by index' view

* Refactor to more generic API

* Remove comment

* Update functional tests
  • Loading branch information
joshdover authored Mar 12, 2019
1 parent baf7758 commit c97eff3
Show file tree
Hide file tree
Showing 14 changed files with 11,240 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ import {
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { FixDefaultFieldsButton } from './default_fields/button';
import { DeleteTasksButton } from './delete_tasks_button';
import { ReindexButton } from './reindex';

interface DeprecationCellProps {
items?: Array<{ title?: string; body: string }>;
reindexIndexName?: string;
deleteIndexName?: string;
indexName?: string;
reindex?: boolean;
deleteIndex?: boolean;
needsDefaultFields?: boolean;
docUrl?: string;
headline?: string;
healthColor?: string;
Expand All @@ -35,8 +38,10 @@ interface DeprecationCellProps {
export const DeprecationCell: StatelessComponent<DeprecationCellProps> = ({
headline,
healthColor,
reindexIndexName,
deleteIndexName,
indexName,
reindex,
deleteIndex,
needsDefaultFields,
docUrl,
items = [],
children,
Expand Down Expand Up @@ -78,17 +83,23 @@ export const DeprecationCell: StatelessComponent<DeprecationCellProps> = ({
))}
</EuiFlexItem>

{reindexIndexName && (
{reindex && (
<EuiFlexItem grow={false}>
<ReindexButton indexName={reindexIndexName} />
<ReindexButton indexName={indexName!} />
</EuiFlexItem>
)}

{deleteIndexName && (
{deleteIndex && (
<EuiFlexItem grow={false}>
<DeleteTasksButton />
</EuiFlexItem>
)}

{needsDefaultFields && (
<EuiFlexItem grow={false}>
<FixDefaultFieldsButton indexName={indexName!} />
</EuiFlexItem>
)}
</EuiFlexGroup>

<EuiSpacer size="s" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* 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, { ReactNode } from 'react';

import { EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { kfetch } from 'ui/kfetch';
import { LoadingState } from '../../../../types';

/**
* Field types used by Metricbeat to generate the default_field setting.
* Matches Beats code here:
* https://github.com/elastic/beats/blob/eee127cb59b56f2ed7c7e317398c3f79c4158216/libbeat/template/processor.go#L104
*/
const METRICBEAT_DEFAULT_FIELD_TYPES: ReadonlySet<string> = new Set(['keyword', 'text', 'ip']);
const METRICBEAT_OTHER_DEFAULT_FIELDS: ReadonlySet<string> = new Set(['fields.*']);

interface FixDefaultFieldsButtonProps {
indexName: string;
}

interface FixDefaultFieldsButtonState {
fixLoadingState?: LoadingState;
}

/**
* Renders a button if given index is a valid Metricbeat index to add a default_field setting.
*/
export class FixDefaultFieldsButton extends React.Component<
FixDefaultFieldsButtonProps,
FixDefaultFieldsButtonState
> {
constructor(props: FixDefaultFieldsButtonProps) {
super(props);
this.state = {};
}

public render() {
const { fixLoadingState } = this.state;

if (!this.isMetricbeatIndex()) {
return null;
}

const buttonProps: any = { size: 's', onClick: this.fixMetricbeatIndex };
let buttonContent: ReactNode;

switch (fixLoadingState) {
case LoadingState.Loading:
buttonProps.disabled = true;
buttonProps.isLoading = true;
buttonContent = (
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.fixMetricbeatIndexButton.fixingLabel"
defaultMessage="Fixing…"
/>
);
break;
case LoadingState.Success:
buttonProps.iconSide = 'left';
buttonProps.iconType = 'check';
buttonProps.disabled = true;
buttonContent = (
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.fixMetricbeatIndexButton.fixedLabel"
defaultMessage="Fixed"
/>
);
break;
case LoadingState.Error:
buttonProps.color = 'danger';
buttonProps.iconSide = 'left';
buttonProps.iconType = 'cross';
buttonContent = (
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.fixMetricbeatIndexButton.failedLabel"
defaultMessage="Failed"
/>
);
break;
default:
buttonContent = (
<FormattedMessage
id="xpack.upgradeAssistant.checkupTab.fixMetricbeatIndexButton.reindexLabel"
defaultMessage="Fix"
/>
);
}

return <EuiButton {...buttonProps}>{buttonContent}</EuiButton>;
}

private isMetricbeatIndex = () => {
return this.props.indexName.startsWith('metricbeat-');
};

private fixMetricbeatIndex = async () => {
if (!this.isMetricbeatIndex()) {
return;
}

this.setState({
fixLoadingState: LoadingState.Loading,
});

try {
await kfetch({
pathname: `/api/upgrade_assistant/add_query_default_field/${this.props.indexName}`,
method: 'POST',
body: JSON.stringify({
fieldTypes: [...METRICBEAT_DEFAULT_FIELD_TYPES],
otherFields: [...METRICBEAT_OTHER_DEFAULT_FIELDS],
}),
});

this.setState({
fixLoadingState: LoadingState.Success,
});
} catch (e) {
this.setState({
fixLoadingState: LoadingState.Error,
});
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from 'react';

import { EuiBasicTable } from '@elastic/eui';
import { injectI18n } from '@kbn/i18n/react';
import { FixDefaultFieldsButton } from './default_fields/button';
import { DeleteTasksButton } from './delete_tasks_button';
import { ReindexButton } from './reindex';

Expand All @@ -18,6 +19,7 @@ export interface IndexDeprecationDetails {
index: string;
reindex: boolean;
delete: boolean;
needsDefaultFields: boolean;
details?: string;
}

Expand Down Expand Up @@ -137,19 +139,24 @@ export class IndexDeprecationTableUI extends React.Component<
// NOTE: this naive implementation assumes all indices in the table are
// should show the reindex button. This should work for known usecases.
const { indices } = this.props;
if (!indices.find(i => i.reindex || i.delete)) {
const showDeleteButton = indices.find(i => i.delete === true);
const showReindexButton = indices.find(i => i.reindex === true);
const showNeedsDefaultFieldsButton = indices.find(i => i.needsDefaultFields === true);
if (!showDeleteButton && !showReindexButton && !showNeedsDefaultFieldsButton) {
return null;
}

return {
actions: [
{
render(indexDep: IndexDeprecationDetails) {
return indexDep.delete ? (
<DeleteTasksButton />
) : (
<ReindexButton indexName={indexDep.index} />
);
if (showDeleteButton) {
return <DeleteTasksButton />;
} else if (showReindexButton) {
return <ReindexButton indexName={indexDep.index!} />;
} else {
return <FixDefaultFieldsButton indexName={indexDep.index!} />;
}
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ describe('DeprecationList', () => {
"delete": false,
"details": undefined,
"index": "0",
"needsDefaultFields": false,
"reindex": false,
},
Object {
"delete": false,
"details": undefined,
"index": "1",
"needsDefaultFields": false,
"reindex": false,
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { IndexDeprecationDetails, IndexDeprecationTable } from './index_table';

const OLD_INDEX_MESSAGE = `Index created before ${CURRENT_MAJOR_VERSION}.0`;
const DELETE_INDEX_MESSAGE = `.tasks index must be re-created`;
const NEEDS_DEFAULT_FIELD_MESSAGE = 'Number of fields exceeds automatic field expansion limit';

const sortByLevelDesc = (a: DeprecationInfo, b: DeprecationInfo) => {
return -1 * (LEVEL_MAP[a.level] - LEVEL_MAP[b.level]);
Expand All @@ -38,10 +39,10 @@ const MessageDeprecation: StatelessComponent<{ deprecation: EnrichedDeprecationI
<DeprecationCell
headline={deprecation.message}
healthColor={COLOR_MAP[deprecation.level]}
reindexIndexName={deprecation.message === OLD_INDEX_MESSAGE ? deprecation.index! : undefined}
deleteIndexName={
deprecation.message === DELETE_INDEX_MESSAGE ? deprecation.index! : undefined
}
indexName={deprecation.index}
reindex={deprecation.message === OLD_INDEX_MESSAGE}
deleteIndex={deprecation.message === DELETE_INDEX_MESSAGE}
needsDefaultFields={deprecation.message === NEEDS_DEFAULT_FIELD_MESSAGE}
docUrl={deprecation.url}
items={items}
/>
Expand Down Expand Up @@ -97,6 +98,7 @@ export const DeprecationList: StatelessComponent<{
details: dep.details,
reindex: dep.message === OLD_INDEX_MESSAGE,
delete: dep.message === DELETE_INDEX_MESSAGE,
needsDefaultFields: dep.message === NEEDS_DEFAULT_FIELD_MESSAGE,
}));

return <IndexDeprecation indices={indices} deprecation={deprecations[0]} />;
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/upgrade_assistant/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import { makeUpgradeAssistantUsageCollector } from './lib/telemetry';
import { registerClusterCheckupRoutes } from './routes/cluster_checkup';
import { registerDeleteTasksRoutes } from './routes/delete_tasks';
import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging';
import { registerQueryDefaultFieldRoutes } from './routes/query_default_field';
import { registerReindexIndicesRoutes, registerReindexWorker } from './routes/reindex_indices';
import { registerTelemetryRoutes } from './routes/telemetry';

export function initServer(server: Legacy.Server) {
registerClusterCheckupRoutes(server);
registerDeleteTasksRoutes(server);
registerDeprecationLoggingRoutes(server);
registerQueryDefaultFieldRoutes(server);

// The ReindexWorker uses a map of request headers that contain the authentication credentials
// for a given reindex. We cannot currently store these in an the .kibana index b/c we do not
Expand Down
Loading

0 comments on commit c97eff3

Please sign in to comment.