Skip to content

Commit

Permalink
[Osquery] Fix scheduled query status (#106600) (#110159)
Browse files Browse the repository at this point in the history
Co-authored-by: Patryk Kopyciński <[email protected]>
  • Loading branch information
kibanamachine and patrykkopycinski authored Aug 26, 2021
1 parent 8834c99 commit b194c3b
Show file tree
Hide file tree
Showing 26 changed files with 1,323 additions and 532 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@
"react-moment-proptypes": "^1.7.0",
"react-monaco-editor": "^0.41.2",
"react-popper-tooltip": "^2.10.1",
"react-query": "^3.18.1",
"react-query": "^3.21.0",
"react-redux": "^7.2.0",
"react-resizable": "^1.7.5",
"react-resize-detector": "^4.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@

import { EuiLink } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';

import { PLUGIN_ID } from '../../../fleet/common';
import { pagePathGetters } from '../../../fleet/public';
import { useKibana, isModifiedEvent, isLeftClickEvent } from '../common/lib/kibana';
import { useAgentPolicy } from './use_agent_policy';

const StyledEuiLink = styled(EuiLink)`
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
`;

interface AgentsPolicyLinkProps {
policyId: string;
}
Expand Down Expand Up @@ -46,10 +53,9 @@ const AgentsPolicyLinkComponent: React.FC<AgentsPolicyLinkProps> = ({ policyId }
);

return (
// eslint-disable-next-line @elastic/eui/href-or-on-click
<EuiLink href={href} onClick={handleClick}>
<StyledEuiLink href={href} onClick={handleClick}>
{data?.name ?? policyId}
</EuiLink>
</StyledEuiLink>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export const useAgentPolicy = ({ policyId, skip, silent }: UseAgentPolicy) => {
defaultMessage: 'Error while fetching agent policy details',
}),
}),
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
staleTime: Infinity,
}
);
};
58 changes: 58 additions & 0 deletions x-pack/plugins/osquery/public/agents/use_agent_policy_agent_ids.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.
*/

import { map } from 'lodash';
import { i18n } from '@kbn/i18n';
import { useQuery } from 'react-query';

import { AGENT_SAVED_OBJECT_TYPE, Agent } from '../../../fleet/common';
import { useErrorToast } from '../common/hooks/use_error_toast';
import { useKibana } from '../common/lib/kibana';

interface UseAgentPolicyAgentIdsProps {
agentPolicyId: string | undefined;
silent?: boolean;
skip?: boolean;
}

export const useAgentPolicyAgentIds = ({
agentPolicyId,
silent,
skip,
}: UseAgentPolicyAgentIdsProps) => {
const { http } = useKibana().services;
const setErrorToast = useErrorToast();

return useQuery<{ agents: Agent[] }, unknown, string[]>(
['agentPolicyAgentIds', agentPolicyId],
() => {
const kuery = `${AGENT_SAVED_OBJECT_TYPE}.policy_id:${agentPolicyId}`;

return http.get(`/internal/osquery/fleet_wrapper/agents`, {
query: {
kuery,
perPage: 10000,
},
});
},
{
select: (data) => map(data?.agents, 'id') || ([] as string[]),
enabled: !skip || !agentPolicyId,
onSuccess: () => setErrorToast(),
onError: (error) =>
!silent &&
setErrorToast(error as Error, {
title: i18n.translate('xpack.osquery.agents.fetchError', {
defaultMessage: 'Error while fetching agents',
}),
}),
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
}
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@ export const useOsqueryIntegrationStatus = () => {
defaultMessage: 'Error while fetching osquery integration',
}),
}),
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
staleTime: Infinity,
});
};
20 changes: 9 additions & 11 deletions x-pack/plugins/osquery/public/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
* 2.0.
*/

import React, { useCallback, useRef } from 'react';
import React, { useEffect, useState } from 'react';
import { EuiCodeEditor } from '@elastic/eui';
import useDebounce from 'react-use/lib/useDebounce';
import 'brace/theme/tomorrow';

import './osquery_mode.ts';
Expand All @@ -26,22 +27,19 @@ interface OsqueryEditorProps {
}

const OsqueryEditorComponent: React.FC<OsqueryEditorProps> = ({ defaultValue, onChange }) => {
const editorValue = useRef(defaultValue ?? '');
const [editorValue, setEditorValue] = useState(defaultValue ?? '');

const handleChange = useCallback((newValue: string) => {
editorValue.current = newValue;
}, []);
useDebounce(() => onChange(editorValue.replaceAll('\n', ' ').replaceAll(' ', ' ')), 500, [
editorValue,
]);

const onBlur = useCallback(() => {
onChange(editorValue.current.replaceAll('\n', ' ').replaceAll(' ', ' '));
}, [onChange]);
useEffect(() => setEditorValue(defaultValue), [defaultValue]);

return (
<EuiCodeEditor
onBlur={onBlur}
value={defaultValue}
value={editorValue}
mode="osquery"
onChange={handleChange}
onChange={setEditorValue}
theme="tomorrow"
name="osquery_editor"
setOptions={EDITOR_SET_OPTIONS}
Expand Down
13 changes: 11 additions & 2 deletions x-pack/plugins/osquery/public/live_queries/form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useMutation } from 'react-query';
import deepMerge from 'deepmerge';

Expand Down Expand Up @@ -114,7 +114,7 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
),
});

const { submit } = form;
const { setFieldValue, submit } = form;

const actionId = useMemo(() => data?.actions[0].action_id, [data?.actions]);
const agentIds = useMemo(() => data?.actions[0].agents, [data?.actions]);
Expand Down Expand Up @@ -253,6 +253,15 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
[queryFieldStepContent, resultsStepContent]
);

useEffect(() => {
if (defaultValue?.agentSelection) {
setFieldValue('agentSelection', defaultValue?.agentSelection);
}
if (defaultValue?.query) {
setFieldValue('query', defaultValue?.query);
}
}, [defaultValue, setFieldValue]);

return (
<>
<Form form={form}>{singleAgentMode ? singleAgentForm : <EuiSteps steps={formSteps} />}</Form>
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/osquery/public/results/results_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
ViewResultsInDiscoverAction,
ViewResultsInLensAction,
ViewResultsActionButtonType,
} from '../scheduled_query_groups/scheduled_query_group_queries_table';
} from '../scheduled_query_groups/scheduled_query_group_queries_status_table';
import { useActionResultsPrivileges } from '../action_results/use_action_privileges';
import { OSQUERY_INTEGRATION_NAME } from '../../common';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import moment from 'moment';
import moment from 'moment-timezone';
import {
EuiInMemoryTable,
EuiButton,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { startCase } from 'lodash';
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useMemo } from 'react';
Expand All @@ -26,7 +27,7 @@ const AddScheduledQueryGroupPageComponent = () => {

return {
name: osqueryIntegration.name,
title: osqueryIntegration.title,
title: osqueryIntegration.title ?? startCase(osqueryIntegration.name),
version: osqueryIntegration.version,
};
}, [osqueryIntegration]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import styled from 'styled-components';
import { useKibana, useRouterNavigate } from '../../../common/lib/kibana';
import { WithHeaderLayout } from '../../../components/layouts';
import { useScheduledQueryGroup } from '../../../scheduled_query_groups/use_scheduled_query_group';
import { ScheduledQueryGroupQueriesTable } from '../../../scheduled_query_groups/scheduled_query_group_queries_table';
import { ScheduledQueryGroupQueriesStatusTable } from '../../../scheduled_query_groups/scheduled_query_group_queries_status_table';
import { useBreadcrumbs } from '../../../common/hooks/use_breadcrumbs';
import { AgentsPolicyLink } from '../../../agent_policies/agents_policy_link';
import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge';
import { useAgentPolicyAgentIds } from '../../../agents/use_agent_policy_agent_ids';

const Divider = styled.div`
width: 0;
Expand All @@ -44,6 +45,10 @@ const ScheduledQueryGroupDetailsPageComponent = () => {
);

const { data } = useScheduledQueryGroup({ scheduledQueryGroupId });
const { data: agentIds } = useAgentPolicyAgentIds({
agentPolicyId: data?.policy_id,
skip: !data,
});

useBreadcrumbs('scheduled_query_group_details', { scheduledQueryGroupName: data?.name ?? '' });

Expand Down Expand Up @@ -131,7 +136,13 @@ const ScheduledQueryGroupDetailsPageComponent = () => {

return (
<WithHeaderLayout leftColumn={LeftColumn} rightColumn={RightColumn} rightColumnGrow={false}>
{data && <ScheduledQueryGroupQueriesTable data={data.inputs[0].streams} />}
{data && (
<ScheduledQueryGroupQueriesStatusTable
agentIds={agentIds}
scheduledQueryGroupName={data.name}
data={data.inputs[0].streams}
/>
)}
</WithHeaderLayout>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ const ScheduledQueryGroupFormComponent: React.FC<ScheduledQueryGroupFormProps> =
),
});

const { submit } = form;
const { setFieldValue, submit } = form;

const policyIdEuiFieldProps = useMemo(
() => ({ isDisabled: !!defaultValue, options: agentPolicyOptions }),
Expand Down Expand Up @@ -276,6 +276,10 @@ const ScheduledQueryGroupFormComponent: React.FC<ScheduledQueryGroupFormProps> =
};
}, [agentPoliciesById, policyId]);

const handleNameChange = useCallback((newName: string) => setFieldValue('name', newName), [
setFieldValue,
]);

const handleSaveClick = useCallback(() => {
if (currentPolicy.agentCount) {
setShowConfirmationModal(true);
Expand Down Expand Up @@ -343,6 +347,7 @@ const ScheduledQueryGroupFormComponent: React.FC<ScheduledQueryGroupFormProps> =
component={QueriesField}
scheduledQueryGroupId={defaultValue?.id ?? null}
integrationPackageVersion={integrationPackageVersion}
handleNameChange={handleNameChange}
/>

<CommonUseField path="enabled" component={GhostFormField} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { mapKeys, kebabCase } from 'lodash';
import { kebabCase } from 'lodash';
import { EuiLink, EuiFormRow, EuiFilePicker, EuiSpacer } from '@elastic/eui';
import React, { useCallback, useState, useRef } from 'react';
import { i18n } from '@kbn/i18n';
Expand All @@ -25,7 +25,7 @@ const ExamplePackLink = React.memo(() => (
ExamplePackLink.displayName = 'ExamplePackLink';

interface OsqueryPackUploaderProps {
onChange: (payload: Record<string, unknown>) => void;
onChange: (payload: Record<string, unknown>, packName: string) => void;
}

const OsqueryPackUploaderComponent: React.FC<OsqueryPackUploaderProps> = ({ onChange }) => {
Expand Down Expand Up @@ -61,12 +61,7 @@ const OsqueryPackUploaderComponent: React.FC<OsqueryPackUploaderProps> = ({ onCh
return;
}

const queriesJSON = mapKeys(
parsedContent?.queries,
(value, key) => `pack_${packName.current}_${key}`
);

onChange(queriesJSON);
onChange(parsedContent?.queries, packName.current);
// @ts-expect-error update types
filePickerRef.current?.removeFiles(new Event('fake'));
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSpacer } from '@elastic/eui';
import { produce } from 'immer';
import React, { useCallback, useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { satisfies } from 'semver';

import {
OsqueryManagerPackagePolicyInputStream,
Expand All @@ -23,6 +24,7 @@ import { OsqueryPackUploader } from './pack_uploader';
import { getSupportedPlatforms } from '../queries/platforms/helpers';

interface QueriesFieldProps {
handleNameChange: (name: string) => void;
field: FieldHook<OsqueryManagerPackagePolicyInput[]>;
integrationPackageVersion?: string | undefined;
scheduledQueryGroupId: string;
Expand Down Expand Up @@ -82,6 +84,7 @@ const getNewStream = (payload: GetNewStreamProps) =>

const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({
field,
handleNameChange,
integrationPackageVersion,
scheduledQueryGroupId,
}) => {
Expand Down Expand Up @@ -208,13 +211,18 @@ const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({
}, [setValue, tableSelectedItems]);

const handlePackUpload = useCallback(
(newQueries) => {
(newQueries, packName) => {
/* Osquery scheduled packs are supported since [email protected] */
const isOsqueryPackSupported = integrationPackageVersion
? satisfies(integrationPackageVersion, '>=0.5.0')
: false;

setValue(
produce((draft) => {
forEach(newQueries, (newQuery, newQueryId) => {
draft[0].streams.push(
getNewStream({
id: newQueryId,
id: isOsqueryPackSupported ? newQueryId : `pack_${packName}_${newQueryId}`,
interval: newQuery.interval,
query: newQuery.query,
version: newQuery.version,
Expand All @@ -227,8 +235,12 @@ const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({
return draft;
})
);

if (isOsqueryPackSupported) {
handleNameChange(packName);
}
},
[scheduledQueryGroupId, setValue]
[handleNameChange, integrationPackageVersion, scheduledQueryGroupId, setValue]
);

const tableData = useMemo(() => (field.value.length ? field.value[0].streams : []), [
Expand Down Expand Up @@ -277,7 +289,6 @@ const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({
<EuiSpacer />
{field.value && field.value[0].streams?.length ? (
<ScheduledQueryGroupQueriesTable
editMode={true}
data={tableData}
onEditClick={handleEditClick}
onDeleteClick={handleDeleteClick}
Expand Down
Loading

0 comments on commit b194c3b

Please sign in to comment.