Skip to content

Commit

Permalink
feat: provide granular progress of starlark package run (#1548)
Browse files Browse the repository at this point in the history
## Description:
* Refactor code to move out of Log.js into the CreateEnclaveLog.js (its
rightful home)
* Make json files autoformatted always
* Granular progress update with execution status text in in the starlark
package run log view

## Is this change user facing?
YES

## References (if applicable):
  • Loading branch information
adschwartz committed Oct 16, 2023
1 parent c463ae2 commit 8b20031
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 66 deletions.
1 change: 1 addition & 0 deletions engine/frontend/src/component/FileArtifactInfo.js
Expand Up @@ -187,6 +187,7 @@ const FileArtifactInfo = ({enclaves}) => {
languages={[detailInfo.extension]}
defaultState={detailInfo.textPreview}
lineNumbers={false}
autoFormat={true}
/>
</Box>
:
Expand Down
111 changes: 93 additions & 18 deletions engine/frontend/src/component/log/CreateEnclaveLog.js
Expand Up @@ -5,8 +5,9 @@ import {useNavigate} from "react-router-dom";
import {runStarlark} from "../../api/enclave";
import {getEnclaveInformation} from "../../api/container";
import LoadingOverlay from "../LoadingOverflow";
import {Box, Text, Flex, Spacer, Center} from "@chakra-ui/react";
import {useEffect, useState} from "react";
import {Box, Text, Flex, Spacer, Center, Tooltip, Spinner, CircularProgress} from "@chakra-ui/react";
import React, {useEffect, useState} from "react";
import {CheckIcon, WarningIcon} from "@chakra-ui/icons";

const SERVICE_IS_ADDED = "added with service";
export const ERROR = "error"
Expand All @@ -16,13 +17,66 @@ const INSTRUCTION_RESULT = "instructionResult"
export const RUN_FINISHED_EVENT = "runFinishedEvent"
export const PROCESSING_EVENT = PROGRESS_INFO

const EXECUTION_IN_PROGRESS = "Execution in progress"
const STARTING_EXECUTION = "Starting execution"


const SCRIPT_RUN_STATUS_PROCESSING_INDETERMINATE = "processing_indeterminate"
const SCRIPT_RUN_STATUS_PROCESSING = "processing"
const SCRIPT_RUN_STATUS_ERROR = "error"
const SCRIPT_RUN_STATUS_SUCCESS = "success"

const logsStatus = (event, progress) => {
if (event === SCRIPT_RUN_STATUS_SUCCESS) {
return (
<>
<Tooltip label='Success! Script finished successfully'>
<CheckIcon boxSize={6} color='green'/>
</Tooltip>
</>
)
} else if (event === SCRIPT_RUN_STATUS_PROCESSING) {
return (
<>
<Tooltip label='Script is running...'>
<CircularProgress size="30px" value={progress} color="green">
</CircularProgress>
</Tooltip>
</>
)
} else if (event === SCRIPT_RUN_STATUS_PROCESSING_INDETERMINATE) {
return (
<>
<Tooltip label='Script is running...'>
<CircularProgress size="30px" isIndeterminate color="green"></CircularProgress>
</Tooltip>
</>
)
} else if (event === SCRIPT_RUN_STATUS_ERROR) {
return (
<>
<Tooltip label='Error! Script finished with an error'>
<WarningIcon boxSize={5} color='red'/>
</Tooltip>
</>
)
} else {
return (
<>
</>
)
}
}


export const CreateEnclaveLog = ({packageId, enclave, args, appData}) => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false)
const [logs, setLogs] = useState([])
const [logsCurrentExecutionStatus, setLogsCurrentExecutionStatus] = useState("")
const [logsErrorExecutionStatus, setLogsErrorExecutionStatus] = useState("")
const [logsCurrentExecutionStatus, setLogsCurrentExecutionStatus] = useState(<></>)
const [logsExecutionStatusText, setLogsExecutionStatusText] = useState("")
const [services, setServices] = useState([])
const [logsComponent, setLogsComponent] = useState(<></>)

const getServices = async (enclave) => {
const {services: newServices} = await getEnclaveInformation(enclave.host, enclave.port, appData.jwtToken, appData.apiHost);
Expand All @@ -32,12 +86,27 @@ export const CreateEnclaveLog = ({packageId, enclave, args, appData}) => {
}

const readStreamData = (result) => {
// console.log(`${result.case} ${JSON.stringify(result.value)}`)
if (result.case === INSTRUCTION) {
setLogs(logs => [...logs, result.value.executableInstruction])
}

if (result.case === PROGRESS_INFO && (result.value.currentStepInfo[0] === EXECUTION_IN_PROGRESS || result.value.currentStepInfo[0] === STARTING_EXECUTION)) {
const totalSteps = result.value.totalSteps
const completedSteps = Math.max(result.value.currentStepNumber - 1, 0)
const text = `Completed ${completedSteps} of ${result.value.totalSteps}`
const progress = (completedSteps / totalSteps) * 100
if (isNaN(progress) || progress < 0.0 || progress > 100.0) {
setLogsCurrentExecutionStatus(logsStatus(SCRIPT_RUN_STATUS_PROCESSING_INDETERMINATE, null))
} else {
setLogsCurrentExecutionStatus(logsStatus(SCRIPT_RUN_STATUS_PROCESSING, progress))
}
setLogsExecutionStatusText(text)
} else if (result.case === PROGRESS_INFO && result.value.currentStepInfo.length > 0) {
setLogsCurrentExecutionStatus(logsStatus(SCRIPT_RUN_STATUS_PROCESSING_INDETERMINATE))
}

if (result.case === PROGRESS_INFO && result.value.currentStepInfo.length > 0) {
setLogsCurrentExecutionStatus(PROCESSING_EVENT)
if (result.value.currentStepInfo[result.value.currentStepNumber] !== undefined) {
setLogs(logs => [...logs, result.value.currentStepInfo[result.value.currentStepNumber]])
}
Expand All @@ -53,12 +122,17 @@ export const CreateEnclaveLog = ({packageId, enclave, args, appData}) => {
if (result.case === ERROR) {
const errorMessage = result.value.error.value.errorMessage;
setLogs(logs => [...logs, errorMessage])
setLogsCurrentExecutionStatus(ERROR)
setLogsErrorExecutionStatus(ERROR)
}

if (result.case === RUN_FINISHED_EVENT) {
setLogsCurrentExecutionStatus(RUN_FINISHED_EVENT)
console.log(result.value)
if (result.value.isRunSuccessful) {
setLogsExecutionStatusText("Script completed")
setLogsCurrentExecutionStatus(logsStatus(SCRIPT_RUN_STATUS_SUCCESS, null))
} else {
setLogsExecutionStatusText("Script failed")
setLogsCurrentExecutionStatus(logsStatus(SCRIPT_RUN_STATUS_ERROR, null))
}
}
}

Expand Down Expand Up @@ -96,16 +170,17 @@ export const CreateEnclaveLog = ({packageId, enclave, args, appData}) => {
})
}

const renderLogView = () => {
return (
(logs.length > 0) ? <Log
logs={logs}
fileName={enclave.name}
currentExecutionStatus={logsCurrentExecutionStatus}
errorStatus={logsErrorExecutionStatus}
/> : <Center color="white"> No Logs Available</Center>
useEffect(() => {
setLogsComponent(
(logs.length > 0) ?
<Log
logs={logs}
fileName={enclave.name}
currentExecutionStatus={logsCurrentExecutionStatus}
executionStatusText={logsExecutionStatusText}
/> : <Center color="white"> No Logs Available</Center>
)
}
}, [logs, logsCurrentExecutionStatus, logsExecutionStatusText])

return (
<div className="flex h-full">
Expand All @@ -126,7 +201,7 @@ export const CreateEnclaveLog = ({packageId, enclave, args, appData}) => {
</Box>
<Spacer/>
</Flex>
{(loading && logs.length === 0) ? <LoadingOverlay/> : renderLogView()}
{(loading && logs.length === 0) ? <LoadingOverlay/> : logsComponent}
</div>
</div>
<RightPanel home={false} isServiceInfo={!loading} enclaveName={enclave.name}/>
Expand Down
52 changes: 12 additions & 40 deletions engine/frontend/src/component/log/Log.js
@@ -1,13 +1,12 @@
import React, {useEffect, useRef, useState} from "react";
import useWindowDimensions from "../../utils/windowDimension";
import {Box, Flex, Spacer, Spinner, Tooltip, useClipboard} from "@chakra-ui/react";
import {Box, Flex, Spacer, Text, Tooltip, useClipboard} from "@chakra-ui/react";
import parse from 'html-react-parser';
import hasAnsi from 'has-ansi';
import stripAnsi from 'strip-ansi';
import {saveTextAsFile} from "../../utils/download";
import {Virtuoso} from "react-virtuoso";
import {CheckIcon, DownloadIcon, TriangleDownIcon, WarningIcon} from '@chakra-ui/icons'
import {PROCESSING_EVENT, RUN_FINISHED_EVENT} from "./CreateEnclaveLog";
import {DownloadIcon, TriangleDownIcon} from '@chakra-ui/icons'

var os = require('os-browserify/browser')
const Convert = require('ansi-to-html');
Expand All @@ -27,41 +26,7 @@ const Row = ({log}) => {
return <></>;
};

const logsStatus = (event, errorStatus) => {
if (event === RUN_FINISHED_EVENT && (errorStatus === "")) {
return (
<>
<Tooltip label='Success! Script finished successfully'>
<CheckIcon color='green'/>
</Tooltip>
</>
)
} else if (event === PROCESSING_EVENT) {
return (
<>
<Tooltip label='Script is running...'>
<Spinner/>
</Tooltip>
</>
)
} else if (event === RUN_FINISHED_EVENT && errorStatus) {
return (
<>
<Tooltip label='Error! Script finished with an error'>
<WarningIcon color='red'/>
</Tooltip>
</>
)
} else {
return (
<>
</>
)
}
}


export const Log = ({logs, fileName, currentExecutionStatus, errorStatus}) => {
export const Log = ({logs, fileName, currentExecutionStatus, executionStatusText}) => {
const [displayLogs, setDisplayLogs] = useState(logs)
const virtuosoRef = useRef(null)
const {height: windowHeight} = useWindowDimensions();
Expand All @@ -74,7 +39,7 @@ export const Log = ({logs, fileName, currentExecutionStatus, errorStatus}) => {
return stripAnsi(log)
})
setCopyValue(logsWithoutAnsi.join(os.EOL))
setLogsExecutionStatus(logsStatus(currentExecutionStatus, errorStatus))
setLogsExecutionStatus(currentExecutionStatus)
}, [logs]);

const handleDownload = () => {
Expand Down Expand Up @@ -105,10 +70,17 @@ export const Log = ({logs, fileName, currentExecutionStatus, errorStatus}) => {
/>
<Flex className="bg-black" style={{height: `80px`}}>
<Spacer/>
<Box
p='2'
m="4"
height={"40px"}
>
<Text fontSize={"sm"}>{executionStatusText}</Text>
</Box>
<Box p='2' m="4"
height={"40px"}
>
<Box>{logsExecutionStatus}</Box>
{logsExecutionStatus}
</Box>
<Tooltip label={`${hasCopied ? "Copied!" : "Copy to clipboard"}`}
placement='top-end'
Expand Down
6 changes: 3 additions & 3 deletions engine/server/webapp/asset-manifest.json
@@ -1,13 +1,13 @@
{
"files": {
"main.css": "./static/css/main.d56b8d0f.css",
"main.js": "./static/js/main.fd7e75a2.js",
"main.js": "./static/js/main.cb7ca964.js",
"index.html": "./index.html",
"main.d56b8d0f.css.map": "./static/css/main.d56b8d0f.css.map",
"main.fd7e75a2.js.map": "./static/js/main.fd7e75a2.js.map"
"main.cb7ca964.js.map": "./static/js/main.cb7ca964.js.map"
},
"entrypoints": [
"static/css/main.d56b8d0f.css",
"static/js/main.fd7e75a2.js"
"static/js/main.cb7ca964.js"
]
}
2 changes: 1 addition & 1 deletion engine/server/webapp/index.html
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Kurtosis Enclave Manager</title><script defer="defer" src="./static/js/main.fd7e75a2.js"></script><link href="./static/css/main.d56b8d0f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Kurtosis Enclave Manager</title><script defer="defer" src="./static/js/main.cb7ca964.js"></script><link href="./static/css/main.d56b8d0f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions engine/server/webapp/static/js/main.cb7ca964.js.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion engine/server/webapp/static/js/main.fd7e75a2.js.map

This file was deleted.

0 comments on commit 8b20031

Please sign in to comment.