- {targetsTotalCount}
+ {targetsTotalCount.toLocaleString()}
{pluralizeHost(targetsTotalCount)} targeted
diff --git a/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx b/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx
index 1c06cdab66..d871253183 100644
--- a/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx
+++ b/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx
@@ -20,12 +20,15 @@ import TabsWrapper from "components/TabsWrapper";
import ShowQueryModal from "components/modals/ShowQueryModal";
import QueryResultsHeading from "components/queries/queryResults/QueryResultsHeading";
import AwaitingResults from "components/queries/queryResults/AwaitingResults";
+import InfoBanner from "components/InfoBanner";
+import CustomLink from "components/CustomLink";
import generateColumnConfigsFromRows from "./QueryResultsTableConfig";
interface IQueryResultsProps {
campaign: ICampaign;
isQueryFinished: boolean;
+ isQueryClipped: boolean;
queryName?: string;
onRunQuery: () => void;
onStopQuery: (evt: React.MouseEvent) => void;
@@ -44,6 +47,7 @@ const NAV_TITLES = {
const QueryResults = ({
campaign,
isQueryFinished,
+ isQueryClipped,
queryName,
onRunQuery,
onStopQuery,
@@ -249,6 +253,24 @@ const QueryResults = ({
onClickRunAgain={onRunAgain}
onClickStop={onStopQuery}
/>
+ {isQueryClipped && (
+
+ }
+ >
+
+ Results clipped. A sample of this query's results and
+ errors is included below. Please target fewer hosts at once to build
+ a full set of results.
+
+
+ )}
setNavTabIndex(i)}>
@@ -256,7 +278,9 @@ const QueryResults = ({
{errors?.length > 0 && (
- {errors.length}
+
+ {errors.length.toLocaleString()}
+
)}
{NAV_TITLES.ERRORS}
diff --git a/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx b/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx
index 5c428e4761..a11ae5a377 100644
--- a/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx
+++ b/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx
@@ -9,7 +9,6 @@ import { QueryContext } from "context/query";
import { LIVE_QUERY_STEPS, DOCUMENT_TITLE_SUFFIX } from "utilities/constants";
import queryAPI from "services/entities/queries";
import hostAPI from "services/entities/hosts";
-import statusAPI from "services/entities/status";
import { IHost, IHostResponse } from "interfaces/host";
import { ILabel } from "interfaces/label";
import { ITeam } from "interfaces/team";
@@ -22,7 +21,6 @@ import MainContent from "components/MainContent";
import SelectTargets from "components/LiveQuery/SelectTargets";
import RunQuery from "pages/queries/live/screens/RunQuery";
-import useTeamIdParam from "hooks/useTeamIdParam";
interface IRunQueryPageProps {
router: InjectedRouter;
@@ -42,25 +40,9 @@ const RunQueryPage = ({
location,
}: IRunQueryPageProps): JSX.Element => {
const queryId = paramsQueryId ? parseInt(paramsQueryId, 10) : null;
- const {
- currentTeamName: teamNameForQuery,
- teamIdForApi: apiTeamIdForQuery,
- } = useTeamIdParam({
- location,
- router,
- includeAllTeams: true,
- includeNoTeam: false,
- });
const handlePageError = useErrorHandler();
- const {
- isGlobalAdmin,
- isGlobalMaintainer,
- isAnyTeamMaintainerOrTeamAdmin,
- isObserverPlus,
- isAnyTeamObserverPlus,
- config,
- } = useContext(AppContext);
+ const { config } = useContext(AppContext);
const {
selectedQueryTargets,
setSelectedQueryTargets,
@@ -89,7 +71,6 @@ const RunQueryPage = ({
selectedQueryTargetsByType.teams
);
const [targetsTotalCount, setTargetsTotalCount] = useState(0);
- const [isLiveQueryRunnable, setIsLiveQueryRunnable] = useState(true);
const disabledLiveQuery = config?.server_settings.live_query_disabled;
@@ -147,16 +128,6 @@ const RunQueryPage = ({
}
);
- const detectIsFleetQueryRunnable = () => {
- statusAPI.live_query().catch(() => {
- setIsLiveQueryRunnable(false);
- });
- };
-
- useEffect(() => {
- detectIsFleetQueryRunnable();
- }, [queryId]);
-
useEffect(() => {
setSelectedQueryTargetsByType({
hosts: targetedHosts,
diff --git a/frontend/pages/queries/live/screens/RunQuery.tsx b/frontend/pages/queries/live/screens/RunQuery.tsx
index e51433435e..a2c5ace15f 100644
--- a/frontend/pages/queries/live/screens/RunQuery.tsx
+++ b/frontend/pages/queries/live/screens/RunQuery.tsx
@@ -18,6 +18,9 @@ import { ITarget } from "interfaces/target";
import QueryResults from "../../edit/components/QueryResults";
+const RESPONSE_COUNT_ZERO = { results: 0, errors: 0 } as const;
+const CAMPAIGN_LIMIT = 250000;
+
interface IRunQueryProps {
storedQuery: IQuery | undefined;
selectedTargets: ITarget[];
@@ -39,6 +42,7 @@ const RunQuery = ({
const { renderFlash } = useContext(NotificationContext);
const [isQueryFinished, setIsQueryFinished] = useState(false);
+ const [isQueryClipped, setIsQueryClipped] = useState(false);
const [campaignState, setCampaignState] = useState(
DEFAULT_CAMPAIGN_STATE
);
@@ -47,12 +51,14 @@ const RunQuery = ({
const runQueryInterval = useRef(null);
const globalSocket = useRef(null);
const previousSocketData = useRef(null);
+ const responseCount = useRef({ ...RESPONSE_COUNT_ZERO });
const removeSocket = () => {
if (globalSocket.current) {
globalSocket.current.close();
globalSocket.current = null;
previousSocketData.current = null;
+ responseCount.current = RESPONSE_COUNT_ZERO;
}
};
@@ -128,6 +134,8 @@ const RunQuery = ({
...campaignHelpers.updateCampaignState(socketData)(prevCampaignState),
};
});
+ responseCount.current.results += socketData?.data?.rows?.length ?? 0;
+ responseCount.current.errors += socketData?.data?.error ? 1 : 0;
if (
socketData.type === "status" &&
@@ -135,6 +143,13 @@ const RunQuery = ({
) {
return teardownDistributedQuery();
}
+ if (
+ responseCount.current.results + responseCount.current.errors >=
+ CAMPAIGN_LIMIT
+ ) {
+ teardownDistributedQuery();
+ setIsQueryClipped(true);
+ }
};
};
@@ -204,6 +219,7 @@ const RunQuery = ({
onRunQuery={onRunQuery}
onStopQuery={onStopQuery}
isQueryFinished={isQueryFinished}
+ isQueryClipped={isQueryClipped}
setSelectedTargets={setSelectedTargets}
goToQueryEditor={goToQueryEditor}
queryName={storedQuery?.name}