├── .DS_Store
├── .dockerignore
├── .gitignore
├── Dockerfile
├── README.md
├── client
├── App.jsx
├── Launch_Material
│ ├── branding
│ │ ├── centered_neptune.png
│ │ ├── intro neptune.png
│ │ ├── intro_nept.png
│ │ └── introducing.png
│ ├── logo
│ │ ├── neptune.svg
│ │ ├── neptune2.png
│ │ └── neptune_logo.png
│ └── team_images
│ │ ├── JinYoo.png
│ │ ├── LawrenceYeh.png
│ │ ├── Miranda-Headshot2022 circle.png
│ │ ├── Miranda-Headshot2022 square.jpeg
│ │ ├── Miranda-Headshot2022.jpg
│ │ └── SwanHtet.png
├── components
│ ├── AreaChart.jsx
│ ├── BasicCard.jsx
│ ├── Depl.jsx
│ ├── DoughnutChart.jsx
│ ├── GaugeChartTemplate.jsx
│ ├── LineChart.jsx
│ ├── Namespace.jsx
│ ├── Node.jsx
│ ├── NotificationCard.jsx
│ ├── PieChart.jsx
│ ├── Pods.jsx
│ ├── PolarAreaChart.jsx
│ ├── SearchBar.jsx
│ ├── Service.jsx
│ └── VerticalLineChart.jsx
├── containers
│ ├── DetailsContainer.jsx
│ ├── HomeContainer.jsx
│ ├── MiniDrawer.jsx
│ ├── NotificationContainer.jsx
│ └── QueryContainer.jsx
├── index.js
└── styles
│ └── style.css
├── index.html
├── package-lock.json
├── package.json
├── server
├── controllers
│ ├── k8sClientController.js
│ ├── promController.js
│ └── queryController.js
├── routes
│ └── routes.js
└── server.js
└── webpack.config.js
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/.DS_Store
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | __tests__
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:latest
2 | WORKDIR /usr/src/app
3 | RUN npm install -g webpack nodemon
4 | COPY . /usr/src/app
5 | RUN npm install
6 | RUN npm run build
7 | EXPOSE 3000
8 | CMD ["npm", "start"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introducing Neptune
2 |
3 |
4 |
5 |
6 | Light-weight, simple, and straightforward learning tool for your Kubernetes cluster
7 |
8 | Neptune is an approachable learning tool, light-weight in nature with a few monitoring features. PromQL has a steep learning curve and not everyone needs to learn how to use these queries. We have pre-selected a few relevant queries that display basic data about a simple Kubernetes cluster. This is to reduce the amount of noise when engaging with Prometheus. Neptune is meant to be an easy, visual way to understand a K8s cluster.
9 | Learn more about Neptune here.
10 |
11 | ## Focus on what matters, with built in alerts and health monitoring.
12 |
13 | Render the metrics of your nodes, pods, and namespaces all in one easy to visualize UI.
14 |
15 | 
16 |
17 | ## Metrics that matter
18 |
19 | Rather than being overloaded with countless metrics, focus on the ones that matter. We highlighted prominent data points related to CPU, Memory, and Network Usage
20 |
21 | 
22 |
23 | ## Alerts, Event Logs, and Cluster Health
24 |
25 | Sort through your firing alerts by severity to stay on top of your alerts
26 | Quick and simplified overview of your cluster health and performance
27 |
28 | 
29 |
30 | # Getting Started
31 |
32 | Prerequisites
33 | There are some prerequisites before you start with Neptune, so make sure you have Docker and Minikube setup. We highly recommend that you follow the order below.
34 |
35 | Install Docker Desktop - the fastest way to containerize applications
36 | Install minikube - minikube quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows
37 | Install Helm - package manager for Kubernetes
38 | Deploy Prometheus - software application that scrapes clusters and returns metrics
39 |
40 | Note: Make sure your minikube cluster is actively running!
41 |
42 | 1. Clone this repo and then change directory into the root folder. Then run the command below:
43 | git clone git@github.com:oslabs-beta/Neptune.git
44 |
45 | 2. Install dependencies
46 | Run these commands from within the root directory:
47 |
48 | npm install
49 |
50 | npm run build
51 |
52 | npm run start
53 |
54 | 3. Port-forward Prometheus to 9090
55 | To port-forward Prometheus, run the following command. Remember that your minikube cluster needs to be actively running!
56 |
57 | kubectl port-forward prometheus-prometheus-kube-prometheus-prometheus-0 --namespace=default 9090:9090
58 |
59 | 4. Explore Neptune!
60 | Go to http://localhost:3000/ and start exploring your Kubernetes cluster!
61 |
62 | ## 🛠 Built With :
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | ## The Core Team
84 |
85 |
86 |
87 |
88 | Swan Htet
89 |
90 | GitHub
91 |
92 | LinkedIn
93 |
94 |
95 |
96 |
97 | Miranda Jaramillo
98 |
99 | GitHub
100 |
101 | LinkedIn
102 |
103 |
104 |
105 |
106 | Lawrence Yeh
107 |
108 | GitHub
109 |
110 | LinkedIn
111 |
112 |
113 |
114 |
115 | Jin Yoo
116 |
117 | GitHub
118 |
119 | LinkedIn
120 |
121 |
122 |
--------------------------------------------------------------------------------
/client/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
3 | import DetailsContainer from './containers/DetailsContainer';
4 | import MiniDrawer from './containers/MiniDrawer';
5 | import { Container, Grid } from '@mui/material';
6 | import NotificationContainer from './containers/NotificationContainer';
7 | import QueryContainer from './containers/QueryContainer';
8 | import HomeContainer from './containers/HomeContainer';
9 | import '../client/styles/style.css';
10 |
11 |
12 |
13 |
14 | function App() {
15 | return (
16 | <>
17 |
18 | {/*
19 |
20 |
21 |
22 |
23 |
24 | */}
30 |
31 |
32 |
33 |
34 | } />
35 | } />
36 | } />
37 |
38 |
39 |
40 | {/*
41 |
42 |
43 | */}
44 |
45 | >
46 | );
47 | }
48 |
49 | export default App;
50 |
--------------------------------------------------------------------------------
/client/Launch_Material/branding/centered_neptune.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/branding/centered_neptune.png
--------------------------------------------------------------------------------
/client/Launch_Material/branding/intro neptune.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/branding/intro neptune.png
--------------------------------------------------------------------------------
/client/Launch_Material/branding/intro_nept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/branding/intro_nept.png
--------------------------------------------------------------------------------
/client/Launch_Material/branding/introducing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/branding/introducing.png
--------------------------------------------------------------------------------
/client/Launch_Material/logo/neptune.svg:
--------------------------------------------------------------------------------
1 | neptu ne
--------------------------------------------------------------------------------
/client/Launch_Material/logo/neptune2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/logo/neptune2.png
--------------------------------------------------------------------------------
/client/Launch_Material/logo/neptune_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/logo/neptune_logo.png
--------------------------------------------------------------------------------
/client/Launch_Material/team_images/JinYoo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/team_images/JinYoo.png
--------------------------------------------------------------------------------
/client/Launch_Material/team_images/LawrenceYeh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/team_images/LawrenceYeh.png
--------------------------------------------------------------------------------
/client/Launch_Material/team_images/Miranda-Headshot2022 circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/team_images/Miranda-Headshot2022 circle.png
--------------------------------------------------------------------------------
/client/Launch_Material/team_images/Miranda-Headshot2022 square.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/team_images/Miranda-Headshot2022 square.jpeg
--------------------------------------------------------------------------------
/client/Launch_Material/team_images/Miranda-Headshot2022.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/team_images/Miranda-Headshot2022.jpg
--------------------------------------------------------------------------------
/client/Launch_Material/team_images/SwanHtet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Neptune/6c5a31e0d5a27f67315a5f8a8e10e969c2c809da/client/Launch_Material/team_images/SwanHtet.png
--------------------------------------------------------------------------------
/client/components/AreaChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Filler,
11 | Legend,
12 | } from 'chart.js';
13 | import { Line } from 'react-chartjs-2';
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Title,
21 | Tooltip,
22 | Filler,
23 | Legend
24 | );
25 |
26 | export const options = {
27 | responsive: true,
28 | plugins: {
29 | legend: {
30 | position: 'top',
31 | },
32 | title: {
33 | display: true,
34 | text: 'Network traffic',
35 | },
36 | },
37 | };
38 |
39 |
40 | const labels = ['0.00 ms', '500.00 ms', '1000.00 ms', '1500.00 ms', '2000.00 ms', '2500.00 ms', '3000.00 ms'];
41 | const dummyData = [20, 262, 175, 210, 324, 149, 120];
42 |
43 | export const data = {
44 | labels,
45 | datasets: [
46 | {
47 | fill: true,
48 | label: 'Dataset 2',
49 | data: dummyData,
50 | borderColor: 'rgb(53, 162, 235)',
51 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
52 | },
53 | ],
54 | };
55 |
56 | export default function AreaChart({networkData, networkType}) {
57 |
58 | // iterate thru the object, skip the undefined (first element)
59 |
60 | //Access the array of each of the key
61 | //each array will contain an array of y values
62 | // x - time (30 min) -- labels / and y - values are data
63 |
64 | // the will be in byte, divide by a million to make in mega bytes
65 |
66 |
67 | const data2 = {};
68 | data2.labels = [];
69 | let minCount = 0;
70 | console.log('Network data inside Area Chart component', networkData);
71 | for(let i = 0; i < networkData['undefined'].length; i++ ) //the count of x-values depend on the length of the array of each key
72 | {
73 | data2.labels.push(`${minCount = minCount + 30}min`);
74 | }
75 | // for each dataset we will have an obj
76 | data2.datasets = [];
77 |
78 |
79 | const clusterNetWork = {};
80 | clusterNetWork.fill = true;
81 | clusterNetWork.label = networkType;
82 | clusterNetWork.data = [];
83 | const outerArr = networkData["undefined"] //outer array contains array of mem usage
84 | outerArr.forEach( innerArr => {clusterNetWork.data.push(innerArr[0] % 1000)}); //take the first value of each inner array
85 | //random colors from Details Container
86 | const r = Math.floor(Math.random() * 255);
87 | const g = Math.floor(Math.random() * 255);
88 | const b = Math.floor(Math.random() * 255);
89 | clusterNetWork.borderColor = `rgba(${r},${g},${b})`;
90 | clusterNetWork.backgroundColor = `rgba(${r},${g},${b}, 0.5)`;
91 |
92 | data2.datasets.push(clusterNetWork);
93 |
94 |
95 |
96 |
97 | return ;
98 | }
99 |
--------------------------------------------------------------------------------
/client/components/BasicCard.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Box from '@mui/material/Box';
3 | import Card from '@mui/material/Card';
4 | import CardActions from '@mui/material/CardActions';
5 | import CardContent from '@mui/material/CardContent';
6 | import Button from '@mui/material/Button';
7 | import Typography from '@mui/material/Typography';
8 |
9 | export default function BasicCard({name, ip, os, color}) {
10 | return (
11 |
12 |
13 |
14 | Name: {name}
15 |
16 |
17 | IP: {ip}
18 |
19 |
20 | OS: {os}
21 |
22 |
23 |
24 | Learn More
25 |
26 |
27 | );
28 | }
--------------------------------------------------------------------------------
/client/components/Depl.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Card from '@mui/material/Card';
3 | import CardContent from '@mui/material/CardContent';
4 | import Typography from '@mui/material/Typography';
5 |
6 | export default function Depl(props) {
7 | return (
8 |
9 |
10 |
11 | Deployments
12 |
13 | {props.deplNumber}
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/client/components/DoughnutChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Doughnut } from 'react-chartjs-2';
3 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
4 | ChartJS.register(ArcElement, Tooltip, Legend);
5 |
6 | const data = {
7 | labels: ['Node 1', 'Node 2', 'Node 3', 'Node 4'],
8 | datasets: [{
9 | data: [20, 30, 35, 15],
10 | backgroundColor: [
11 | 'rgba(255, 99, 132, 0.2)',
12 | 'rgba(75, 192, 192, 0.2)',
13 | 'rgba(54, 162, 235, 0.2)',
14 | 'rgba(255, 206, 86, 0.2)',
15 | ],
16 | borderColor: [
17 | 'rgba(255, 99, 132, 1)',
18 | 'rgba(75, 192, 192, 1)',
19 | 'rgba(54, 162, 235, 1)',
20 | 'rgba(255, 206, 86, 1)',
21 |
22 | ],
23 | borderWidth: 1,
24 | }]
25 | }
26 |
27 | export default function DoughnutChart() {
28 | return (
29 |
30 |
CPU Usage
31 |
32 |
33 | )
34 | }
--------------------------------------------------------------------------------
/client/components/GaugeChartTemplate.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | ArcElement,
11 | } from 'chart.js';
12 | import { Doughnut } from 'react-chartjs-2';
13 | import { Paper, Typography } from '@mui/material';
14 | import ChartDataLabels from 'chartjs-plugin-datalabels';
15 |
16 |
17 | function GaugeChartTemplate({ chartData, title, label }) {
18 | ChartJS.register(
19 | CategoryScale,
20 | LinearScale,
21 | BarElement,
22 | Title,
23 | Tooltip,
24 | Legend,
25 | ArcElement,
26 | ChartDataLabels
27 | );
28 |
29 | const options = {
30 | plugins: {
31 | title: {
32 | display: false,
33 | responsive: true,
34 | animation: {
35 | animateScale: true,
36 | },
37 | },
38 | datalabels: {
39 | // This code is used to display data values
40 | anchor: 'center',
41 | align: 'center',
42 | font: {
43 | weight: 'bold',
44 | size: 20,
45 | },
46 | formatter: (value) => value + '%',
47 | color: 'white',
48 | },
49 | },
50 | layout: {
51 | padding: {
52 | top: '-50',
53 | },
54 | },
55 | pointRadius: 0,
56 | circumference: '180',
57 | rotation: '-90',
58 | };
59 |
60 | let utilColor;
61 |
62 | if (chartData) {
63 | if (100 - chartData <= 33.3) {
64 | utilColor = '#ea2315';
65 | } else if (100 - chartData > 33.3 && 100 - chartData < 66.6) {
66 | utilColor = 'yellow';
67 | } else {
68 | utilColor = 'rgba(39, 244, 142, 1)';
69 | }
70 | }
71 |
72 | const data = {
73 | datasets: [
74 | {
75 | data: [chartData, 100 - chartData],
76 | borderColor: ['transparent', 'transparent'],
77 | backgroundColor: [utilColor, '#808080'],
78 | },
79 | ],
80 | };
81 |
82 | const renderChartData = () => {
83 | if (chartData !== undefined) {
84 | return {chartData.toString() + '%'} ;
85 | }
86 | };
87 |
88 | return (
89 |
90 | <>
91 |
92 | {title}
93 |
94 |
95 | >
96 | );
97 | }
98 |
99 | export default GaugeChartTemplate;
--------------------------------------------------------------------------------
/client/components/LineChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Legend,
11 | } from 'chart.js';
12 | import { Line } from 'react-chartjs-2';
13 | // import our data from Prometheus eventually
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Title,
21 | Tooltip,
22 | Legend
23 | );
24 |
25 | export const options = {
26 | responsive: true,
27 | plugins: {
28 | legend: {
29 | position: 'top', // as const,
30 | },
31 | title: {
32 | display: true,
33 | text: 'Memory Usage of Each Namespace in MB',
34 | },
35 | },
36 | };
37 |
38 | // labels is x-values
39 |
40 | const labels = [
41 | '0.00 ms',
42 | '500.00 ms',
43 | '1000.00 ms',
44 | '1500.00 ms',
45 | '2000.00 ms',
46 | '2500.00 ms',
47 | '3000.00 ms',
48 | '3500.00 ms',
49 | '4000.00 ms',
50 | '4500.00 ms',
51 | '5000.00 ms',
52 | '5500.00 ms',
53 | '6000.00 ms',
54 | '6500.00 ms',
55 | '7000.00 ms',
56 | '7500.00 ms, 8000.00 ms',
57 | '8500.00 ms',
58 | '9000.00 ms',
59 | ];
60 |
61 | //dummyData are the y-values
62 | const dummyData = [
63 | 14, 45, 32, 7, 2, 22, 5, 56, 6, 12, 23, 200, 60, 260, 70, 267, 220, 225,
64 | ];
65 | const dummyData2 = [
66 | 5, 6, 8, 1, 20, 110, 55, 6, 8, 60, 20, 18, 32, 23, 75, 47, 89, 200,
67 | ];
68 | const dummyData3 = [
69 | 5, 6, 8, 1, 20, 18, 5, 6, 8, 1, 20, 18, 32, 23, 75, 47, 89, 200,
70 | ];
71 | const dummyData4 = [
72 | 1, 2, 3, 2, 12, 18, 7, 6, 8, 11, 20, 18, 180, 230, 98, 10, 89, 20,
73 | ];
74 |
75 | export const data = {
76 | //labels is x-values
77 | labels,
78 | // data: dummyData, are the y-values
79 | datasets: [
80 | {
81 | label: 'Network Utilization',
82 | data: dummyData,
83 | borderColor: 'rgb(255, 99, 132)',
84 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
85 | },
86 | {
87 | label: 'Network Utilization 2',
88 | data: dummyData2,
89 | borderColor: 'rgb(53, 162, 235)',
90 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
91 | },
92 | {
93 | label: 'Network Utilization 3',
94 | data: dummyData3,
95 | borderColor: 'rgb(53, 216, 235)',
96 | backgroundColor: 'rgba(53, 216, 235, 0.5)',
97 | },
98 | {
99 | label: 'Network Utilization 4',
100 | data: dummyData4,
101 | borderColor: 'rgb(153, 162, 235)',
102 | backgroundColor: 'rgba(153, 162, 235, 0.5)',
103 | },
104 | ],
105 | };
106 |
107 | export default function LineChart({lineData}) {
108 | console.log("lineData --> ", lineData);
109 |
110 |
111 | // iterate thru the object, skip the undefined (first element)
112 |
113 | //Access the array of each of the key
114 | //each array will contain an array of y values
115 | // x - time (30 min) -- labels / and y - values are data
116 |
117 | // the will be in byte, divide by a million to make in mega bytes
118 |
119 |
120 | const data2 = {};
121 | data2.labels = [];
122 | let minCount = 0;
123 | console.log("this console" ,lineData['default'])
124 | for(let i = 0; i < lineData['default'].length; i++ ) //the count of x-values depend on the length of the array of each key
125 | {
126 | data2.labels.push(`${minCount = minCount + 30}min`);
127 | }
128 | // for each dataset we will have an obj
129 | data2.datasets = [];
130 |
131 | for(let namespace in lineData)
132 | {
133 | if(namespace !== 'undefined')
134 | {
135 | const namespaceMemory = {};
136 | namespaceMemory.label = namespace;
137 | namespaceMemory.data = [];
138 | const outerArr = lineData[namespace] //outer array contains array of mem usage
139 | outerArr.forEach( innerArr => {namespaceMemory.data.push(innerArr[0] / 1000000)}); //take the first value of each inner array
140 | //random colors from Details Container
141 | const r = Math.floor(Math.random() * 255);
142 | const g = Math.floor(Math.random() * 255);
143 | const b = Math.floor(Math.random() * 255);
144 | namespaceMemory.borderColor = `rgba(${r},${g},${b})`;
145 | namespaceMemory.backgroundColor = `rgba(${r},${g},${b}, 0.5)`;
146 |
147 | data2.datasets.push(namespaceMemory);
148 | }
149 | }
150 |
151 | // console.log('lets get down', data2);
152 | //console.log('data 1', data);
153 |
154 | return ;
155 | }
156 |
--------------------------------------------------------------------------------
/client/components/Namespace.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Card from '@mui/material/Card';
3 | import CardContent from '@mui/material/CardContent';
4 | import Typography from '@mui/material/Typography';
5 |
6 | export default function Namespace(props) {
7 | return (
8 |
9 |
10 |
11 | Namespace
12 |
13 | {props.namespaceNumber}
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/client/components/Node.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Card from '@mui/material/Card';
3 | import CardContent from '@mui/material/CardContent';
4 | import Typography from '@mui/material/Typography';
5 |
6 | export default function Node(props) {
7 | return (
8 |
9 |
10 |
11 | Nodes
12 |
13 | {props.nodeNumber}
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/client/components/NotificationCard.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Box from '@mui/material/Box';
3 | import Card from '@mui/material/Card';
4 | import CardActions from '@mui/material/CardActions';
5 | import CardContent from '@mui/material/CardContent';
6 | import Button from '@mui/material/Button';
7 | import Typography from '@mui/material/Typography';
8 | import { red } from '@mui/material/colors';
9 |
10 | export default function CardForNoti({alertname, description, namespace, service, severity, state, activeAt, value, summary}) {
11 |
12 | const color = severity === 'critical' ? "red" : "orange";
13 |
14 | return (
15 |
16 |
17 |
18 | Alert Name: {alertname}
19 |
20 |
21 | Description: {description}
22 |
23 |
24 | Namespace: {namespace}
25 |
26 |
27 | Service: {service}
28 |
29 |
30 | Severity: {severity}
31 |
32 |
33 | State: {state}
34 |
35 |
36 | Active at: {activeAt}
37 |
38 |
39 | Value: {value}
40 |
41 |
42 | Summary: {summary}
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/client/components/PieChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
3 | import { Pie } from 'react-chartjs-2';
4 |
5 | ChartJS.register(ArcElement, Tooltip, Legend);
6 |
7 | export const data = {
8 | labels: ['Node 1', 'Node 2', 'Node 3', 'Node 4'],
9 | datasets: [
10 | {
11 | label: '# of Votes',
12 | data: [25, 40, 20, 15],
13 | backgroundColor: [
14 | 'rgba(255, 99, 132, 0.2)',
15 | 'rgba(75, 192, 192, 0.2)',
16 | 'rgba(54, 162, 235, 0.2)',
17 | 'rgba(255, 206, 86, 0.2)',
18 | ],
19 | borderColor: [
20 | 'rgba(255, 99, 132, 1)',
21 | 'rgba(75, 192, 192, 1)',
22 | 'rgba(54, 162, 235, 1)',
23 | 'rgba(255, 206, 86, 1)',
24 | ],
25 | borderWidth: 1,
26 | },
27 | ],
28 | };
29 |
30 | export default function PieChart() {
31 | return (
32 |
33 |
Memory Usage
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/client/components/Pods.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Card from '@mui/material/Card';
3 | import CardContent from '@mui/material/CardContent';
4 | import Typography from '@mui/material/Typography';
5 |
6 | export default function Pods(props) {
7 | return (
8 |
9 |
10 |
11 | Pods
12 |
13 | {props.podNumber}
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/client/components/PolarAreaChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | RadialLinearScale,
5 | ArcElement,
6 | Tooltip,
7 | Legend,
8 | } from 'chart.js';
9 | import { PolarArea } from 'react-chartjs-2';
10 |
11 | ChartJS.register(RadialLinearScale, ArcElement, Tooltip, Legend);
12 |
13 | export const data = {
14 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
15 | datasets: [
16 | {
17 | label: '# of Votes',
18 | data: [17, 23, 16, 8, 6, ],
19 | backgroundColor: [
20 | 'rgba(255, 99, 132, 0.5)',
21 | 'rgba(54, 162, 235, 0.5)',
22 | 'rgba(255, 206, 86, 0.5)',
23 | 'rgba(75, 192, 192, 0.5)',
24 | 'rgba(153, 102, 255, 0.5)',
25 | 'rgba(255, 159, 64, 0.5)',
26 | ],
27 | borderWidth: 1,
28 | },
29 | ],
30 | };
31 |
32 | export default function PolarAreaChart() {
33 | return ;
34 | }
35 |
--------------------------------------------------------------------------------
/client/components/SearchBar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { IconButton, Stack, TextField } from "@mui/material";
3 | import SearchIcon from '@mui/icons-material/Search';
4 |
5 | const Searcher = (props) => {
6 |
7 | const { setInputUser } = props;
8 |
9 | const [valueInput, setValueInput] = useState('');
10 |
11 | const onSearchValueChange = (event) =>{
12 | const inputValue = event.target.value;
13 | setValueInput(inputValue);
14 | }
15 |
16 | const handleSubmit = () => {
17 | setInputUser(valueInput);
18 | }
19 |
20 | return(
21 |
28 |
40 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default Searcher;
53 |
--------------------------------------------------------------------------------
/client/components/Service.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Card from '@mui/material/Card';
3 | import CardContent from '@mui/material/CardContent';
4 | import Typography from '@mui/material/Typography';
5 |
6 | export default function Service(props) {
7 | return (
8 |
9 |
10 |
11 | Services
12 |
13 | {props.serviceNumber}
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/client/components/VerticalLineChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 |
13 | ChartJS.register(
14 | CategoryScale,
15 | LinearScale,
16 | BarElement,
17 | Title,
18 | Tooltip,
19 | Legend
20 | );
21 |
22 | export const options = {
23 | responsive: true,
24 | plugins: {
25 | legend: {
26 | position: 'top',
27 | },
28 | title: {
29 | display: true,
30 | text: 'Pod Memory and Performance',
31 | },
32 | },
33 | };
34 |
35 | const labels = ['0.00 ms', '500.00 ms', '1000.00 ms', '1500.00 ms', '2000.00 ms', '2500.00 ms', '3000.00 ms'];
36 | const dummyData = [0, 300, 40, 500, 624, 982];
37 | const dummyData2 = [0, 10, 100, 120,500, 999];
38 |
39 | export const data = {
40 | labels,
41 | datasets: [
42 | {
43 | label: 'Pod Capacity 1',
44 | data: dummyData,
45 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
46 | },
47 |
48 | {
49 | label: 'Pod Capacity 2',
50 | data: dummyData2,
51 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
52 | },
53 | ],
54 | };
55 |
56 | export default function VerticalLineChart({allPodsData}) {
57 |
58 | const data2 = {};
59 | data2.labels = [];
60 | let minCount = 0;
61 | for(let i = 0; i < allPodsData['undefined'].length; i++ ) //the count of x-values depend on the length of the array of each key
62 | {
63 | data2.labels.push(`${minCount = minCount + 30}min`);
64 | }
65 | // for each dataset we will have an obj
66 | data2.datasets = [];
67 |
68 | for(let podName in allPodsData)
69 | {
70 | if(podName !== 'undefined')
71 | {
72 | const podNameMemory = {};
73 | podNameMemory.label = podName;
74 | podNameMemory.data = [];
75 | const outerArr = allPodsData[podName] //outer array contains array of mem usage
76 | outerArr.forEach( innerArr => {podNameMemory.data.push(innerArr[0] % 10000)}); //take the first value of each inner array
77 | //random colors from Details Container
78 | const r = Math.floor(Math.random() * 255);
79 | const g = Math.floor(Math.random() * 255);
80 | const b = Math.floor(Math.random() * 255);
81 | podNameMemory.backgroundColor = `rgba(${r},${g},${b}, 0.5)`;
82 |
83 | data2.datasets.push(podNameMemory);
84 | }
85 | }
86 |
87 | console.log('lets get down', data2);
88 | console.log('data 1', data);
89 |
90 |
91 | return ;
92 | }
93 |
--------------------------------------------------------------------------------
/client/containers/DetailsContainer.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Box from '@mui/material/Box';
3 | import InputLabel from '@mui/material/InputLabel';
4 | import MenuItem from '@mui/material/MenuItem';
5 | import FormControl from '@mui/material/FormControl';
6 | import Select from '@mui/material/Select';
7 | import BasicCard from '../components/BasicCard';
8 | import List from '@mui/material/List';
9 | import ListItem from '@mui/material/ListItem';
10 | import DoughnutChart from '../components/DoughnutChart';
11 | import PieChart from '../components/PieChart';
12 | import Grid from '@mui/material/Grid';
13 | import { useEffect, useState } from 'react';
14 | import GaugeChartTemplate from '../components/GaugeChartTemplate'
15 |
16 | export default function DetailsContainer() {
17 | // For our drop down
18 | const [cpu, setCpu] = useState(0);
19 | const [mem, setMem] = useState(0);
20 | const [podData, setPodData] = useState({});
21 | const [podsInfo, setPodsInfo] = useState([]);
22 |
23 | const handleChange = (event) => {
24 | console.log(event.target.value);
25 | const data = event.target.value;
26 |
27 | const pods = [];
28 | data.forEach( (element, i) => {
29 |
30 | //for each pod of podsInfo
31 | const pod = {};
32 |
33 | pod.name = element['Pod Name'];
34 | pod.ip = element['Pod IP'];
35 | pod.os = 'Linux';
36 |
37 | //assign random color
38 | const r = Math.floor(Math.random() * 255);
39 | const g = Math.floor(Math.random() * 255);
40 | const b = Math.floor(Math.random() * 255);
41 | pod.color = `rgba(${r},${g},${b},1)`;
42 | pod.key = {i};
43 |
44 | pods.push(pod);
45 | })
46 |
47 |
48 | setPodsInfo(pods);
49 | };
50 |
51 |
52 | useEffect(() => {
53 |
54 | fetch('/api/k8s/podInfo')
55 | .then((response) => response.json())
56 | .then((data) => {
57 |
58 | data.forEach( (element, i) => {
59 |
60 | //for each pod of podsInfo
61 | const pod = {};
62 |
63 | pod.name = element.metadata.generateName;
64 | pod.ip = element.status.podIP;
65 | pod.os = 'Linux';
66 | //assign random color
67 | const r = 0;
68 | const g = 138;
69 | const b = 216;
70 | pod.color = `rgba(${r},${g},${b},1)`;
71 | pod.key = {i};
72 |
73 | setPodsInfo((prev) => ([...prev,pod]));
74 | })
75 | });
76 |
77 | //console.log(podsInfo);
78 | //---- FETCH REQ FOR THE 1ST SPEEDOMETER ---- CPU USAGE OF THE CLUSTER
79 | fetch('/api/k8s/promClusterCpuPct')
80 | .then((response) => response.json())
81 | .then((data) => {
82 | setCpu(data);
83 | });
84 |
85 | //---- FETCH REQ FOR THE 2ND SPEEDOMETER ---- CPU MEMORY OF THE CLUSTER
86 | fetch('/api/k8s/promClusterMemoryUtil')
87 | .then((response) => response.json())
88 | .then((data) => {
89 | setMem(data);
90 | });
91 |
92 | // get pod info based for each namespace
93 | fetch('/api/k8s/podData')
94 | .then((response) => response.json())
95 | .then((data) => {
96 | setPodData(data);
97 | });
98 |
99 | },[])
100 |
101 |
102 | return (
103 | <>
104 |
105 |
106 | Namespace
107 |
114 |
115 | {/* Namespace 1
116 | Namespace 2
117 | Namespace 3 */}
118 | {Object.keys(podData).map(namespace => (
119 | {namespace}
120 | ))}
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | {podsInfo.map((pod) => (
131 |
132 |
138 |
139 |
140 | ))}
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
153 |
154 |
155 |
161 |
162 |
163 |
164 |
165 | {/* */}
166 | >
167 | );
168 | }
169 |
--------------------------------------------------------------------------------
/client/containers/HomeContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import List from '@mui/material/List';
3 | import ListItem from '@mui/material/ListItem';
4 | import Node from '../components/Node';
5 | import Pods from '../components/Pods';
6 | import Depl from '../components/Depl';
7 | import Service from '../components/Service';
8 | import Namespace from '../components/Namespace';
9 | import LineChart from '../components/LineChart';
10 | import Grid from '@mui/material/Grid';
11 | import VerticalLineChart from '../components/VerticalLineChart';
12 | import PolarAreaChart from '../components/PolarAreaChart';
13 | import AreaChart from '../components/AreaChart';
14 |
15 | export default function HomeContainer() {
16 | //fetch request originally in Pods.jsx
17 | const [podNumber, setPodNumber] = useState([]);
18 | const [nodeNumber, setNodeNumber] = useState([]);
19 | const [deplNumber, setDeplNumber] = useState([]);
20 | const [serviceNumber, setServiceNumber] = useState([]);
21 | const [namespaceNumber, setNamespaceNumber] = useState([]);
22 | const [lineData, setLineData] = useState({});
23 | const [allPodsData, setAllPodsData] = useState({});
24 | const [networkReceived, setNetworkReceived] = useState({});
25 | const [networkTransmitted, setNetworkTransmitted] = useState({});
26 |
27 |
28 |
29 |
30 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
31 | /////////////////////////////////////// --- FETCH REQ FROM KUBERNETES DASHBOARD ------//////////////////////////////////////////////
32 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
33 |
34 | // useEffect counts the state
35 | useEffect(() => {
36 | //CHANGED THE ALLNAMESPACES TO THE BEGGINING
37 | //return pod count
38 |
39 | fetch('/api/k8s/podCount')
40 | .then((response) => response.json())
41 | .then((data) => {
42 | setPodNumber(data.length);
43 | console.log('POD COUNT: ', data.length);
44 | });
45 |
46 | // return node count
47 | fetch('/api/k8s/node')
48 | .then((response) => response.json())
49 | .then((data) => {
50 | setNodeNumber(data.length);
51 | console.log('NODE COUNT: ', data.length);
52 | });
53 |
54 | // return deployment count
55 | fetch('/api/k8s/deployment')
56 | .then((response) => response.json())
57 | .then((data) => {
58 | setDeplNumber(data.length);
59 | console.log('DEPL COUNT: ', data.length);
60 | });
61 |
62 | // return services count
63 | fetch('/api/k8s/services')
64 | .then((response) => response.json())
65 | .then((data) => {
66 | setServiceNumber(data.length);
67 | console.log('SERVICES COUNT: ', data.length);
68 | });
69 |
70 | // fetch req for namespaces
71 | fetch('/api/k8s/namespace')
72 | .then((response) => response.json())
73 | .then((data) => {
74 | setNamespaceNumber(data.length);
75 | console.log('NAMESPACEX COUNT', data.length);
76 | });
77 |
78 | //fetch req for line graph data (memory used for all namespaces)
79 | fetch('/api/k8s/memoryAllNamespaces')
80 | .then((response) => response.json())
81 | .then((data) => {
82 | setLineData(data);
83 | });
84 |
85 |
86 |
87 | //fetch req for vertical line graph data (memory used for all pods)
88 | fetch('/api/k8s/memoryAllPods')
89 | .then((response) => response.json())
90 | .then((data) => {
91 | setAllPodsData(data);
92 | });
93 |
94 | ///api/k8s/clusterNetRec
95 | //fetch req for area chart (Network Received)
96 | fetch('/api/k8s/clusterNetRec')
97 | .then((response) => response.json())
98 | .then((data) => {
99 | setNetworkReceived(data);
100 | console.log('network received inside homecontainer', networkReceived);
101 | });
102 |
103 |
104 | ///api/k8s/clusterNetTrans
105 | //fetch req for area chart (Network Transmitted)
106 | fetch('/api/k8s/clusterNetTrans')
107 | .then((response) => response.json())
108 | .then((data) => {
109 | setNetworkTransmitted(data);
110 | });
111 | },[]);
112 |
113 |
114 |
115 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
116 | /////////////////////////////////////// --- FETCH REQ FROM PROMETHEUS ------//////////////////////////////////////////////
117 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
118 |
119 | return (
120 | <>
121 |
122 |
125 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | {' '}
141 |
142 | { lineData.default && }
143 |
144 |
145 | {' '}
146 | { allPodsData.undefined && }
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | {' '}
156 | { networkTransmitted.undefined && }
157 |
158 |
159 | {' '}
160 | { networkReceived.undefined && }
161 |
162 |
163 |
164 |
165 |
166 | >
167 | );
168 | }
169 |
--------------------------------------------------------------------------------
/client/containers/MiniDrawer.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { styled, useTheme } from '@mui/material/styles';
3 | import Box from '@mui/material/Box';
4 | import MuiDrawer from '@mui/material/Drawer';
5 | import MuiAppBar from '@mui/material/AppBar';
6 | import Toolbar from '@mui/material/Toolbar';
7 | import List from '@mui/material/List';
8 | import CssBaseline from '@mui/material/CssBaseline';
9 | import Typography from '@mui/material/Typography';
10 | import Divider from '@mui/material/Divider';
11 | import IconButton from '@mui/material/IconButton';
12 | import MenuIcon from '@mui/icons-material/Menu';
13 | import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
14 | import ChevronRightIcon from '@mui/icons-material/ChevronRight';
15 | import ListItem from '@mui/material/ListItem';
16 | import ListItemButton from '@mui/material/ListItemButton';
17 | import ListItemIcon from '@mui/material/ListItemIcon';
18 | import ListItemText from '@mui/material/ListItemText';
19 | import InboxIcon from '@mui/icons-material/MoveToInbox';
20 | import MailIcon from '@mui/icons-material/Mail';
21 | import { Link } from 'react-router-dom';
22 | import neptune2 from "../Launch_Material/logo/neptune2.png"
23 | import { Container } from "@mui/material";
24 |
25 | const drawerWidth = 240;
26 |
27 | const openedMixin = (theme) => ({
28 | width: drawerWidth,
29 | transition: theme.transitions.create('width', {
30 | easing: theme.transitions.easing.sharp,
31 | duration: theme.transitions.duration.enteringScreen,
32 | }),
33 | overflowX: 'hidden',
34 | });
35 |
36 | const closedMixin = (theme) => ({
37 | transition: theme.transitions.create('width', {
38 | easing: theme.transitions.easing.sharp,
39 | duration: theme.transitions.duration.leavingScreen,
40 | }),
41 | overflowX: 'hidden',
42 | width: `calc(${theme.spacing(7)} + 1px)`,
43 | [theme.breakpoints.up('sm')]: {
44 | width: `calc(${theme.spacing(8)} + 1px)`,
45 | },
46 | });
47 |
48 | const DrawerHeader = styled('div')(({ theme }) => ({
49 | display: 'flex',
50 | alignItems: 'center',
51 | justifyContent: 'flex-end',
52 | padding: theme.spacing(0, 1),
53 | // necessary for content to be below app bar
54 | ...theme.mixins.toolbar,
55 | }));
56 |
57 | const AppBar = styled(MuiAppBar, {
58 | shouldForwardProp: (prop) => prop !== 'open',
59 | })(({ theme, open }) => ({
60 | zIndex: theme.zIndex.drawer + 1,
61 | transition: theme.transitions.create(['width', 'margin'], {
62 | easing: theme.transitions.easing.sharp,
63 | duration: theme.transitions.duration.leavingScreen,
64 | }),
65 | ...(open && {
66 | marginLeft: drawerWidth,
67 | width: `calc(100% - ${drawerWidth}px)`,
68 | transition: theme.transitions.create(['width', 'margin'], {
69 | easing: theme.transitions.easing.sharp,
70 | duration: theme.transitions.duration.enteringScreen,
71 | }),
72 | }),
73 | }));
74 |
75 | const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
76 | ({ theme, open }) => ({
77 | width: drawerWidth,
78 | flexShrink: 0,
79 | whiteSpace: 'nowrap',
80 | boxSizing: 'border-box',
81 | ...(open && {
82 | ...openedMixin(theme),
83 | '& .MuiDrawer-paper': openedMixin(theme),
84 | }),
85 | ...(!open && {
86 | ...closedMixin(theme),
87 | '& .MuiDrawer-paper': closedMixin(theme),
88 | }),
89 | }),
90 | );
91 |
92 | export default function MiniDrawer({ children }) {
93 | const theme = useTheme();
94 | const [open, setOpen] = React.useState(false);
95 |
96 | const handleDrawerOpen = () => {
97 | setOpen(true);
98 | };
99 |
100 | const handleDrawerClose = () => {
101 | setOpen(false);
102 | };
103 |
104 | return (
105 |
106 |
107 |
108 |
109 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | {/* */}
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | {theme.direction === 'rtl' ? : }
137 |
138 |
139 |
140 |
141 |
142 | {[
143 | { text: 'Home', path: '/', icon: 'house' },
144 | { text: 'Details', path: '/details', icon: 'speed' },
145 | { text: 'Notifications', path: '/alerts', icon: 'notifications' },
146 | ].map((menu, index) => (
147 |
148 |
149 |
156 |
163 |
164 | {menu.icon}
165 |
166 |
167 |
168 |
169 |
170 |
171 | ))}
172 |
173 |
174 |
175 |
176 |
177 | { children }
178 |
179 |
180 | );
181 | }
182 |
--------------------------------------------------------------------------------
/client/containers/NotificationContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SearchBar from '../components/SearchBar';
3 | import Box from '@mui/material/Box';
4 | import InputLabel from '@mui/material/InputLabel';
5 | import MenuItem from '@mui/material/MenuItem';
6 | import FormControl from '@mui/material/FormControl';
7 | import Select from '@mui/material/Select';
8 | import BasicCard from '../components/BasicCard';
9 | import List from '@mui/material/List';
10 | import ListItem from '@mui/material/ListItem';
11 | import Grid from '@mui/material/Grid';
12 | import { useEffect, useState } from 'react';
13 | import NotificationCard from '../components/NotificationCard';
14 |
15 |
16 |
17 |
18 |
19 | export default function NotisContainer (){
20 | const [namespace, setNamespace] = React.useState('');
21 |
22 | const handleChange = (event) => {
23 | setNamespace(event.target.value);
24 | };
25 |
26 | const [notis, setNotis] = useState([]);
27 | useEffect(() => {
28 |
29 | fetch('api/k8s/promAlerts')
30 | .then((response) => response.json())
31 | .then((data) => {
32 |
33 | // nodesInfo.push(node);
34 | setNotis((prev) => ([...data]));
35 | })
36 |
37 | },[])
38 |
39 | return (
40 | <>
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Sort By
49 |
56 | Alert Name
57 | Namespace
58 | Severity
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {notis.map((noti) => (
69 |
70 |
71 |
82 |
83 |
84 | ))}
85 |
86 |
87 | >
88 | );
89 | }
90 |
--------------------------------------------------------------------------------
/client/containers/QueryContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function QueryContainer (){
4 | return (
5 | <>
6 | This is Query container
7 | >
8 | );
9 | }
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from './App.jsx'
4 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
5 | import { Container } from '@mui/material';
6 |
7 | // instead of using regular rendering, we will be using the root method for react v 18.0
8 | const root = ReactDOM.createRoot(document.getElementById('root'));
9 |
10 | root.render(
11 | <>
12 |
13 | >
14 | )
--------------------------------------------------------------------------------
/client/styles/style.css:
--------------------------------------------------------------------------------
1 | .gaugeChartClass {
2 | margin-top: -150px;
3 | }
4 |
5 | /*
6 | .App {
7 | background-color: black;
8 | }
9 | */
10 |
11 | .css-1pnmrwp-MuiTypography-root{
12 | font-weight:700px;
13 | font-size:2rem;
14 | text-align: center;
15 | }
16 |
17 |
18 | .css-pwngrv-MuiPaper-root-MuiCard-root{
19 | background-color:#ffffff;
20 | }
21 |
22 |
23 | /*
24 |
25 | .AppGlass {
26 | display: grid;
27 | height: 97%;
28 | width: 97%;
29 | border-radius: 2rem;
30 | background-color: blue;
31 | } */
32 |
33 |
34 | /* This is the header with the logo */
35 | /* top nav bar */
36 | .css-1nt9mjz-MuiToolbar-root{
37 | background-color:#073072;
38 | min-height: 80px;
39 |
40 | }
41 |
42 |
43 | .css-12i7wg6-MuiPaper-root-MuiDrawer-paper{
44 | background-color: #f2f7ff;
45 | width: 5%;
46 | border-radius: 16px;
47 | }
48 |
49 | /* Icon box */
50 | .css-h4y409-MuiList-root{
51 | /* padding-top: 700%; */
52 | /* vertical-align: middle; */
53 | }
54 |
55 |
56 | .css-p55zm9-MuiButtonBase-root-MuiIconButton-root {
57 | margin-left:-18px
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Neptune
8 |
9 |
10 |
11 | />
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "neptune",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "cross-env NODE_ENV=production nodemon server/server.js",
8 | "build": "NODE_ENV=production webpack",
9 | "dev": "concurrently \"cross-env NODE_ENV=development webpack-dev-server --open --hot --progress --color \" \"cross-env NODE_ENV=test nodemon ./server/server.js\"",
10 | "test": "jest"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/lawyeh/Neptune.git"
15 | },
16 | "keywords": [],
17 | "author": "Jin Yoo, Lawrence Yeh, Miranda Jaramillo, Swan Htet",
18 | "license": "ISC",
19 | "bugs": {
20 | "url": "https://github.com/lawyeh/Neptune/issues"
21 | },
22 | "homepage": "https://github.com/oslabs-beta/Neptune",
23 | "dependencies": {
24 | "@emotion/react": "^11.9.3",
25 | "@emotion/styled": "^11.9.3",
26 | "@kubernetes/client-node": "^0.16.3",
27 | "@mui/icons-material": "^5.8.4",
28 | "@mui/material": "^5.9.0",
29 | "@mui/x-data-grid": "^5.11.0",
30 | "@visx/group": "^2.10.0",
31 | "@visx/heatmap": "^2.10.0",
32 | "@visx/mock-data": "^2.1.2",
33 | "@visx/responsive": "^2.10.0",
34 | "@visx/scale": "^2.2.2",
35 | "chart.js": "^3.8.0",
36 | "chartjs-plugin-datalabels": "^2.0.0",
37 | "concurrently": "^7.2.2",
38 | "cors": "^2.8.5",
39 | "express": "^4.18.1",
40 | "fs": "0.0.1-security",
41 | "match-sorter": "^6.3.1",
42 | "node-cmd": "^5.0.0",
43 | "node-fetch": "^3.2.4",
44 | "prom-client": "^14.0.1",
45 | "react": "^18.1.0",
46 | "react-chartjs-2": "^4.3.1",
47 | "react-copy-to-clipboard": "^5.1.0",
48 | "react-dom": "^18.1.0",
49 | "react-router-dom": "^6.3.0",
50 | "react-scroll": "^1.8.7",
51 | "react-table": "^7.8.0",
52 | "ts-node": "^10.8.2",
53 | "webpack": "^5.72.1"
54 | },
55 | "devDependencies": {
56 | "@babel/core": "^7.17.10",
57 | "@babel/preset-env": "^7.17.10",
58 | "@babel/preset-react": "^7.16.7",
59 | "babel-loader": "^8.2.5",
60 | "cross-env": "^7.0.3",
61 | "css-loader": "^6.7.1",
62 | "html-webpack-plugin": "^5.5.0",
63 | "jest": "^28.1.2",
64 | "jest-puppeteer": "^6.1.1",
65 | "mini-css-extract-plugin": "^2.6.0",
66 | "nodemon": "^2.0.19",
67 | "puppeteer": "^15.3.2",
68 | "react-select": "^5.3.2",
69 | "sass-loader": "^12.6.0",
70 | "style-loader": "^3.3.1",
71 | "supertest": "^6.2.3",
72 | "ts-loader": "^9.3.1",
73 | "webpack-cli": "^4.9.2",
74 | "webpack-dev-server": "^4.9.3"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/server/controllers/k8sClientController.js:
--------------------------------------------------------------------------------
1 | const k8s = require('@kubernetes/client-node');
2 |
3 | //New instance of k8s, (Kube config are the files that connect )
4 | const kc = new k8s.KubeConfig();
5 | kc.loadFromDefault();
6 |
7 | const k8sApiSvc = kc.makeApiClient(k8s.CoreV1Api); // Queries services
8 | const k8sApiDepl = kc.makeApiClient(k8s.AppsV1Api); // Queries deployments
9 | //const metricsClient = new k8s.Metrics(kc);
10 |
11 | k8sController = {};
12 |
13 | // Getting pod count and pod names
14 | k8sController.getAllPods = async (req, res, next) => {
15 | try {
16 | const podsResult = await k8sApiSvc.listPodForAllNamespaces();
17 | res.locals.podData = podsResult.response.body.items;
18 | res.locals.podCount = podsResult.body.items;
19 | const podNames = [];
20 | const podIps = [];
21 | const podData = {};
22 | res.locals.podData.forEach((element) => {
23 | // if namespace does not exist in podData object
24 | if (!podData[element.metadata.namespace]) {
25 | // then create namespace as a property and assign podname, podip as values in an object
26 | podData[element.metadata.namespace] = [
27 | {
28 | 'Pod Name': element.metadata.name,
29 | 'Pod IP': element.status.podIP,
30 | },
31 | ];
32 | // else if namespac exists AND
33 | } else if (podData[element.metadata.namespace]) {
34 | podData[element.metadata.namespace].push({
35 | 'Pod Name': element.metadata.name,
36 | 'Pod IP': element.status.podIP,
37 | });
38 | }
39 | // console.log('YODA', podData);
40 | res.locals.podData = podData;
41 | });
42 |
43 | podsResult.body.items.forEach((element) => {
44 | podNames.push(element.metadata.generateName);
45 | podIps.push(element.status.podIP);
46 | });
47 | res.locals.podNames = podNames;
48 | res.locals.podIps = podIps;
49 | res.locals.info = podsResult.body.items;
50 | return next();
51 | } catch (err) {
52 | return next({
53 | log: 'Error getting data from getAllPods',
54 | status: 404,
55 | message: {
56 | err: 'An error happened trying to get the data from getAllPods',
57 | },
58 | });
59 | }
60 | };
61 |
62 | // Getting list of nodes and list of component statuses
63 | k8sController.getAllNodes = async (req, res, next) => {
64 | const { name } = req.params;
65 | const nodeNames = [];
66 | try {
67 | const nodeResult = await k8sApiSvc.listNode(name);
68 | nodeResult.response.body.items.forEach((element) => {
69 | nodeNames.push(element.metadata.name);
70 | });
71 |
72 | res.locals.nodeList = nodeNames;
73 | // const nodeStatus = await k8sApiSvc.listComponentStatus();
74 | // res.locals.nodeList.nodeStatus = nodeStatus.body;
75 | return next();
76 | } catch (err) {
77 | return next({
78 | log: 'Error getting data from getAllNodes',
79 | status: 500,
80 | message: {
81 | err: 'An error happened trying to get All Nodes',
82 | },
83 | });
84 | }
85 | };
86 |
87 | // Getting number of namespaces and their names
88 | k8sController.getAllNamespaces = async (req, res, next) => {
89 | try {
90 | const namespaceResult = await k8sApiSvc.listNamespace();
91 | res.locals.namespace = namespaceResult.response.body.items;
92 | const namespaceNames = [];
93 | namespaceResult.response.body.items.forEach((element) => {
94 | namespaceNames.push(element.metadata.name);
95 | });
96 | res.locals.namespaceNames = namespaceNames;
97 | return next();
98 | } catch (err) {
99 | return next({
100 | log: 'Error getting data from getAllNamespaces',
101 | status: 500,
102 | message: {
103 | err: 'An error happened trying to get the data from getAllNamespaces',
104 | },
105 | });
106 | }
107 | };
108 |
109 | // Getting all deployments in cluster
110 | k8sController.getDeployment = async (req, res, next) => {
111 | try {
112 | const getDeployment = await k8sApiDepl.listDeploymentForAllNamespaces();
113 | res.locals.deployment = getDeployment.body.items;
114 | return next();
115 | } catch (err) {
116 | return next({
117 | log: 'Error getting data from getDeploymentList',
118 | status: 500,
119 | message: {
120 | err: 'An error happened trying to get the data from getDeploymentList',
121 | },
122 | });
123 | }
124 | };
125 |
126 | // Getting all services in cluster
127 | k8sController.getService = async (req, res, next) => {
128 | try {
129 | const getService = await k8sApiSvc.listServiceForAllNamespaces();
130 | res.locals.service = getService.body.items;
131 | return next();
132 | } catch (err) {
133 | return next({
134 | log: 'Error getting data from getServiceList',
135 | status: 500,
136 | message: {
137 | err: 'An error happened trying to get the data from getServiceList',
138 | },
139 | });
140 | }
141 | };
142 |
143 | module.exports = k8sController;
144 |
--------------------------------------------------------------------------------
/server/controllers/promController.js:
--------------------------------------------------------------------------------
1 | // kubectl port-forward prometheus-prometheus-kube-prometheus-prometheus-0 --namespace=default 9090:9090
2 | const { response } = require('express');
3 | const fetch = (...args) =>
4 | import('node-fetch').then(({ default: fetch }) => fetch(...args));
5 | const queryURL = 'http://127.0.0.1:9090/api/v1/';
6 | const promController = {};
7 |
8 | //----------CLUSTER----------//
9 |
10 | //CLUSTER - Total CPU Cores
11 | promController.promClusterCpuCore = async (req, res, next) => {
12 | try {
13 | const response = await fetch(
14 | `${queryURL}query?query=count without(cpu, mode) (node_cpu_seconds_total{mode="idle"})`
15 | );
16 | res.locals.promClusterCpuCore = await response.json();
17 | res.locals.promClusterCpuCore = parseInt(
18 | res.locals.promClusterCpuCore.data.result[0].value[1]
19 | );
20 | return next();
21 | } catch (err) {
22 | return next({
23 | log: 'Error getting data from promClusterCpuCore',
24 | status: 500,
25 | message: {
26 | err: 'An error happened trying to get the data from promClusterCpuCore',
27 | },
28 | });
29 | }
30 | };
31 |
32 | // CLUSTER - CPU USAGE
33 | promController.promClusterCpuPct = async (req, res, next) => {
34 | try {
35 | const response = await fetch(
36 | `${queryURL}query?query=1 - sum(avg by (mode) (rate(node_cpu_seconds_total{job="node-exporter", mode=~"idle|iowait|steal"}[10m])))`
37 | );
38 | data = await response.json();
39 | data = Number(data.data.result[0].value[1]) * 100;
40 | res.locals.promClusterCpuPct = data;
41 | return next();
42 | } catch (err) {
43 | return next({
44 | log: 'Error getting data from promClusterCpuPct',
45 | status: 500,
46 | message: {
47 | err: 'An error happened trying to get the data from promClusterCpuPct',
48 | },
49 | });
50 | }
51 | };
52 |
53 | // CLUSTER - TOTAL MEMORY
54 | promController.promClusterMemoryTotal = async (req, res, next) => {
55 | try {
56 | const response = await fetch(
57 | `${queryURL}query?query=sum(container_memory_working_set_bytes)`
58 | );
59 | data = await response.json();
60 | res.locals.promClusterMemoryTotal = data.data.result[0].value[1];
61 | return next();
62 | } catch (err) {
63 | return next({
64 | log: 'Error getting data from promClusterMemoryTotal',
65 | status: 500,
66 | message: {
67 | err: 'An error happened trying to get the data from promClusterMemoryTotal',
68 | },
69 | });
70 | }
71 | };
72 |
73 | // CLUSTER - MEMORY UTILIZATION
74 | promController.promClusterMemoryUtil = async (req, res, next) => {
75 | try {
76 | const response = await fetch(
77 | `${queryURL}query?query=node_memory_Active_bytes/node_memory_MemTotal_bytes`
78 | );
79 | data = await response.json();
80 | data = data.data.result[0].value[1] * 100;
81 | res.locals.promClusterMemoryUtil = data.toFixed(2);
82 | return next();
83 | } catch (err) {
84 | return next({
85 | log: 'Error getting data from promClusterMemoryUtil',
86 | status: 500,
87 | message: {
88 | err: 'An error happened trying to get the data from promClusterMemoryUtil',
89 | },
90 | });
91 | }
92 | };
93 |
94 | // CLUSTER - TOTAL MEMORY
95 | promController.promClusterMemoryTotal = async (req, res, next) => {
96 | try {
97 | const response = await fetch(
98 | `${queryURL}query?query=sum(container_memory_working_set_bytes)`
99 | );
100 | data = await response.json();
101 | res.locals.promClusterMemoryTotal = data.data.result[0].value[1];
102 | return next();
103 | } catch (err) {
104 | return next({
105 | log: 'Error getting data from promClusterMemoryTotal',
106 | status: 500,
107 | message: {
108 | err: 'An error happened trying to get the data from promClusterMemoryTotal',
109 | },
110 | });
111 | }
112 | };
113 |
114 | //----------NAMESPACES----------//
115 |
116 | // GETTING ALL THE NAMESPACES IN AN ARRAY
117 | promController.promNamespaces = async (req, res, next) => {
118 | try {
119 | const response = await fetch(
120 | `${queryURL}query?query=kube_namespace_created`
121 | );
122 | data = await response.json();
123 | const namespaces = [];
124 | data.data.result.forEach((element) => {
125 | namespaces.push(element.metric.namespace);
126 | });
127 | res.locals.promNamespaces = namespaces;
128 | return next();
129 | } catch (err) {
130 | return next({
131 | log: 'Error getting data from promNamespaces',
132 | status: 500,
133 | message: {
134 | err: 'An error happened trying to get the data from promNamespaces',
135 | },
136 | });
137 | }
138 | };
139 |
140 | //----------NODES----------//
141 |
142 | // NODE - CPU usage
143 | promController.promNodeCpu = async (req, res, next) => {
144 | try {
145 | const response = await fetch(
146 | `${queryURL}query?query=(1 - sum by (instance)(increase(node_cpu_seconds_total{mode="idle"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))*100)`
147 | );
148 | res.locals.promNodeCpu = await response.json();
149 | res.locals.promNodeCpu = res.locals.promNodeCpu.data;
150 | return next();
151 | } catch (err) {
152 | return next({
153 | log: 'Error getting data from promNodeCpu',
154 | status: 500,
155 | message: {
156 | err: 'An error happened trying to get the data from promNodeCpu',
157 | },
158 | });
159 | }
160 | };
161 |
162 | // NODE - Return pod capacity of node as a number
163 | promController.promNodePodCap = async (req, res, next) => {
164 | try {
165 | const response = await fetch(
166 | `${queryURL}query?query=kube_node_status_capacity{resource="pods"}`
167 | );
168 | res.locals.promNodePodCap = await response.json();
169 | res.locals.promNodePodCap = parseInt(
170 | res.locals.promNodePodCap.data.result[0].value[1]
171 | );
172 | return next();
173 | } catch (err) {
174 | return next({
175 | log: 'Error getting data from promNodePodCap',
176 | status: 500,
177 | message: {
178 | err: 'An error happened trying to get the data from promNodePodCap',
179 | },
180 | });
181 | }
182 | };
183 |
184 | // NODE - Return network utilization
185 | promController.promNodeNetUtil = async (req, res, next) => {
186 | try {
187 | const response = await fetch(
188 | `${queryURL}query?query=sum(rate(container_network_receive_bytes_total[5m]))`
189 | );
190 | data = await response.json();
191 | res.locals.promNodeNetUtil = data;
192 | res.locals.promNodeNetUtil = Number(data.data.result[0].value[1]);
193 | return next();
194 | } catch (err) {
195 | return next({
196 | log: 'Error getting data from promNodeNetUtil',
197 | status: 500,
198 | message: {
199 | err: 'An error happened trying to get the data from promNodeNetUtil',
200 | },
201 | });
202 | }
203 | };
204 |
205 | // NODE - Return network errors
206 | promController.promNodeNetErr = async (req, res, next) => {
207 | try {
208 | const response1 = await fetch(
209 | `${queryURL}query?query=sum(node_network_receive_errs_total)`
210 | );
211 | const receiveErr = await response1.json();
212 |
213 | const response2 = await fetch(
214 | `${queryURL}query?query=sum(node_network_transmit_errs_total)`
215 | );
216 | const transmitErr = await response2.json();
217 |
218 | const networkErrors = Math.floor(
219 | (parseInt(receiveErr.data.result[0].value[1]) +
220 | parseInt(transmitErr.data.result[0].value[1])) /
221 | 1024
222 | );
223 |
224 | res.locals.promNodeNetErr = networkErrors;
225 |
226 | return next();
227 | } catch (err) {
228 | return next({
229 | log: 'Error getting data from promNodeNetErr',
230 | status: 500,
231 | message: {
232 | err: 'An error happened trying to get the data from promNodeNetErr',
233 | },
234 | });
235 | }
236 | };
237 |
238 | //----------ALERTS----------//
239 |
240 | // ALERTS
241 | promController.promAlerts = async (req, res, next) => {
242 | try {
243 | const response = await fetch(`${queryURL}alerts`);
244 | data = await response.json();
245 | res.locals.promAlerts = data.data.alerts;
246 | return next();
247 | } catch (err) {
248 | return next({
249 | log: 'Error getting data from promAlerts',
250 | status: 500,
251 | message: {
252 | err: 'An error happened trying to get the data from promAlerts',
253 | },
254 | });
255 | }
256 | };
257 |
258 | module.exports = promController;
259 |
--------------------------------------------------------------------------------
/server/controllers/queryController.js:
--------------------------------------------------------------------------------
1 | // const { data } = require('../../client/components/PieChart');
2 | const fetch = (...args) =>
3 | import('node-fetch').then(({ default: fetch }) => fetch(...args));
4 | const queryURL = 'http://127.0.0.1:9090/api/v1/';
5 | const queryController = {};
6 |
7 | // Returning all queries for PROMQL
8 | queryController.allQueries = async (req, res, next) => {
9 | try {
10 | const response = await fetch(`${queryURL}label/__name__/values`);
11 | data = await response.json();
12 | res.locals.allQueries = data.data;
13 | return next();
14 | } catch (err) {
15 | return next({
16 | log: 'Error getting data from allQueries in queryController',
17 | status: 500,
18 | message: {
19 | err: 'An error happened trying to get the data from allQueries',
20 | },
21 | });
22 | }
23 | };
24 |
25 | // Memory in bytes for all namespaces, returned in object, key is name of namespace, value is an array with data
26 | queryController.memoryAllNamespaces = async (req, res, next) => {
27 | let start = new Date();
28 | let end = new Date(start.getTime());
29 | end.setHours(end.getHours() - 24);
30 | let endDateTime = start.toISOString();
31 | let startDateTime = end.toISOString();
32 | const memoryCache = {};
33 | const query = `${queryURL}query_range?query=sum(container_memory_working_set_bytes) by (namespace)&start=${startDateTime}&end=${endDateTime}&step=30m`;
34 | try {
35 | const response = await fetch(query);
36 | data = await response.json();
37 | data.data.result.forEach((element) => {
38 | if (!memoryCache[element.metric.namespace]) {
39 | memoryCache[element.metric.namespace] = element.values;
40 | }
41 | });
42 | res.locals.memoryAllNamespaces = memoryCache;
43 | return next();
44 | } catch (err) {
45 | return next({
46 | log: 'Error getting data from memoryAllNamespaces in queryController',
47 | status: 500,
48 | message: {
49 | err: 'An error happened trying to get the data from memoryAllNamespaces',
50 | },
51 | });
52 | }
53 | };
54 |
55 | // Memory in bytes for all pods, returned in object, key is name of namespace, value is an array with data
56 | queryController.memoryAllPods = async (req, res, next) => {
57 | let start = new Date();
58 | let end = new Date(start.getTime());
59 | end.setHours(end.getHours() - 24);
60 | let endDateTime = start.toISOString();
61 | let startDateTime = end.toISOString();
62 | const memoryCache = {};
63 | const query = `${queryURL}query_range?query=sum(container_memory_working_set_bytes) by (pod)&start=${startDateTime}&end=${endDateTime}&step=30m`;
64 | try {
65 | const response = await fetch(query);
66 | data = await response.json();
67 | data.data.result.forEach((element) => {
68 | if (!memoryCache[element.metric.pod]) {
69 | memoryCache[element.metric.pod] = element.values;
70 | }
71 | });
72 | res.locals.memoryAllPods = memoryCache;
73 | return next();
74 | } catch (err) {
75 | return next({
76 | log: 'Error getting data from memoryAllPods in queryController',
77 | status: 500,
78 | message: {
79 | err: 'An error happened trying to get the data from memoryAllPods',
80 | },
81 | });
82 | }
83 | };
84 |
85 | // Get cluster network utilization - Transmit
86 | queryController.clusterNetRec = async (req, res, next) => {
87 | let start = new Date();
88 | let end = new Date(start.getTime());
89 | end.setHours(end.getHours() - 24);
90 | let endDateTime = start.toISOString();
91 | let startDateTime = end.toISOString();
92 | const memoryCache = {};
93 | const query = `${queryURL}query_range?query=sum(irate(container_network_receive_bytes_total[10m])) by (namespace)&start=${startDateTime}&end=${endDateTime}&step=30m`;
94 | try {
95 | const response = await fetch(query);
96 | data = await response.json();
97 | data.data.result.forEach((element) => {
98 | if (!memoryCache[element.metric.pod]) {
99 | memoryCache[element.metric.pod] = element.values;
100 | }
101 | });
102 | res.locals.clusterNetRec = memoryCache;
103 | return next();
104 | } catch (err) {
105 | return next({
106 | log: 'Error getting data from clusterNetRec in queryController',
107 | status: 500,
108 | message: {
109 | err: 'An error happened trying to get the data from clusterNetRec',
110 | },
111 | });
112 | }
113 | };
114 |
115 | // Get cluster network utilization - Receive
116 | queryController.clusterNetTrans = async (req, res, next) => {
117 | let start = new Date();
118 | let end = new Date(start.getTime());
119 | end.setHours(end.getHours() - 24);
120 | let endDateTime = start.toISOString();
121 | let startDateTime = end.toISOString();
122 | const memoryCache = {};
123 | const query = `${queryURL}query_range?query=sum(irate(container_network_transmit_bytes_total[10m])) by (namespace)&start=${startDateTime}&end=${endDateTime}&step=30m`;
124 | try {
125 | const response = await fetch(query);
126 | data = await response.json();
127 | data.data.result.forEach((element) => {
128 | if (!memoryCache[element.metric.pod]) {
129 | memoryCache[element.metric.pod] = element.values;
130 | }
131 | });
132 | res.locals.clusterNetTrans = memoryCache;
133 | return next();
134 | } catch (err) {
135 | return next({
136 | log: 'Error getting data from clusterNetTrans in queryController',
137 | status: 500,
138 | message: {
139 | err: 'An error happened trying to get the data from clusterNetTrans',
140 | },
141 | });
142 | }
143 | };
144 |
145 | module.exports = queryController;
146 |
--------------------------------------------------------------------------------
/server/routes/routes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const k8sRouter = express.Router();
3 | const k8sController = require('../controllers/k8sClientController');
4 | const promController = require('../controllers/promController');
5 | const queryController = require('../controllers/queryController');
6 |
7 | /**
8 | * ************************************
9 | *
10 | * @module kubernetes client API -requests
11 | * @description contains our routes for our k8's API client fetch requests
12 | *
13 | * ************************************
14 | */
15 |
16 | //----------PODS---------//
17 |
18 | // ROUTE FOR PODS, PODNAMES, PODIPS
19 | k8sRouter.get('/podCount', k8sController.getAllPods, (req, res) => {
20 | return res.status(200).json(res.locals.podCount);
21 | });
22 | k8sRouter.get('/podNames', k8sController.getAllPods, (req, res) => {
23 | return res.status(200).json(res.locals.podNames);
24 | });
25 | k8sRouter.get('/podIps', k8sController.getAllPods, (req, res) => {
26 | return res.status(200).json(res.locals.podIps);
27 | });
28 | k8sRouter.get('/podData', k8sController.getAllPods, (req, res) => {
29 | return res.status(200).json(res.locals.podData);
30 | });
31 |
32 | k8sRouter.get('/podInfo', k8sController.getAllPods, (req, res) => {
33 | return res.status(200).json(res.locals.info);
34 | });
35 |
36 | //----------NODES----------//
37 |
38 | // ROUTE FOR NODE LIST AND NODE STATUS
39 | // returns node count
40 | k8sRouter.get('/node', k8sController.getAllNodes, (req, res) => {
41 | return res.status(200).json(res.locals.nodeList);
42 | });
43 |
44 | // returns node cpu usage
45 | k8sRouter.get('/promNodeCpu', promController.promNodeCpu, (req, res) => {
46 | return res.status(200).json(res.locals.promNodeCpu);
47 | });
48 |
49 | // return pod capacity of node as a number
50 | k8sRouter.get('/promNodePodCap', promController.promNodePodCap, (req, res) => {
51 | return res.status(200).json(res.locals.promNodePodCap);
52 | });
53 |
54 | // returns node network utilization
55 | k8sRouter.get(
56 | '/promNodeNetUtil',
57 | promController.promNodeNetUtil,
58 | (req, res) => {
59 | return res.status(200).json(res.locals.promNodeNetUtil);
60 | }
61 | );
62 |
63 | // returns node network errors
64 | k8sRouter.get('/promNodeNetErr', promController.promNodeNetErr, (req, res) => {
65 | return res.status(200).json(res.locals.promNodeNetErr);
66 | });
67 |
68 | //----------NAMESPACES----------//
69 |
70 | // ROUTE FOR NUMBER OF NAMESPACES
71 | k8sRouter.get('/namespace', k8sController.getAllNamespaces, (req, res) => {
72 | return res.status(200).json(res.locals.namespace);
73 | });
74 | // ROUTE FOR NAMESPACES NAMES
75 | k8sRouter.get('/namespaceNames', k8sController.getAllNamespaces, (req, res) => {
76 | return res.status(200).json(res.locals.namespaceNames);
77 | });
78 |
79 | //----------DEPLOYMENTS----------//
80 |
81 | // ROUTE FOR DEPLOYMENTS
82 | k8sRouter.get('/deployment', k8sController.getDeployment, (req, res) => {
83 | return res.status(200).json(res.locals.deployment);
84 | });
85 |
86 | //----------SERVICES----------//
87 |
88 | // ROUTE FOR SERVICES
89 | k8sRouter.get('/services', k8sController.getService, (req, res) => {
90 | return res.status(200).json(res.locals.service);
91 | });
92 |
93 | /**
94 | * ************************************
95 | *
96 | * @module promql-requests
97 | * @description contains our routes for our promethus fetch requests
98 | *
99 | * ************************************
100 | */
101 |
102 | //----------PROMETHEUS ROUTE HANDLERS----------//
103 |
104 | // TESTING NAMESPACES ********************
105 | k8sRouter.get('/promNamespaces', promController.promNamespaces, (req, res) => {
106 | return res.status(200).json(res.locals.promNamespaces);
107 | });
108 |
109 | //----------PROMETHEUS CLUSTERS----------//
110 |
111 | // PROMETHEUS CPU CORES
112 | k8sRouter.get(
113 | '/promClusterCpuCore',
114 | promController.promClusterCpuCore,
115 | (req, res) => {
116 | return res.status(200).json(res.locals.promClusterCpuCore);
117 | }
118 | );
119 |
120 | // PROMETHEUS CPU UTILIZATION
121 | k8sRouter.get(
122 | '/promClusterCpuPct',
123 | promController.promClusterCpuPct,
124 | (req, res) => {
125 | return res.status(200).json(res.locals.promClusterCpuPct);
126 | }
127 | );
128 |
129 | // // PROMETHEUS CLUSTER MEMORY UTILIZATION
130 | k8sRouter.get(
131 | '/promClusterMemoryUtil',
132 | promController.promClusterMemoryUtil,
133 | (req, res) => {
134 | return res.status(200).json(res.locals.promClusterMemoryUtil);
135 | }
136 | );
137 |
138 | // // PROMETHEUS CLUSTER MEMORY TOTAL
139 | k8sRouter.get(
140 | '/promClusterMemoryTotal',
141 | promController.promClusterMemoryTotal,
142 | (req, res) => {
143 | return res.status(200).json(res.locals.promClusterMemoryTotal);
144 | }
145 | );
146 |
147 | //----------PROMETHEUS NODES----------//
148 |
149 | // Prometheus node CPU usage
150 | k8sRouter.get('/promNodeCpu', promController.promNodeCpu, (req, res) => {
151 | return res.status(200).json(res.locals.promNodeCpu);
152 | });
153 |
154 | k8sRouter.get('/promAlerts', promController.promAlerts, (req, res) => {
155 | return res.status(200).json(res.locals.promAlerts);
156 | });
157 |
158 | //----------PROMQL QUERIES----------//
159 |
160 | k8sRouter.get('/allQueries', queryController.allQueries, (req, res) => {
161 | return res.status(200).json(res.locals.allQueries);
162 | });
163 |
164 | // Memory in bytes for all namespaces
165 | k8sRouter.get(
166 | '/memoryAllNamespaces',
167 | queryController.memoryAllNamespaces,
168 | (req, res) => {
169 | return res.status(200).json(res.locals.memoryAllNamespaces);
170 | }
171 | );
172 |
173 | // Memory in bytes for all pods
174 | k8sRouter.get('/memoryAllPods', queryController.memoryAllPods, (req, res) => {
175 | return res.status(200).json(res.locals.memoryAllPods);
176 | });
177 |
178 | // Cluster Network Util Transmitted
179 | k8sRouter.get('/clusterNetRec', queryController.clusterNetRec, (req, res) => {
180 | return res.status(200).json(res.locals.clusterNetRec);
181 | });
182 |
183 | // Cluster Network Util Transmitted
184 | k8sRouter.get(
185 | '/clusterNetTrans',
186 | queryController.clusterNetTrans,
187 | (req, res) => {
188 | return res.status(200).json(res.locals.clusterNetTrans);
189 | }
190 | );
191 |
192 | module.exports = k8sRouter;
193 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | // const exp = require('constants');
2 | const express = require('express');
3 | const path = require('path');
4 | const cors = require('cors');
5 | const k8sRouter = require('./routes/routes');
6 |
7 | const PORT = 3000;
8 | const app = express();
9 |
10 | app.use(cors());
11 | app.use(express.json());
12 | app.use(express.urlencoded({ extended: true }));
13 | app.use(express.static(path.resolve(__dirname, '../build')));
14 |
15 | app.use('/api/k8s', k8sRouter);
16 |
17 | app.get('/api', (req, res) => {
18 | return res.status(200).json('HELLO from Neptune BackEnd');
19 | });
20 |
21 | app.get('/', (req, res) => {
22 | return res.status(200).json('TESTING POSTMAN');
23 | });
24 |
25 | // Global route handler
26 | app.use('*', (req, res) => {
27 | console.log('Page not found.');
28 | return res.status(404).send('404! Page not found.');
29 | });
30 |
31 | // Global error handler
32 | app.use(defaultErrorHandler);
33 | function defaultErrorHandler(err, req, res, next) {
34 | const defaultErr = {
35 | log: 'Global error handler',
36 | status: 500,
37 | message: {
38 | err: 'An error occurred, and this is the global error handler',
39 | },
40 | };
41 | const errorObj = Object.assign(defaultErr, err);
42 | console.log(errorObj.log);
43 | return res.status(errorObj.status).send(JSON.stringify(errorObj.message));
44 | }
45 |
46 | app.listen(PORT, () => {
47 | console.log(`Server listening on port: ${PORT}...`);
48 | });
49 |
50 | module.exports = app;
51 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
5 | // const WebpackDevServer = require('webpack-dev-server');
6 |
7 | const config = {
8 | mode: process.env.NODE_ENV,
9 | entry: './client/index.js',
10 | output: {
11 | path: path.resolve(__dirname, 'build'),
12 | filename: 'bundle.js',
13 | publicPath: '/',
14 | clean: true,
15 | },
16 | plugins: [
17 | new HtmlWebpackPlugin({
18 | template: path.join(__dirname, 'index.html'),
19 | }),
20 | new MiniCssExtractPlugin(),
21 | ],
22 | module: {
23 | rules: [
24 | {
25 | test: /\.(js|jsx)$/,
26 | use: {
27 | loader: 'babel-loader',
28 | options: {
29 | presets: ['@babel/preset-env', '@babel/preset-react'],
30 | plugins: [],
31 | },
32 | },
33 | exclude: /node_modules/,
34 | },
35 | // {
36 | // test: /\.(ts|tsx)$/,
37 | // use: {
38 | // loader: 'ts-loader',
39 | // },
40 | // exclude: /node_modules/,
41 | // },
42 | // {
43 | // test: /\.css$/,
44 | // use: [MiniCssExtractPlugin.loader, "css-loader"],
45 | // },
46 | {
47 | test: /\.css$/i,
48 | exclude: /node_modules/,
49 | use: [MiniCssExtractPlugin.loader, 'css-loader'],
50 | // check the webpack
51 | // Dockerize diff container
52 | },
53 | {
54 | test: /\.(png|svg|jpg|jpeg|gif)$/i,
55 | type: 'asset/resource',
56 | },
57 | ],
58 | },
59 |
60 | devServer: {
61 | host: 'localhost',
62 | port: 8080,
63 | hot: true,
64 | open: true,
65 | compress: true,
66 | historyApiFallback: true,
67 |
68 | static: {
69 | directory: path.join(__dirname, 'build'),
70 | publicPath: '/',
71 | },
72 |
73 | headers: { 'Access-Control-Allow-Origin': '*' },
74 |
75 | proxy: {
76 | '/api/**': {
77 | target: 'http://localhost:3000',
78 | secure: false,
79 | },
80 | },
81 | watchFiles: ['client'],
82 | },
83 | resolve: { extensions: ['.js', '.jsx'] },
84 | };
85 |
86 | module.exports = config;
87 |
--------------------------------------------------------------------------------