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

[Osquery] Add multiline query support #131224

Merged
merged 27 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b6abc2d
Add multiline query support
tomsonpl Apr 29, 2022
599edb3
Add test to convertSOQueriesToPack
tomsonpl Apr 29, 2022
5470b30
rename
tomsonpl Apr 29, 2022
10702a0
Merge branch 'main' into osquery-multi-line
tomsonpl Apr 29, 2022
c3b34ef
Merge branch 'main' into osquery-multi-line
tomsonpl Apr 29, 2022
7fe8cb8
Merge branch 'main' into osquery-multi-line
kibanamachine May 2, 2022
c0f9b08
add osquery api integration tests
tomsonpl May 2, 2022
d53ca03
Merge remote-tracking branch 'origin/osquery-multi-line' into osquery…
tomsonpl May 2, 2022
da6982c
rename
tomsonpl May 2, 2022
b8f3e7d
rename 2
tomsonpl May 2, 2022
230903b
Merge branch 'main' into osquery-multi-line
kibanamachine May 4, 2022
b6f1c77
adjust tests
tomsonpl May 6, 2022
6691490
remove multi spaces
tomsonpl May 6, 2022
a3de11d
Merge branch 'main' into osquery-multi-line
kibanamachine May 8, 2022
835e720
Merge branch 'main' into osquery-multi-line
kibanamachine May 11, 2022
1d4065f
refactor last results in pack queries table
tomsonpl May 16, 2022
b054da8
Merge branch 'main' into osquery-multi-line
kibanamachine May 16, 2022
da4da32
fix translations
tomsonpl May 16, 2022
57049b6
Merge remote-tracking branch 'origin/osquery-multi-line' into osquery…
tomsonpl May 16, 2022
48cd1a7
change flag in argument to be an option instead removeMultiLines
tomsonpl May 17, 2022
fc1b55c
WIP combine query with aggs
tomsonpl May 17, 2022
69f55b8
fix
patrykkopycinski May 17, 2022
e71c1b7
fix frontend logic
tomsonpl May 18, 2022
4f19f4f
revert logic to the old way
tomsonpl May 18, 2022
637bc9d
Merge branch 'main' into osquery-multi-line
tomsonpl May 18, 2022
268d54a
remove old file
tomsonpl May 18, 2022
b47286a
fix typo
tomsonpl May 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 const removeMultilines = (query: string): string =>
query.replaceAll('\n', ' ').replaceAll(/ +/g, ' ');
4 changes: 1 addition & 3 deletions x-pack/plugins/osquery/public/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ const OsqueryEditorComponent: React.FC<OsqueryEditorProps> = ({
}) => {
const [editorValue, setEditorValue] = useState(defaultValue ?? '');

useDebounce(() => onChange(editorValue.replaceAll('\n', ' ').replaceAll(' ', ' ')), 500, [
editorValue,
]);
useDebounce(() => onChange(editorValue), 500, [editorValue]);

useEffect(() => setEditorValue(defaultValue), [defaultValue]);

Expand Down
267 changes: 180 additions & 87 deletions x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
EuiPanel,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n-react';
import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n-react';
import moment from 'moment-timezone';

import type {
Expand All @@ -32,6 +32,7 @@ import type {
} from '@kbn/lens-plugin/public';
import { DOCUMENT_FIELD_NAME as RECORDS_FIELD } from '@kbn/lens-plugin/common/constants';
import { FilterStateStore, DataView } from '@kbn/data-plugin/common';
import { removeMultilines } from '../../common/utils/build_query/remove_multilines';
import { useKibana } from '../common/lib/kibana';
import { OsqueryManagerPackagePolicyInputStream } from '../../common/types';
import { ScheduledQueryErrorsTable } from './scheduled_query_errors_table';
Expand Down Expand Up @@ -384,6 +385,13 @@ const ScheduledQueryExpandedContent = React.memo<ScheduledQueryExpandedContentPr
ScheduledQueryExpandedContent.displayName = 'ScheduledQueryExpandedContent';

interface ScheduledQueryLastResultsProps {
actionId: string;
queryId?: string;
interval: number;
logsDataView: DataView | undefined;
}

interface ScheduledQueryErrorsProps {
actionId: string;
queryId: string;
interval: number;
Expand All @@ -394,34 +402,20 @@ interface ScheduledQueryLastResultsProps {

const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
actionId,
queryId,
interval,
logsDataView,
toggleErrors,
expanded,
}) => {
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});

const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({
actionId,
interval,
logsDataView,
});

const handleErrorsToggle = useCallback(
() => toggleErrors({ queryId, interval }),
[queryId, interval, toggleErrors]
);

if (isLoading || errorsLoading) {
if (isLoading) {
return <EuiLoadingSpinner />;
}

if (!lastResultsData && !errorsData?.total) {
if (!lastResultsData) {
return <>{'-'}</>;
}

Expand All @@ -448,73 +442,115 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
'-'
)}
</EuiFlexItem>
<EuiFlexItem grow={4}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.docCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.osquery.queriesStatusTable.documentLabelText"
defaultMessage="{count, plural, one {Document} other {Documents}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: lastResultsData?.docCount as number }}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

<EuiFlexItem grow={4}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.uniqueAgentsCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.osquery.queriesStatusTable.agentsLabelText"
defaultMessage="{count, plural, one {Agent} other {Agents}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: lastResultsData?.uniqueAgentsCount ?? 0 }}
/>
</EuiFlexItem>
</EuiFlexGroup>
const DocsColumnResults: React.FC<ScheduledQueryLastResultsProps> = ({
actionId,
interval,
logsDataView,
}) => {
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});
if (isLoading) {
return <EuiLoadingSpinner />;
}

if (!lastResultsData) {
return <>{'-'}</>;
}

return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.docCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
</EuiFlexGroup>
);
};

<EuiFlexItem grow={5}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color={errorsData?.total ? 'accent' : 'subdued'}>
{errorsData?.total ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>

<EuiFlexItem grow={false}>
{' '}
<FormattedMessage
id="xpack.osquery.queriesStatusTable.errorsLabelText"
defaultMessage="{count, plural, one {Error} other {Errors}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: errorsData?.total as number }}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButtonIcon
isDisabled={!errorsData?.total}
onClick={handleErrorsToggle}
iconType={expanded ? 'arrowUp' : 'arrowDown'}
/>
</EuiFlexItem>
</EuiFlexGroup>
const AgentsColumnResults: React.FC<ScheduledQueryLastResultsProps> = ({
actionId,
interval,
logsDataView,
}) => {
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});
if (isLoading) {
return <EuiLoadingSpinner />;
}

if (!lastResultsData) {
return <>{'-'}</>;
}

return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.uniqueAgentsCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
</EuiFlexGroup>
);
};

const ErrorsColumnResults: React.FC<ScheduledQueryErrorsProps> = ({
actionId,
interval,
queryId,
toggleErrors,
expanded,
logsDataView,
}) => {
const handleErrorsToggle = useCallback(
() => toggleErrors({ queryId, interval }),
[toggleErrors, queryId, interval]
);

const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({
actionId,
interval,
logsDataView,
});
if (errorsLoading) {
return <EuiLoadingSpinner />;
}

if (!errorsData?.total) {
return <>{'-'}</>;
}

return (
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color={errorsData?.total ? 'accent' : 'subdued'}>
{errorsData?.total ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButtonIcon
isDisabled={!errorsData?.total}
onClick={handleErrorsToggle}
iconType={expanded ? 'arrowUp' : 'arrowDown'}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
};

const getPackActionId = (actionId: string, packName: string) => `pack_${packName}_${actionId}`;

interface PackViewInActionProps {
Expand Down Expand Up @@ -625,14 +661,18 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
fetchLogsDataView();
}, [dataViews]);

const renderQueryColumn = useCallback(
(query: string) => (
<EuiCodeBlock language="sql" fontSize="s" paddingSize="none" transparentBackground>
{query}
</EuiCodeBlock>
),
[]
);
const renderQueryColumn = useCallback((query: string, item) => {
const singleLine = removeMultilines(query);
const content = singleLine.length > 55 ? `${singleLine.substring(0, 55)}...` : singleLine;

return (
<EuiToolTip title={item.id} content={<EuiFlexItem>{query}</EuiFlexItem>}>
<EuiCodeBlock language="sql" fontSize="s" paddingSize="none" transparentBackground>
{content}
</EuiCodeBlock>
</EuiToolTip>
);
}, []);

const toggleErrors = useCallback(
({ queryId, interval }: { queryId: string; interval: number }) => {
Expand All @@ -658,14 +698,44 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
(item) => (
<ScheduledQueryLastResults
logsDataView={logsDataView}
queryId={item.id}
actionId={getPackActionId(item.id, packName)}
interval={item.interval}
/>
),
[packName, logsDataView]
);
const renderDocsColumn = useCallback(
(item) => (
<DocsColumnResults
logsDataView={logsDataView}
actionId={getPackActionId(item.id, packName)}
interval={item.interval}
/>
),
[logsDataView, packName]
);
const renderAgentsColumn = useCallback(
(item) => (
<AgentsColumnResults
logsDataView={logsDataView}
actionId={getPackActionId(item.id, packName)}
interval={item.interval}
/>
),
[logsDataView, packName]
);
const renderErrorsColumn = useCallback(
(item) => (
<ErrorsColumnResults
queryId={item.id}
interval={item.interval}
actionId={getPackActionId(item.id, packName)}
toggleErrors={toggleErrors}
logsDataView={logsDataView}
expanded={!!itemIdToExpandedRowMap[item.id]}
/>
),
[itemIdToExpandedRowMap, packName, toggleErrors, logsDataView]
[itemIdToExpandedRowMap, logsDataView, packName, toggleErrors]
);

const renderDiscoverResultsAction = useCallback(
Expand Down Expand Up @@ -705,6 +775,7 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
defaultMessage: 'ID',
}),
width: '15%',
truncateText: true,
},
{
field: 'interval',
Expand All @@ -719,13 +790,32 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
defaultMessage: 'Query',
}),
render: renderQueryColumn,
width: '20%',
width: '40%',
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.lastResultsColumnTitle', {
defaultMessage: 'Last results',
}),
render: renderLastResultsColumn,
width: '12%',
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.docsResultsColumnTitle', {
defaultMessage: 'Docs',
}),
render: renderDocsColumn,
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.agentsResultsColumnTitle', {
defaultMessage: 'Agents',
}),
render: renderAgentsColumn,
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.errorsResultsColumnTitle', {
defaultMessage: 'Errors',
}),
render: renderErrorsColumn,
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.viewResultsColumnTitle', {
Expand All @@ -745,6 +835,9 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
[
renderQueryColumn,
renderLastResultsColumn,
renderDocsColumn,
renderAgentsColumn,
renderErrorsColumn,
renderDiscoverResultsAction,
renderLensResultsAction,
]
Expand Down
Loading