From 058fd56d3e488bb070e30c8f408416d4ceb1d927 Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Tue, 5 Sep 2023 15:50:50 +0100 Subject: [PATCH] Updates to the Script details modal. (#13623) relates to #13308 This updates the implementation for the scripts details modal. This includes changing how we are showing messages to the user when there are errors and also changing how we are displaying the messages for integer exit codes. - [x] Manual QA for all new/changed functionality --- .../ScriptDetailsModal/ScriptDetailsModal.tsx | 158 ++++++++++-------- frontend/services/entities/scripts.ts | 36 ++++ frontend/utilities/endpoints.ts | 3 + 3 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 frontend/services/entities/scripts.ts diff --git a/frontend/pages/DashboardPage/cards/ActivityFeed/components/ScriptDetailsModal/ScriptDetailsModal.tsx b/frontend/pages/DashboardPage/cards/ActivityFeed/components/ScriptDetailsModal/ScriptDetailsModal.tsx index 621754485b..87cae173dc 100644 --- a/frontend/pages/DashboardPage/cards/ActivityFeed/components/ScriptDetailsModal/ScriptDetailsModal.tsx +++ b/frontend/pages/DashboardPage/cards/ActivityFeed/components/ScriptDetailsModal/ScriptDetailsModal.tsx @@ -1,6 +1,8 @@ import React from "react"; import { useQuery } from "react-query"; +import scriptsAPI, { IScriptResult } from "services/entities/scripts"; + import Modal from "components/Modal"; import Button from "components/buttons/Button"; import TooltipWrapper from "components/TooltipWrapper"; @@ -11,9 +13,6 @@ import Spinner from "components/Spinner/Spinner"; const baseClass = "script-details-modal"; -const SCRIPT_RUNNING_CODE = -999; -const HOST_NOT_REACHED_CODE = -998; - interface IScriptContentProps { content: string; } @@ -29,60 +28,84 @@ const ScriptContent = ({ content }: IScriptContentProps) => { ); }; +const StatusMessageRunning = () => ( +
+

+ + Script is running. To see if the script finished, close this modal and + open it again. +

+
+); + +const StatusMessageSuccess = () => ( +
+

+ + Exit code: 0 (Script ran successfully.) +

+
+); + +const StatusMessageFailed = ({ exitCode }: { exitCode: number }) => ( +
+

+ + Exit code: {exitCode} (Script failed.) +

{" "} +
+); + +const StatusMessageError = ({ message }: { message: string }) => ( +
+

+ + Error: {message} +

+
+); + interface IStatusMessageProps { - exitCode: number; + hostTimeout: boolean; + exitCode: number | null; message: string; - runtime: number; } -const StatusMessage = ({ exitCode, message, runtime }: IStatusMessageProps) => { - let statusMessage: JSX.Element; - - // script timed out error - if (runtime > 30) { - statusMessage = ( -

- - Timeout error: Fleet stopped the script after 30 seconds to protect host - performance. -

- ); - // host could not be reached - } else if (exitCode === HOST_NOT_REACHED_CODE) { - statusMessage = ( -

- - The script ran but Fleet couldn't get its output because Fleet - didn't hear back from the host. -

- ); - // script still running - } else if (exitCode === SCRIPT_RUNNING_CODE) { - statusMessage = ( -

- - Script is running. To see if the script finished, close this modal and - open it again. -

- ); - } else { - // 0 or 1 exit code with message - statusMessage = ( -

- - {`Exit code: ${exitCode} (${message})`} -

- ); +const StatusMessage = ({ + hostTimeout, + exitCode, + message, +}: IStatusMessageProps) => { + switch (exitCode) { + case null: + return !hostTimeout ? ( + // Expected API message: "A script is already running on this host. Please wait about 1 minute to let it finish." + + ) : ( + // Expected API message: "Fleet hasn’t heard from the host in over 1 minute. Fleet doesn’t know if the script ran because the host went offline." + + ); + case -2: + // Expected API message: "Scripts are disabled for this host. To run scripts, deploy a Fleet installer with scripts enabled." + return ; + case -1: + // Expected API message: "Timeout. Fleet stopped the script after 30 seconds to protect host performance." + return ; + case 0: + // Expected API message: "" + return ; + default: + // Expected API message: "" + return ; } - - return
{statusMessage}
; }; interface IScriptOutputProps { output: string; + hostname: string; } -const ScriptOutput = ({ output }: IScriptOutputProps) => { +const ScriptOutput = ({ output, hostname }: IScriptOutputProps) => { return (

@@ -94,7 +117,7 @@ const ScriptOutput = ({ output }: IScriptOutputProps) => { > output recorded {" "} - when Marko's MacBook Pro ran the script above: + when {hostname} ran the script above:

@@ -102,24 +125,34 @@ const ScriptOutput = ({ output }: IScriptOutputProps) => { }; interface IScriptResultProps { - exitCode: number; + hostname: string; + hostTimeout: boolean; + exitCode: number | null; message: string; output: string; - runtime: number; } const ScriptResult = ({ + hostname, + hostTimeout, exitCode, message, output, - runtime, }: IScriptResultProps) => { - const showOutputText = exitCode !== -998 && exitCode !== -999 && runtime < 30; + const hostTimedOut = exitCode === null && hostTimeout === true; + const scriptsDisabledForHost = exitCode === -2; + const scriptStillRunning = exitCode === null && hostTimeout === false; + const showOutputText = + !hostTimedOut && !scriptsDisabledForHost && !scriptStillRunning; return (
- - {showOutputText && } + + {showOutputText && }
); }; @@ -129,38 +162,31 @@ interface IScriptDetailsModalProps { } const ScriptDetailsModal = ({ onCancel }: IScriptDetailsModalProps) => { - const TEST_DATA = { - script_contents: "test contentsss", - exit_code: 0, - output: "test output", - message: "test message", - runtime: 20, - }; - - const { data, isLoading, isError } = useQuery( + const { data, isLoading, isError } = useQuery( ["scriptDetailsModal"], () => { - return new Promise((resolve) => resolve(TEST_DATA)); + return scriptsAPI.getScriptResult(1); }, { refetchOnWindowFocus: false } ); const renderContent = () => { - let content: JSX.Element; + let content = <>; if (isLoading) { content = ; } else if (isError) { content = ; - } else { + } else if (data) { content = ( <> ); diff --git a/frontend/services/entities/scripts.ts b/frontend/services/entities/scripts.ts new file mode 100644 index 0000000000..88f13d3c96 --- /dev/null +++ b/frontend/services/entities/scripts.ts @@ -0,0 +1,36 @@ +import sendRequest from "services"; +import endpoints from "utilities/endpoints"; + +export interface IScriptResult { + host_name: string; + host_id: number; + execution_id: number; + script_contents: string; + exit_code: number | null; + output: string; + message: string; + runtime: number; + host_timeout: boolean; +} + +export default { + getScriptResult(id: number) { + const { SCRIPT_RESULT } = endpoints; + + // TODO: uncomment when API is ready. + // return sendRequest("GET", SCRIPT_RESULT(id)); + return new Promise((resolve) => { + resolve({ + host_name: "test host", + host_id: 1, + execution_id: 1, + script_contents: "test contentsss here is here", + exit_code: 1, + output: "test output", + message: "Error: This is an error message.", + runtime: 20, + host_timeout: false, + }); + }); + }, +}; diff --git a/frontend/utilities/endpoints.ts b/frontend/utilities/endpoints.ts index c0505d9fce..632a3f4a3b 100644 --- a/frontend/utilities/endpoints.ts +++ b/frontend/utilities/endpoints.ts @@ -122,4 +122,7 @@ export default { USERS: `/${API_VERSION}/fleet/users`, USERS_ADMIN: `/${API_VERSION}/fleet/users/admin`, VERSION: `/${API_VERSION}/fleet/version`, + + // SCRIPTS + SCRIPT_RESULT: (id: number) => `/${API_VERSION}/fleet/scripts/results/${id}`, };