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

feat: add service details to EM UI #1352

Merged
merged 25 commits into from Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d6fc033
Add container object to Service.
laurentluce Sep 11, 2023
4c5bbba
Add container attribute to ServiceInfo.
laurentluce Sep 11, 2023
cd65e78
Add container to service info and service context.
laurentluce Sep 13, 2023
b35bbb5
Merge branch 'main' into laurent/service_inspect
laurentluce Sep 13, 2023
59366c3
Add CLI service inspect command.
laurentluce Sep 13, 2023
6e67aac
Use container inspect to populate the user docker resources.
laurentluce Sep 15, 2023
e9564cb
Support for K8S.
laurentluce Sep 16, 2023
bccc605
Update NewService calls.
laurentluce Sep 16, 2023
3b98f5f
Update show full uuids to show full uuid.
laurentluce Sep 17, 2023
ae8558c
Add service inspect CLI doc.
laurentluce Sep 17, 2023
211da9b
Update UTs.
laurentluce Sep 17, 2023
7d00ad0
Merge branch 'main' into laurent/service_inspect
laurentluce Sep 17, 2023
4d010cf
Merge branch 'main' into laurent/service_inspect
laurentluce Sep 18, 2023
12164a4
Regenerate bindings.
laurentluce Sep 18, 2023
2bd016a
Merge branch 'main' into laurent/service_inspect
laurentluce Sep 18, 2023
0832db0
Linting.
laurentluce Sep 18, 2023
e194b7b
Linting
laurentluce Sep 18, 2023
43eaec1
Merge branch 'main' into laurent/service_inspect
laurentluce Sep 18, 2023
4988748
Merge branch 'laurent/service_inspect' into anders/service-inspect-ui
adschwartz Sep 18, 2023
0c5a1fa
Merge branch 'main' into anders/service-inspect-ui
adschwartz Sep 18, 2023
18ae72b
add service inspect view
adschwartz Sep 19, 2023
261fbb0
update the build
adschwartz Sep 19, 2023
ded0493
Merge branch 'main' into anders/service-inspect-ui
adschwartz Sep 19, 2023
0a7d5b0
use invariant service status
adschwartz Sep 20, 2023
5541a8e
Merge branch 'main' into anders/service-inspect-ui
adschwartz Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions engine/frontend/src/api/container.js
Expand Up @@ -102,9 +102,7 @@ export const getEnclaveInformation = async (host, port, token, apiHost) => {
)

return {
name: data.serviceInfo[serviceName].name,
uuid: data.serviceInfo[serviceName].serviceUuid,
privateIpAddr: data.serviceInfo[serviceName].privateIpAddr,
...data.serviceInfo[serviceName],
ports: ports,
}
})
Expand Down
67 changes: 43 additions & 24 deletions engine/frontend/src/component/CodeEditor.js
Expand Up @@ -12,6 +12,12 @@ export const CodeEditor = (
defaultState = languages.includes("json") ? "{\n}" : "",
autoFormat = false,
lineNumbers = false,
id = 0,
showCopyButton = true,
showDownloadButton = true,
showFormatButton = true,
buttonSizes = "sm",
border = "1px"
) => {
// https://github.com/microsoft/monaco-editor/blob/main/webpack-plugin/README.md#options
const [value, setValue] = useState(defaultState)
Expand All @@ -29,7 +35,7 @@ export const CodeEditor = (
// TODO: Add a promise to getEditor()
const getEditor = () => {
if (!monacoRef.current) return null;
return monacoRef.current.editor.getEditors()[0];
return monacoRef.current.editor.getEditors()[id];
}

const isEditorReadOnly = () => {
Expand All @@ -51,7 +57,7 @@ export const CodeEditor = (

useEffect(() => {
if (getEditor()) {
console.log("Changing readOnly in monaco to:", readOnlySetting)
// console.log("Changing readOnly in monaco to:", readOnlySetting)
getEditor().updateOptions({
readOnly: readOnlySetting,
})
Expand All @@ -61,15 +67,15 @@ export const CodeEditor = (
useEffect(() => {
if (formatCode) {
if (isEditorReadOnly()) {
console.log("Cannot format with readonly=true, requesting to set readOnly=false")
// console.log("Cannot format with readonly=true, requesting to set readOnly=false")
setReadOnlySetting(false)
} else {
if (getEditor()) {
getEditor()
.getAction('editor.action.formatDocument')
.run()
.then(() => {
console.log(`Formatting finished running. Setting readonly=${originalReadOnlySetting.current}`)
// console.log(`Formatting finished running. Setting readonly=${originalReadOnlySetting.current}`)
setReadOnlySetting(originalReadOnlySetting.current)
setFormatCode(false)
});
Expand Down Expand Up @@ -165,7 +171,7 @@ export const CodeEditor = (

return (
<Box
border="1px"
border={border}
borderColor='gray.700'
borderRadius="7"
margin={"1px"}
Expand All @@ -180,7 +186,7 @@ export const CodeEditor = (
onChange={handleEditorChange}
// onValidate={handleEditorValidation}
options={{
automaticLayout: true,
automaticLayout: false, // if this is `true` a ResizeObserver is installed. This causes issues with us managing the container size outside.
selectOnLineNumbers: lineNumbers,
lineNumbers: lineNumbers,
languages: languages,
Expand All @@ -191,25 +197,38 @@ export const CodeEditor = (
scrollBeyondLastLine: false
}}
/>
<Button
margin={1}
onClick={contentClipboard.onCopy}
>
{contentClipboard.hasCopied ? "Copied!" : "Copy"}
</Button>
<Button
margin={1}
onClick={handleDownload}
>
Download
</Button>
<Button
margin={1}
onClick={handleCodeFormat}
isDisabled={isNotFormattable()}
<Box
marginTop={1}
>
Format
</Button>
{showCopyButton && (
<Button
margin={1}
onClick={contentClipboard.onCopy}
size={buttonSizes}
>
{contentClipboard.hasCopied ? "Copied!" : "Copy"}
</Button>
)}
{showDownloadButton && (
<Button
margin={1}
onClick={handleDownload}
size={buttonSizes}
>
Download
</Button>
)}
{showFormatButton && (
<Button
margin={1}
onClick={handleCodeFormat}
isDisabled={isNotFormattable()}
size={buttonSizes}
>
Format
</Button>
)}
</Box>
</Box>
)
}
2 changes: 0 additions & 2 deletions engine/frontend/src/component/Enclaves.js
Expand Up @@ -141,8 +141,6 @@ const Enclaves = ({enclaves, isLoading, handleDeleteClick}) => {
const [enclaveName, setEnclaveName] = useState("")
const { isOpen, onOpen, onClose } = useDisclosure()

console.log(enclaveName)

const handleCreateEnvClick = () => {
navigate("/catalog")
}
Expand Down
6 changes: 3 additions & 3 deletions engine/frontend/src/component/LogView.js
@@ -1,13 +1,13 @@
import Heading from "./Heading"

export const LogView = ({heading, logs, size="h-[70%]" }) => {
export const LogView = ({heading, logs, size="h[100%]" }) => {
return (
<div className={`flex-col flex ${size}`}>
<Heading content={heading} />
<div className="overflow-y-auto h-full">
<ul className="border border-gray-200 p-2">
{logs.map((log, index) => (
<li key={index} className="p-2 border-b border-gray-200 text-black">
<li key={index} className="p-2 border-b border-gray-200 text-white">
{log}
</li>
))}
Expand Down Expand Up @@ -35,4 +35,4 @@ export const LogView = ({heading, logs, size="h-[70%]" }) => {
)
}
</div>
</div> */}
</div> */}
4 changes: 3 additions & 1 deletion engine/frontend/src/component/PackageCatalogForm.js
Expand Up @@ -92,11 +92,12 @@ const KeyValueTable = (dataCallBack) => {
</HStack>
</Box>
)}
renderAdd={addItem => <Button margin={1} onClick={addItem}>Add item</Button>}
renderAdd={addItem => <Button size={"sm"} margin={1} onClick={addItem}>Add item</Button>}
// renderEmpty={() => <p></p>}
/>
<Button
margin={1}
size={"sm"}
onClick={clipboard.onCopy}
>
<Tooltip label="Copy as JSON">
Expand All @@ -106,6 +107,7 @@ const KeyValueTable = (dataCallBack) => {
</Button>
<Button
margin={1}
size={"sm"}
onClick={paste}
>
<Tooltip label='Paste as a JSON key value map, e.g. `{ "key_1": "value", "key_2": 1 }` '>
Expand Down
121 changes: 93 additions & 28 deletions engine/frontend/src/component/ServiceInfo.js
@@ -1,16 +1,19 @@
import Heading from "./Heading";
import {useEffect, useState} from "react";
import {useLocation, useNavigate, useParams} from "react-router-dom";
import {LogView} from "./LogView";
import LeftPanel from "./LeftPanel";
import RightPanel from "./RightPanel";
import {getServiceLogs} from "../api/enclave";
import {useAppContext} from "../context/AppState";
import {Badge, Box, GridItem, Table, TableContainer, Tbody, Td, Tr} from "@chakra-ui/react";
import {CodeEditor} from "./CodeEditor";
import {LogView} from "./LogView";

const renderServices = (services, handleClick) => {
return services.map(service => {
return (
<div className={`flex items-center justify-center h-14 text-base bg-[#24BA27]`} key={service.name} onClick={() => handleClick(service)}>
<div className={`flex items-center justify-center h-14 text-base bg-[#24BA27]`} key={service.name}
onClick={() => handleClick(service)}>
<div className='cursor-default text-lg text-white'> {service.name} </div>
</div>
)
Expand All @@ -31,7 +34,8 @@ const ServiceInfo = () => {
let stream;
const ctrl = new AbortController();
const fetchLogs = async () => {
stream = await getServiceLogs(ctrl, enclaveName, serviceUuid, appData.apiHost); try {
stream = await getServiceLogs(ctrl, enclaveName, serviceUuid, appData.apiHost);
try {
for await (const res of stream) {
const log = res["serviceLogsByServiceUuid"][serviceUuid]["line"][0]
setLogs(logs => [...logs, log])
Expand All @@ -52,6 +56,75 @@ const ServiceInfo = () => {
navigate(fullPath, {state: {services, selected: service}})
}

const func = () => {
}

const codeBox = (id, parameterName, data) => {
const serializedData = JSON.stringify(data, null, 2)
return (
<Box>
{
CodeEditor(
func,
true,
`${parameterName}.json`,
["json"],
250,
serializedData,
true,
false,
id,
true,
true,
false,
"xs",
"1px"
)
}
</Box>
);
}

const tableRow = (heading, data) => {
let displayData = ""
try {
displayData = data()
} catch (e) {
console.error("Error while processing row", e)
displayData = "Error while retrieving information"
}
return (
<Tr key={heading}>
<Td><p><b>{heading}</b></p></Td>
<Td>{displayData}</Td>
</Tr>
);
};

const selectedSerialized = selected; // JSON.parse(JSON.stringify(selected))
const statusBadge = (status) => {
console.log(`status=${status}`)
let color = ""
let text = ""
if(status === "RUNNING" || status === 1){
color="green"
text="RUNNING"
} else if (status === "STOPPED" || status === 0) {
color="red"
text="STOPPED"
} else if(status ==="UNKNOWN" || status === 2){
color = "yellow"
text="UNKNOWN"
} else {
return (
<Badge>N/A</Badge>
)
}
return (
<Badge colorScheme={color}>{text}</Badge>
)
}

return (
<div className="flex h-full">
<LeftPanel
Expand All @@ -61,32 +134,24 @@ const ServiceInfo = () => {
renderList={() => renderServices(services, handleServiceClick)}
/>
<div className="flex h-full w-[calc(100vw-39rem)] flex-col space-y-5">
<div className='flex flex-col h-full space-y-1 bg-white'>
<Heading content={`${enclaveName} ::${selected.name}`}/>
<div className="flex-1">
<div className="text-xl text-left h-fit mb-2 ml-5 text-black">
Ports
</div>
<div className="overflow-auto">
{
selected.ports.map(port => {
const urlWithApplicationString = `${port.applicationProtocol}://localhost:${port.publicPortNumber}`
const urlWithoutApplicationString = `localhost:${port.publicPortNumber}`
const url = port.applicationProtocol ? urlWithApplicationString : urlWithoutApplicationString
<div className='flex flex-col h-full space-y-1 bg-[#171923]'>
<Heading content={`${enclaveName} - ${selected.name}`}/>
<TableContainer>
<Table variant='simple' size='sm'>
<Tbody>
{tableRow("Name", () => selectedSerialized.name)}
{tableRow("UUID", () => <pre>{selectedSerialized.serviceUuid}</pre>)}
{tableRow("Status", () => statusBadge(selectedSerialized.serviceStatus))}
{tableRow("Image", () => selectedSerialized.container.imageName)}
{tableRow("Ports", () => codeBox(0, "ports", selectedSerialized.ports))}
{tableRow("ENTRYPOINT", () => codeBox(1, "entrypoint", selectedSerialized.container.entrypointArgs))}
{tableRow("CMD", () => codeBox(2, "cmd", selectedSerialized.container.cmdArgs))}
{tableRow("ENV", () => codeBox(3, "env", selectedSerialized.container.envVars))}
</Tbody>
</Table>
</TableContainer>

return (
<div className="h-fit flex flex-row space-x-10 ml-5 text-black">
<div> {port.portName}:</div>
<a href={url} rel="noreferrer" className="grow">
<u> {url} </u>
</a>
</div>
)
})
}
</div>
</div>
<LogView heading={`Service Logs`} logs={logs}/>
{/*<LogView heading={`Service Logs`} logs={logs}/>*/}
adschwartz marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
<RightPanel home={false} isServiceInfo={true} enclaveName={enclaveName}/>
Expand Down
12 changes: 6 additions & 6 deletions engine/server/webapp/asset-manifest.json
@@ -1,13 +1,13 @@
{
"files": {
"main.css": "./static/css/main.d554a53d.css",
"main.js": "./static/js/main.78b479f1.js",
"main.css": "./static/css/main.d32f0461.css",
"main.js": "./static/js/main.4c387d64.js",
"index.html": "./index.html",
"main.d554a53d.css.map": "./static/css/main.d554a53d.css.map",
"main.78b479f1.js.map": "./static/js/main.78b479f1.js.map"
"main.d32f0461.css.map": "./static/css/main.d32f0461.css.map",
"main.4c387d64.js.map": "./static/js/main.4c387d64.js.map"
},
"entrypoints": [
"static/css/main.d554a53d.css",
"static/js/main.78b479f1.js"
"static/css/main.d32f0461.css",
"static/js/main.4c387d64.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.78b479f1.js"></script><link href="./static/css/main.d554a53d.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.4c387d64.js"></script><link href="./static/css/main.d32f0461.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.