Skip to content

Commit d894bea

Browse files
authored
[UI] Use tabbed pane with Queries and Executors tabs (#309)
1 parent 2ae2d51 commit d894bea

File tree

7 files changed

+110
-54
lines changed

7 files changed

+110
-54
lines changed

ballista/rust/scheduler/src/api/handlers.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ use graphviz_rust::printer::PrinterContext;
2222
use warp::Rejection;
2323

2424
#[derive(Debug, serde::Serialize)]
25-
struct StateResponse {
26-
executors: Vec<ExecutorMetaResponse>,
25+
struct SchedulerStateResponse {
2726
started: u128,
2827
version: &'static str,
2928
}
3029

30+
#[derive(Debug, serde::Serialize)]
31+
struct ExecutorsResponse {
32+
executors: Vec<ExecutorMetaResponse>,
33+
}
34+
3135
#[derive(Debug, serde::Serialize)]
3236
pub struct ExecutorMetaResponse {
3337
pub id: String,
@@ -45,12 +49,21 @@ pub struct JobResponse {
4549
pub percent_complete: u8,
4650
}
4751

48-
/// Return current scheduler state, including list of executors and active, completed, and failed
49-
/// job ids.
50-
pub(crate) async fn get_state<T: AsLogicalPlan, U: AsExecutionPlan>(
52+
/// Return current scheduler state
53+
pub(crate) async fn get_scheduler_state<T: AsLogicalPlan, U: AsExecutionPlan>(
54+
data_server: SchedulerServer<T, U>,
55+
) -> Result<impl warp::Reply, Rejection> {
56+
let response = SchedulerStateResponse {
57+
started: data_server.start_time,
58+
version: BALLISTA_VERSION,
59+
};
60+
Ok(warp::reply::json(&response))
61+
}
62+
63+
/// Return list of executors
64+
pub(crate) async fn get_executors<T: AsLogicalPlan, U: AsExecutionPlan>(
5165
data_server: SchedulerServer<T, U>,
5266
) -> Result<impl warp::Reply, Rejection> {
53-
// TODO: Display last seen information in UI
5467
let state = data_server.state;
5568
let executors: Vec<ExecutorMetaResponse> = state
5669
.executor_manager
@@ -66,12 +79,7 @@ pub(crate) async fn get_state<T: AsLogicalPlan, U: AsExecutionPlan>(
6679
})
6780
.collect();
6881

69-
let response = StateResponse {
70-
executors,
71-
started: data_server.start_time,
72-
version: BALLISTA_VERSION,
73-
};
74-
Ok(warp::reply::json(&response))
82+
Ok(warp::reply::json(&executors))
7583
}
7684

7785
/// Return list of jobs

ballista/rust/scheduler/src/api/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,13 @@ fn with_data_server<T: AsLogicalPlan + Clone, U: 'static + AsExecutionPlan>(
8585
pub fn get_routes<T: AsLogicalPlan + Clone, U: 'static + AsExecutionPlan>(
8686
scheduler_server: SchedulerServer<T, U>,
8787
) -> BoxedFilter<(impl Reply,)> {
88-
let route_state = warp::path!("api" / "state")
88+
let route_scheduler_state = warp::path!("api" / "state")
8989
.and(with_data_server(scheduler_server.clone()))
90-
.and_then(handlers::get_state);
90+
.and_then(handlers::get_scheduler_state);
91+
92+
let route_executors = warp::path!("api" / "executors")
93+
.and(with_data_server(scheduler_server.clone()))
94+
.and_then(handlers::get_executors);
9195

9296
let route_jobs = warp::path!("api" / "jobs")
9397
.and(with_data_server(scheduler_server.clone()))
@@ -104,7 +108,8 @@ pub fn get_routes<T: AsLogicalPlan + Clone, U: 'static + AsExecutionPlan>(
104108
.and(with_data_server(scheduler_server))
105109
.and_then(|job_id, data_server| handlers::get_job_svg_graph(data_server, job_id));
106110

107-
let routes = route_state
111+
let routes = route_scheduler_state
112+
.or(route_executors)
108113
.or(route_jobs)
109114
.or(route_job_summary)
110115
.or(route_job_dot)

ballista/ui/scheduler/src/App.tsx

+38-5
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,27 @@
1616
// under the License.
1717

1818
import React, { useState, useEffect } from "react";
19-
import { Box, Grid, VStack } from "@chakra-ui/react";
19+
import {
20+
Box,
21+
Grid,
22+
Tab,
23+
TabList,
24+
TabPanel,
25+
TabPanels,
26+
Tabs,
27+
VStack,
28+
} from "@chakra-ui/react";
2029
import { Header } from "./components/Header";
2130
import { Summary } from "./components/Summary";
31+
import { ExecutorsList } from "./components/ExecutorsList";
2232
import { QueriesList } from "./components/QueriesList";
2333
import { Footer } from "./components/Footer";
24-
2534
import "./App.css";
2635

2736
const App: React.FunctionComponent<any> = () => {
2837
const [schedulerState, setSchedulerState] = useState(undefined);
2938
const [jobs, setJobs] = useState(undefined);
39+
const [executors, setExecutors] = useState(undefined);
3040

3141
function getSchedulerState() {
3242
return fetch(`/api/state`, {
@@ -50,20 +60,43 @@ const App: React.FunctionComponent<any> = () => {
5060
.then((res) => setJobs(res));
5161
}
5262

63+
function getExecutors() {
64+
return fetch(`/api/executors`, {
65+
method: "POST",
66+
headers: {
67+
Accept: "application/json",
68+
},
69+
})
70+
.then((res) => res.json())
71+
.then((res) => setExecutors(res));
72+
}
73+
5374
useEffect(() => {
5475
getSchedulerState();
5576
getJobs();
77+
getExecutors();
5678
}, []);
5779

58-
console.log(JSON.stringify(schedulerState));
59-
6080
return (
6181
<Box>
6282
<Grid minH="100vh">
6383
<VStack alignItems={"flex-start"} spacing={0} width={"100%"}>
6484
<Header schedulerState={schedulerState} />
6585
<Summary schedulerState={schedulerState} />
66-
<QueriesList queries={jobs} />
86+
<Tabs width={"100%"}>
87+
<TabList>
88+
<Tab>Jobs</Tab>
89+
<Tab>Executors</Tab>
90+
</TabList>
91+
<TabPanels>
92+
<TabPanel>
93+
<QueriesList queries={jobs} />
94+
</TabPanel>
95+
<TabPanel>
96+
<ExecutorsList executors={executors} />
97+
</TabPanel>
98+
</TabPanels>
99+
</Tabs>
67100
<Footer />
68101
</VStack>
69102
</Grid>

ballista/ui/scheduler/src/components/NodesList.tsx ballista/ui/scheduler/src/components/ExecutorsList.tsx

+12-11
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,27 @@ import React from "react";
1919
import { Box } from "@chakra-ui/react";
2020
import { Column, ElapsedCell, DataTable } from "./DataTable";
2121

22-
export enum NodeStatus {
22+
export enum ExecutorStatus {
2323
RUNNING = "RUNNING",
2424
TERMINATED = "TERMINATED",
2525
}
2626

27-
export interface NodeInfo {
27+
export interface ExecutorsListProps {
28+
executors?: ExecutorMeta[];
29+
}
30+
31+
export interface ExecutorMeta {
2832
id: string;
2933
host: string;
3034
port: number;
31-
status: NodeStatus;
35+
last_seen: number;
36+
status: ExecutorStatus;
3237
started: string;
3338
}
3439

3540
const columns: Column<any>[] = [
3641
{
37-
Header: "Node",
42+
Header: "ID",
3843
accessor: "id",
3944
},
4045
{
@@ -56,16 +61,12 @@ const columns: Column<any>[] = [
5661
},
5762
];
5863

59-
interface NodesListProps {
60-
nodes: NodeInfo[];
61-
}
62-
63-
export const NodesList: React.FunctionComponent<NodesListProps> = ({
64-
nodes = [],
64+
export const ExecutorsList: React.FunctionComponent<ExecutorsListProps> = ({
65+
executors = [],
6566
}) => {
6667
return (
6768
<Box flex={1}>
68-
<DataTable maxW={960} columns={columns} data={nodes} pageSize={4} />
69+
<DataTable columns={columns} data={executors} pageSize={4} />
6970
</Box>
7071
);
7172
};

ballista/ui/scheduler/src/components/QueriesList.tsx

+14-16
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,13 @@ const columns: Column<any>[] = [
163163
},
164164
];
165165

166-
const getSkeletion = () => (
166+
const getSkeleton = () => (
167167
<>
168168
<Skeleton height={5} />
169169
<Skeleton height={5} />
170170
<Skeleton height={5} />
171171
<Skeleton height={5} />
172+
<Skeleton height={5} />
172173
</>
173174
);
174175

@@ -178,20 +179,17 @@ export const QueriesList: React.FunctionComponent<QueriesListProps> = ({
178179
const isLoaded = typeof queries !== "undefined";
179180

180181
return (
181-
<VStack flex={1} p={4} w={"100%"} alignItems={"flex-start"}>
182-
<Text mb={4}>Queries</Text>
183-
<Stack w={"100%"} flex={1}>
184-
{isLoaded ? (
185-
<DataTable
186-
columns={columns}
187-
data={queries || []}
188-
pageSize={10}
189-
pb={10}
190-
/>
191-
) : (
192-
getSkeletion()
193-
)}
194-
</Stack>
195-
</VStack>
182+
<Box w={"100%"} flex={1}>
183+
{isLoaded ? (
184+
<DataTable
185+
columns={columns}
186+
data={queries || []}
187+
pageSize={10}
188+
pb={10}
189+
/>
190+
) : (
191+
getSkeleton()
192+
)}
193+
</Box>
196194
);
197195
};

ballista/ui/scheduler/src/components/Summary.tsx

-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import React from "react";
1919
import { Box, Text, Flex, VStack } from "@chakra-ui/react";
2020
import { HiCheckCircle } from "react-icons/hi";
2121
import TimeAgo from "react-timeago";
22-
import { NodesList, NodeInfo } from "./NodesList";
2322

2423
const Label: React.FunctionComponent<React.PropsWithChildren<any>> = ({
2524
children,
@@ -35,7 +34,6 @@ export interface SchedulerState {
3534
status: string;
3635
started: string;
3736
version: string;
38-
executors: NodeInfo[];
3937
}
4038

4139
export interface SummaryProps {
@@ -75,10 +73,6 @@ export const Summary: React.FunctionComponent<SummaryProps> = ({
7573
<Text pl={1}>Active</Text>
7674
</Flex>
7775
</Flex>
78-
<Flex>
79-
<Label>Nodes</Label>
80-
<Text>{schedulerState.executors?.length}</Text>
81-
</Flex>
8276
<Flex>
8377
<Label>Started</Label>
8478
<Text>
@@ -90,7 +84,6 @@ export const Summary: React.FunctionComponent<SummaryProps> = ({
9084
<Text>{schedulerState.version}</Text>
9185
</Flex>
9286
</VStack>
93-
<NodesList nodes={schedulerState.executors} />
9487
</Flex>
9588
</Box>
9689
</Flex>

ballista/ui/scheduler/yarn.lock

+18
Original file line numberDiff line numberDiff line change
@@ -5442,6 +5442,11 @@ execa@^4.0.0:
54425442
signal-exit "^3.0.2"
54435443
strip-final-newline "^2.0.0"
54445444

5445+
exenv@^1.2.2:
5446+
version "1.2.2"
5447+
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
5448+
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
5449+
54455450
exit@^0.1.2:
54465451
version "0.1.2"
54475452
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -9770,11 +9775,24 @@ [email protected]:
97709775
use-callback-ref "^1.2.1"
97719776
use-sidecar "^1.0.1"
97729777

9778+
react-from-dom@^0.6.2:
9779+
version "0.6.2"
9780+
resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.6.2.tgz#9da903a508c91c013b55afcd59348b8b0a39bdb4"
9781+
integrity sha512-qvWWTL/4xw4k/Dywd41RBpLQUSq97csuv15qrxN+izNeLYlD9wn5W8LspbfYe5CWbaSdkZ72BsaYBPQf2x4VbQ==
9782+
97739783
react-icons@^4.2.0:
97749784
version "4.2.0"
97759785
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.2.0.tgz#6dda80c8a8f338ff96a1851424d63083282630d0"
97769786
integrity sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==
97779787

9788+
react-inlinesvg@^3.0.1:
9789+
version "3.0.1"
9790+
resolved "https://registry.yarnpkg.com/react-inlinesvg/-/react-inlinesvg-3.0.1.tgz#2133f5d2c770ac405060db2ce1c13eed30e7e83b"
9791+
integrity sha512-cBfoyfseNI2PkDA7ZKIlDoHq0eMfpoC3DhKBQNC+/X1M4ZQB+aXW+YiNPUDDDKXUsGDUIZWWiZWNFeauDIVdoA==
9792+
dependencies:
9793+
exenv "^1.2.2"
9794+
react-from-dom "^0.6.2"
9795+
97789796
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
97799797
version "16.13.1"
97809798
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"

0 commit comments

Comments
 (0)