Skip to content

Commit 0d860a1

Browse files
committed
Created scaled object page
1 parent b4aeca0 commit 0d860a1

12 files changed

+2328
-184
lines changed

client/src/App.tsx

+15-106
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,14 @@
11
import React, { Component } from 'react';
2-
import { CssBaseline, Grid, Paper, CircularProgress } from '@material-ui/core';
3-
import { ScaledObjectModel } from './models/ScaledObjectModel';
4-
import ScaledObjectCard from './components/ScaledObjectCard';
5-
import { V1Deployment } from '@kubernetes/client-node';
2+
import { CssBaseline } from '@material-ui/core';
63
import { Style } from 'jss';
7-
import SideBarNav from './components/SideBarNav';
8-
import ScaleControllerView from './components/ScaleControllerView';
9-
import { ScaleControllerLog } from './models/ScaleControllerLog';
4+
import ScaleControllerDashboard from './components/ScaleControllerDashboard';
5+
import ScaledObjectsDashboard from './components/ScaledObjectsDashboard';
6+
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
107

11-
class App extends Component<{}, { loaded: boolean, route: string, scaledObjects: ScaledObjectModel[], logs: ScaleControllerLog[], keda: V1Deployment, metadata: ""}> {
8+
class App extends Component<{}, {}> {
129
constructor(props: {}) {
1310
super(props);
14-
15-
this.state = {
16-
loaded: false,
17-
route: "/",
18-
scaledObjects: [],
19-
keda: new V1Deployment(),
20-
logs: [],
21-
metadata: "",
22-
};
23-
}
24-
25-
formatLogs(text: string) {
26-
let logs = text.split("\n");
27-
let scaleControllerLogs: ScaleControllerLog[] = [];
28-
29-
logs.forEach(function(log) {
30-
let logRegex = new RegExp("time.*level.*msg");
31-
let logSplit = new RegExp("(time|level|msg)=");
32-
let removeDoubleQuotes = new RegExp("^\"|\"$");
33-
34-
if (logRegex.test(log)) {
35-
let logComponents = log.split(logSplit);
36-
let scaleControllerLog = new ScaleControllerLog(logComponents[6].replace(removeDoubleQuotes, "").trim(), logComponents[4].trim(),
37-
logComponents[2].replace(removeDoubleQuotes, "").trim(), logComponents[4].trim());
38-
scaleControllerLogs.push(scaleControllerLog);
39-
}
40-
});
41-
42-
return scaleControllerLogs;
43-
}
44-
45-
async fetchRequests() {
46-
await fetch('/api/scaledobjects')
47-
.then(res => res.json());
48-
49-
await fetch('/api/keda')
50-
.then(res => { return res.json(); })
51-
.then(data => {
52-
let keda = new V1Deployment();
53-
keda.metadata = data.metadata;
54-
keda.spec = data.spec;
55-
keda.status = data.status;
56-
this.setState({ keda: keda });
57-
});
58-
59-
await fetch('/api/logs')
60-
.then(res => res.text().then(text =>
61-
{ this.setState( {logs: this.formatLogs(text) }) }));
62-
63-
this.setState( { loaded:true });
64-
}
65-
66-
async componentDidMount() {
67-
await this.fetchRequests();
68-
//this.setState({route: window.location.pathname});
69-
//setInterval(this.fetchRequests, 30000)
11+
this.state = { };
7012
}
7113

7214
static style: Style = {
@@ -79,54 +21,21 @@ class App extends Component<{}, { loaded: boolean, route: string, scaledObjects:
7921
}
8022
};
8123

82-
getScaleControllerOverview() {
83-
return (
84-
<ScaleControllerView deployment={this.state.keda} logs={this.state.logs}></ScaleControllerView>
85-
);
86-
}
87-
88-
getScaledObjectContent() {
89-
return (
90-
<div>
91-
<h1> Scaled Objects: </h1>
92-
<Grid container spacing={3}>
93-
<Grid item xs={12} md={12} lg={12}>
94-
<Paper>
95-
{this.state.scaledObjects
96-
.map(o => <ScaledObjectCard scaledObject={o} />)}
97-
</Paper>
98-
</Grid>
99-
</Grid>
100-
</div>
101-
);
102-
}
10324

104-
getNavLinks() {
105-
return [
106-
{text: "Overview", link: "/"},
107-
{text: "Scaled Objects", link: "/scaledobjects"}
108-
];
109-
}
110-
11125
render() {
112-
let content = null;
113-
let navLinks = this.getNavLinks();
114-
115-
if (this.state.route === "/" && this.state.loaded) {
116-
content = this.getScaleControllerOverview();
117-
} else if (this.state.route.toLowerCase() === "scaledobjects" && this.state.loaded) {
118-
content = this.getScaledObjectContent();
119-
} else {
120-
content = <div style={{display: 'flex', justifyContent:'center', alignItems:'center', height: '80vh'}}><CircularProgress/></div>
121-
}
122-
12326
return (
124-
12527
<React.Fragment>
12628
<CssBaseline />
127-
<SideBarNav content={content} navigationLinks={navLinks}></SideBarNav>
29+
<Router>
30+
<Switch>
31+
<Route exact path="/" component={ScaleControllerDashboard}></Route>
32+
</Switch>
33+
<Switch>
34+
<Route exact path="/scaled-objects" component={ScaledObjectsDashboard}></Route>
35+
</Switch>
36+
</Router>
12837
</React.Fragment>
129-
);
38+
)
13039
}
13140
}
13241

client/src/components/LoadingView.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import { CircularProgress } from '@material-ui/core';
3+
4+
export default class LoadingView extends React.Component<{}> {
5+
render() {
6+
return <div style={{display: 'flex', justifyContent:'center', alignItems:'center', height: '80vh'}}><CircularProgress/></div>;
7+
}
8+
}
+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import React from 'react';
2+
import { Typography } from '@material-ui/core';
3+
import { ResponsiveBar } from '@nivo/bar';
4+
import { ScaledObjectModel } from '../models/ScaledObjectModel';
5+
import { V1Deployment } from '@kubernetes/client-node';
6+
7+
export default class ReplicaDisplay extends React.Component<{}, {currentReplicas: {[key: string]: number}}> {
8+
replicaAutoscalingDataset: {[key: string]: number}[] = [];
9+
10+
constructor(props: {}) {
11+
super(props);
12+
13+
this.state = {
14+
currentReplicas: {},
15+
}
16+
}
17+
18+
async componentDidMount() {
19+
await this.getCurrentReplicaCount();
20+
await this.formatData();
21+
22+
try {
23+
setInterval(async() => {
24+
await this.getCurrentReplicaCount();
25+
await this.formatData();
26+
}, 5000);
27+
} catch(e) {
28+
console.log(e);
29+
}
30+
}
31+
32+
async getCurrentReplicaCount() {
33+
await fetch('/api/scaledobjects')
34+
.then(res => res.json())
35+
.then(({ items }) => {
36+
let scaledObjects = items;
37+
let currentReplicas:{[key: string]: number} = {};
38+
39+
scaledObjects.map(async (scaledObject:ScaledObjectModel) => {
40+
await fetch(`/api/deployment/${scaledObject.metadata.name}`)
41+
.then(res => { return res.json() })
42+
.then(data => {
43+
let deploy = new V1Deployment();
44+
deploy.metadata = data.metadata;
45+
deploy.spec = data.spec;
46+
deploy.status = data.status;
47+
48+
if (deploy.metadata && deploy.metadata!.name) {
49+
currentReplicas[deploy.metadata!.name] = (deploy.spec!.replicas === undefined) ? 0:deploy.spec!.replicas;
50+
}
51+
52+
this.setState({currentReplicas: currentReplicas });
53+
})
54+
});
55+
});
56+
}
57+
58+
formatData() {
59+
let dataAtTimestamp:{[key:string]: any} = {};
60+
let date = new Date();
61+
dataAtTimestamp["timestamp"] = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}T${date.getHours()}-${date.getMinutes()}-${date.getMilliseconds()}`;
62+
63+
for (let deploy in this.state.currentReplicas) {
64+
dataAtTimestamp[deploy] = this.state.currentReplicas[deploy];
65+
}
66+
67+
this.replicaAutoscalingDataset.push(dataAtTimestamp);
68+
69+
console.log(this.replicaAutoscalingDataset);
70+
}
71+
72+
render() {
73+
const data = [
74+
{
75+
"timestamp": "AD",
76+
"hot dog": 155,
77+
"hot dogColor": "hsl(282, 70%, 50%)",
78+
"burger": 67,
79+
"burgerColor": "hsl(20, 70%, 50%)",
80+
"sandwich": 150,
81+
"sandwichColor": "hsl(10, 70%, 50%)",
82+
"kebab": 31,
83+
"kebabColor": "hsl(66, 70%, 50%)",
84+
"fries": 103,
85+
"friesColor": "hsl(147, 70%, 50%)",
86+
"donut": 99,
87+
"donutColor": "hsl(16, 70%, 50%)"
88+
},
89+
{
90+
"timestamp": "AE",
91+
"hot dog": 20,
92+
"hot dogColor": "hsl(209, 70%, 50%)",
93+
"burger": 104,
94+
"burgerColor": "hsl(193, 70%, 50%)",
95+
"sandwich": 138,
96+
"sandwichColor": "hsl(111, 70%, 50%)",
97+
"kebab": 30,
98+
"kebabColor": "hsl(95, 70%, 50%)",
99+
"fries": 14,
100+
"friesColor": "hsl(252, 70%, 50%)",
101+
"donut": 173,
102+
"donutColor": "hsl(354, 70%, 50%)"
103+
},
104+
{
105+
"timestamp": "AF",
106+
"hot dog": 12,
107+
"hot dogColor": "hsl(62, 70%, 50%)",
108+
"burger": 119,
109+
"burgerColor": "hsl(80, 70%, 50%)",
110+
"sandwich": 121,
111+
"sandwichColor": "hsl(353, 70%, 50%)",
112+
"kebab": 187,
113+
"kebabColor": "hsl(308, 70%, 50%)",
114+
"fries": 142,
115+
"friesColor": "hsl(353, 70%, 50%)",
116+
"donut": 96,
117+
"donutColor": "hsl(88, 70%, 50%)"
118+
},
119+
{
120+
"timestamp": "AG",
121+
"hot dog": 153,
122+
"hot dogColor": "hsl(153, 70%, 50%)",
123+
"burger": 196,
124+
"burgerColor": "hsl(125, 70%, 50%)",
125+
"sandwich": 49,
126+
"sandwichColor": "hsl(260, 70%, 50%)",
127+
"kebab": 135,
128+
"kebabColor": "hsl(81, 70%, 50%)",
129+
"fries": 22,
130+
"friesColor": "hsl(56, 70%, 50%)",
131+
"donut": 23,
132+
"donutColor": "hsl(6, 70%, 50%)"
133+
},
134+
{
135+
"timestamp": "AI",
136+
"hot dog": 39,
137+
"hot dogColor": "hsl(350, 70%, 50%)",
138+
"burger": 16,
139+
"burgerColor": "hsl(198, 70%, 50%)",
140+
"sandwich": 134,
141+
"sandwichColor": "hsl(129, 70%, 50%)",
142+
"kebab": 30,
143+
"kebabColor": "hsl(179, 70%, 50%)",
144+
"fries": 158,
145+
"friesColor": "hsl(334, 70%, 50%)",
146+
"donut": 73,
147+
"donutColor": "hsl(128, 70%, 50%)"
148+
},
149+
{
150+
"timestamp": "AL",
151+
"hot dog": 179,
152+
"hot dogColor": "hsl(226, 70%, 50%)",
153+
"burger": 39,
154+
"burgerColor": "hsl(137, 70%, 50%)",
155+
"sandwich": 63,
156+
"sandwichColor": "hsl(309, 70%, 50%)",
157+
"kebab": 118,
158+
"kebabColor": "hsl(96, 70%, 50%)",
159+
"fries": 100,
160+
"friesColor": "hsl(270, 70%, 50%)",
161+
"donut": 61,
162+
"donutColor": "hsl(179, 70%, 50%)"
163+
},
164+
{
165+
"timestamp": "AM",
166+
"hot dog": 136,
167+
"hot dogColor": "hsl(318, 70%, 50%)",
168+
"burger": 8,
169+
"burgerColor": "hsl(238, 70%, 50%)",
170+
"sandwich": 159,
171+
"sandwichColor": "hsl(157, 70%, 50%)",
172+
"kebab": 133,
173+
"kebabColor": "hsl(326, 70%, 50%)",
174+
"fries": 82,
175+
"friesColor": "hsl(357, 70%, 50%)",
176+
"donut": 88,
177+
"donutColor": "hsl(231, 70%, 50%)"
178+
}
179+
]
180+
181+
return (
182+
<div>
183+
<Typography variant="h6" id="Replica Count">Replicas</Typography>
184+
<div style={{ height: 400 }}>
185+
<ResponsiveBar
186+
data={this.replicaAutoscalingDataset}
187+
keys={Object.keys(this.state.currentReplicas)}
188+
indexBy="timestamp"
189+
margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
190+
padding={0.3}
191+
colors={{ scheme: 'nivo' }}
192+
axisTop={null}
193+
axisRight={null}
194+
axisBottom={{
195+
tickSize: 5,
196+
tickPadding: 5,
197+
tickRotation: 0,
198+
legend: 'timestamp',
199+
legendPosition: 'middle',
200+
legendOffset: 32
201+
}}
202+
axisLeft={{
203+
tickSize: 5,
204+
tickPadding: 5,
205+
tickRotation: 0,
206+
legend: 'replicas',
207+
legendPosition: 'middle',
208+
legendOffset: -40
209+
}}
210+
labelSkipWidth={12}
211+
labelSkipHeight={12}
212+
labelTextColor={{ from: 'color', modifiers: [ [ 'darker', 1.6 ] ] }}
213+
legends={[
214+
{
215+
dataFrom: 'keys',
216+
anchor: 'bottom-right',
217+
direction: 'column',
218+
justify: false,
219+
translateX: 120,
220+
translateY: 0,
221+
itemsSpacing: 2,
222+
itemWidth: 100,
223+
itemHeight: 20,
224+
itemDirection: 'left-to-right',
225+
itemOpacity: 0.85,
226+
symbolSize: 20,
227+
effects: [
228+
{
229+
on: 'hover',
230+
style: {
231+
itemOpacity: 1
232+
}
233+
}
234+
]
235+
}
236+
]}
237+
animate={false}
238+
motionStiffness={90}
239+
motionDamping={15}
240+
/>
241+
</div>
242+
</div>
243+
);
244+
}
245+
}

0 commit comments

Comments
 (0)