├── .babelrc
├── .dockerignore
├── .gitignore
├── DEV_SETUP.md
├── Dockerfile
├── LICENSE.md
├── README.md
├── assets
├── images
│ ├── nautics-resize.jpg
│ ├── nautics.png
│ └── ocean.jpg
└── styles
│ ├── dashboard.scss
│ └── styles.scss
├── devspace.yaml
├── devspace_start.sh
├── jest.all.config.js
├── k8s
├── demo-app
│ ├── demo-app-mongo-config.yml
│ ├── demo-app-mongo-secret.yml
│ ├── demo-app-mongo.yml
│ └── demo-app.yml
└── kubernautics
│ ├── cluster-role-binding.yaml
│ ├── cluster-role.yaml
│ └── kubernautics.yml
├── package-lock.json
├── package.json
├── scripts
├── dev_cluster.sh
└── prod_cluster.sh
├── server
├── __tests__
│ └── server.test.js
├── controllers
│ ├── clusterController.js
│ └── dataController.js
├── index.js
├── jest.config.js
├── routers
│ ├── clusterRoutes.js
│ └── dataRoutes.js
└── server.js
├── src
├── __tests__
│ ├── ClearCharts.test.js
│ ├── CustomChart.test.js
│ └── NavBar.test.jsx
├── components
│ ├── App.jsx
│ ├── ClearCharts.jsx
│ ├── ClusterVisualizer.jsx
│ ├── CustomChart.jsx
│ ├── Dashboard.jsx
│ ├── MonitoringComponent.jsx
│ ├── NavBar.jsx
│ └── action.js
├── index.html
├── index.js
├── jest-setup.js
└── jest.config.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/charts
15 | **/docker-compose*
16 | **/compose*
17 | **/Dockerfile*
18 | **/node_modules
19 | **/npm-debug.log
20 | **/obj
21 | **/secrets.dev.yaml
22 | **/values.dev.yaml
23 | LICENSE
24 | README.md
25 |
26 | ./scripts
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE files
2 | .vscode/
3 |
4 | # nodejs ignores
5 | node_modules/
6 |
7 | # webpack build artifacts
8 | dist/
9 |
10 | # Ignore DevSpace cache and log folder
11 | .devspace/
12 |
13 | #ignore ds_store
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/DEV_SETUP.md:
--------------------------------------------------------------------------------
1 | # Kubernautics
2 |
3 | ## Setting Up the Dev Environment
4 | The dev environment for Kubernautics places the app inside a local Kubernetes cluster while you iterate, so changes to code can be evaluated in an environment as close to a production scenario as possible. Changes to the codebase are made locally and are hot-loaded into the cluster as you work.
5 |
6 | ### Prerequisites
7 | The script that sets up the dev environment assumes the following programs are installed and present on your `$PATH`:
8 | - `minikube`
9 | - `devspace`
10 |
11 | `minikube` should be installed in whatever way is most appropriate for your system. [See the docs here for guidance](https://minikube.sigs.k8s.io/docs/start/).
12 |
13 | `devspace` is most easily installed by running --
14 | ```
15 | npm install -g devspace
16 | ```
17 | -- [alternate install methods are described here](https://www.devspace.sh/docs/getting-started/installation).
18 | ### Starting Up
19 | You can set up the dev cluster by running --
20 |
21 | ```
22 | npm run cluster up
23 | ```
24 | This will start up your minikube cluster, create a `kubernautics-dev` cluster namespace, set up all the necessary deployments/services, and give you a shell sitting inside a container on the cluster from which you can run kubernautics --
25 | ```
26 | npm run dev
27 | ```
28 | Kubernautics makes use of `webpack-dev-server` and `nodemon` in development mode to pick up changes to the codebase. Once up and running, changes made to the code will be reflected in-cluster.
29 |
30 | To leave the container and return your terminal to your local machine's context, simply run --
31 | ```
32 | exit
33 | ```
34 |
35 | ### Tearing Down
36 | The project can be removed from your minikube cluster, and the cluster itself shut down, by `exit`ing the container shell environment and running --
37 | ```
38 | npm run cluster down
39 | ```
40 | -- on your local machine.
41 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:lts-alpine
2 | ENV NODE_ENV=production
3 | WORKDIR /usr/src/app
4 | COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
5 | RUN npm install --production --silent
6 | COPY . .
7 | EXPOSE 3000
8 | RUN chown -R node /usr/src/app
9 | USER node
10 | CMD ["npm", "start"]
11 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Kubernautics
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kubernautics
2 |
3 |
4 |
5 |
6 |
7 | Kubernautics is an open-source cluster monitoring solution that looks to prioritize ease of deployment, ease of use, and a low resource footprint. The program is placed directly into the cluster it is meant to monitor, and interfaces with your Prometheus deployment to provide detailed, reliable metric data. When you arrive at the UI, at your disposal are two dynamic tools to shape your monitoring experience; the dashboard of prometheus-fed cluster and application metrics, and the kubernetes cluster visualizer.
8 |
9 |
10 |
11 | ## Table of Contents
12 |
13 |
14 |
15 | Built with
16 |
17 |
18 | Features
19 |
20 | Getting Started with Kubernautics
21 | Progress
22 | How to Contribute
23 | Our Team
24 | License
25 |
26 |
27 | ## Built with
28 |
29 |
30 |
31 | [![Kubernetes][Kubernetes-logo]](https://kubernetes.io)
32 | [![Docker][Docker-logo]](https://www.docker.com)
33 | [![Prometheus][Prometheus-logo]](https://prometheus.io)
34 | [![ChartJS][Chartjs-logo]](https://www.chartjs.org)
35 | [![ReactRouter][ReactRouter-logo]](https://reactrouter.com/en/main)
36 | [![Express][Express-logo]](https://expressjs.com)
37 | [![React][React-logo]](https://react.dev)
38 | [![Jest][Jest-logo]](https://jestjs.io)
39 | [![MUI][MUI-logo]](https://mui.com)
40 | [![Webpack][Webpack-logo]](https://webpack.js.org)
41 | [![Helm][Helm-logo]](https://helm.sh)
42 | [![Javascript][Javascript-logo]](https://www.javascript.com)
43 | [![npm][npm-logo]](https://www.npmjs.com)
44 | [![Node.JS][Node-logo]](https://nodejs.org/en)
45 | [![HTML5][HTML-logo]](https://developer.mozilla.org/en-US/docs/Web/HTML)
46 |
47 |
48 |
49 | ## Features
50 |
51 | ### 1. Realtime Monitoring Dashboard
52 |
53 | - Kubernautics compiles all metrics you want to track and constantly pulls information based off of the custom metric scrape rate
54 |
55 | ### 2. Customizable Metrics
56 |
57 | - Kubernautics simplifies the Prometheus scraping tasks and provides additional information to each query task
58 |
59 | 
60 |
61 | ### 3. Cluster Visualization
62 |
63 | - Kubernautics depicts the interdependencies within your cluster but also employs a color-coded system for ease of navigation
64 |
65 | 
66 |
67 | ## Getting Started with Kubernautics
68 | ### Building the Container Image
69 | The Kubernautics container image must currently be built from source. Download a copy of this repo, and once inside run --
70 | ```
71 | docker build . -t kubernautics:latest
72 | ```
73 | -- to yield an image that can be deployed into your cluster.
74 |
75 | ### Deploying to your cluster
76 | Kubernautics currently assumes it can communicate with prometheus at the following address --
77 | ```
78 | http://prometheus-kube-prometheus-prometheus:9090
79 | ```
80 | -- which is the default configuration when Prometheus is deployed via the [Community Helm Chart](https://prometheus-community.github.io/helm-charts). We plan to make this configurable in the future.
81 |
82 | For a reference deployment, you can run `npm run cluster:prod up` from inside the repository. You will need `minikube` and `devspace` installed locally on your machine; see [the setup guide for developers](/DEV_SETUP.md) for more info.
83 |
84 | ## How to Contribute
85 |
86 | 1. Fork the Project
87 | 2. Create your Feature Branch based off of Dev
88 |
89 | - `git checkout -b feature/NewFeature`
90 |
91 | 3. [Setup/enter the development environment](/DEV_SETUP.md)
92 |
93 | 3. Commit your Changes
94 |
95 | - `git commit -m 'What was changed: Description of the NewFeature'`
96 |
97 | 4. Push to the Branch
98 |
99 | - `git push origin feature/NewFeature`
100 |
101 | 5. Open a Pull Request (from `feature/NewFeature` to `dev`)
102 |
103 | - make sure newest dev branch has been merged
104 |
105 | ## Progress
106 |
107 | | Feature | Status |
108 | | --------------------------------------- | ------ |
109 | | Customizable Cluster Visualizer | ⏳ |
110 | | Additional Chart Typing | ⏳ |
111 | | Automate Prometheus Configs/Deployment | ⏳ |
112 | | Increase Test Coverage | 🙏🏻 |
113 | | Reduce Resource Usage | 🙏🏻 |
114 | | Deployment with cloud-hosting providers | 🙏🏻 |
115 |
116 | - ✅ = Ready to use
117 | - ⏳ = In progress
118 | - 🙏🏻 = Looking for contributors
119 |
120 | ## Our Team
121 |
122 |
123 |
124 |
125 |
126 |
127 | Jordan Buranskas
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | Edward Li
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | Matin Schams
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | Tyler Shelton
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | ## License
161 |
162 | By contributing, you agree that your contributions will be licensed under Kubernautics's MIT License.
163 |
164 | back to top
165 |
166 | [Kubernetes-logo]: https://img.shields.io/badge/KUBERNETES-326CE5?style=for-the-badge&logo=KUBERNETES&logoColor=white
167 | [Docker-logo]: https://img.shields.io/badge/DOCKER-2496ED?style=for-the-badge&logo=DOCKER&logoColor=white
168 | [Prometheus-logo]: https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge&logo=Prometheus&color=black
169 | [Chartjs-logo]: https://img.shields.io/badge/Chart.js-FF6384?style=for-the-badge&logo=Chart.js&logoColor=white&color=FF6384
170 | [DevSpace-logo]: https://example.com/path/to/devspace-logo.png
171 | [ReactRouter-logo]: https://img.shields.io/badge/ReactRouter-CA4245?style=for-the-badge&logo=React%20Router&color=rgb(180%2C180%2C180)
172 | [React-logo]: https://img.shields.io/badge/React-61DAFB?style=for-the-badge&logo=React&color=rgb(90%2C90%2C90)
173 | [Jest-logo]: https://img.shields.io/badge/Jest-C21325?style=for-the-badge&logo=Jest
174 | [MUI-logo]: https://img.shields.io/badge/MUI-%23007FFF?style=for-the-badge&logo=MUI&logoColor=white
175 | [Webpack-logo]: https://img.shields.io/badge/Webpack-%238DD6F9?style=for-the-badge&logo=Webpack&logoColor=blue
176 | [Helm-logo]: https://img.shields.io/badge/Helm-0F1689?style=for-the-badge&logo=Helm
177 | [Javascript-logo]: https://img.shields.io/badge/Javascript-F7DF1E?style=for-the-badge&logo=Javascript&color=rgb(45%2C45%2C45)
178 | [Express-logo]: https://img.shields.io/badge/Express-000000?style=for-the-badge&logo=Express
179 | [npm-logo]: https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm
180 | [Node-logo]: https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=Node.js&logoColor=white&color=339933
181 | [HTML-logo]: https://img.shields.io/badge/HTML-E34F26?style=for-the-badge&logo=HTML5&logoColor=white&color=E34F26
182 | [Git-logo]: https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white
183 | [Linkedin-logo]: https://img.shields.io/badge/LinkedIn-%230077B5.svg?logo=linkedin&logoColor=white
184 |
--------------------------------------------------------------------------------
/assets/images/nautics-resize.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Kubernautics/623a34aabd6208049bc4ba00327d28f6ac47fe63/assets/images/nautics-resize.jpg
--------------------------------------------------------------------------------
/assets/images/nautics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Kubernautics/623a34aabd6208049bc4ba00327d28f6ac47fe63/assets/images/nautics.png
--------------------------------------------------------------------------------
/assets/images/ocean.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Kubernautics/623a34aabd6208049bc4ba00327d28f6ac47fe63/assets/images/ocean.jpg
--------------------------------------------------------------------------------
/assets/styles/dashboard.scss:
--------------------------------------------------------------------------------
1 | #dashboard {
2 | padding: 1rem;
3 | display: flex;
4 | align-items: flex-start;
5 | flex-wrap: wrap;
6 | justify-content: space-around;
7 |
8 | //text
9 | text-align: center;
10 | color: white;
11 |
12 | .monitor {
13 | background-color: #fcfcfc;
14 | border-radius: 0.66rem;
15 | color: #141414;
16 | padding: 1rem;
17 | margin: 1rem 1rem 0 0;
18 | max-height: 400px;
19 | min-height: 200px;
20 | max-width: 800px;
21 | min-width: 400px;
22 | flex-grow: 1;
23 | flex-shrink: 1;
24 |
25 | h2 {
26 | margin: 0 0 0.5rem 0;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/assets/styles/styles.scss:
--------------------------------------------------------------------------------
1 | @use 'dashboard';
2 |
3 | html {
4 | //background
5 | background-image: url(../images/ocean.jpg);
6 | background-size: cover;
7 | background-color: #384148;
8 |
9 | //text
10 | color: #fcfcfc;
11 | font-family: Verdana;
12 | font-size: 16px;
13 | text-align: center;
14 | }
15 |
16 | body {
17 | margin: 0;
18 | }
19 |
20 | // this element wraps our entire application; rules here are
21 | // for laying out the highest-level components relative to one
22 | // another (e.g., the navbar relative to the rest of the app below it).
23 | div#application {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | header.navBar {
29 | background: #020101;
30 | color: #fcfcfc;
31 | padding: 1rem;
32 |
33 | display: flex;
34 | justify-content: space-between;
35 |
36 | // main app title
37 | .logo {
38 | width: 68px;
39 | height: 52px;
40 | top: 9px;
41 | }
42 |
43 | .brand {
44 | display: flex;
45 | align-items: center;
46 |
47 | h1 {
48 | font-size: 1.4rem;
49 | margin: 0;
50 | }
51 | }
52 |
53 | nav {
54 | display: flex;
55 | align-items: center;
56 |
57 | // links
58 | li {
59 | display: inline;
60 | margin: 0 1rem;
61 | }
62 |
63 | // linkstyling
64 | a {
65 | color: inherit;
66 | text-decoration: none;
67 |
68 | &:hover {
69 | text-decoration: underline;
70 | }
71 | }
72 | }
73 | }
74 |
75 | #myVideo {
76 | position: fixed;
77 | right: 0;
78 | bottom: 0;
79 | min-width: 100%;
80 | min-height: 100%;
81 | }
82 |
--------------------------------------------------------------------------------
/devspace.yaml:
--------------------------------------------------------------------------------
1 | version: v2beta1
2 | name: kubernautics
3 |
4 | # This is a list of `pipelines` that DevSpace can execute (you can define your own)
5 | pipelines:
6 | # This is the pipeline for the main command: `devspace dev` (or `devspace run-pipeline dev`)
7 | dev:
8 | run: |-
9 | run_dependencies --all # 1. Deploy any projects this project needs (see "dependencies")
10 | ensure_pull_secrets --all # 2. Ensure pull secrets
11 | create_deployments --all # 3. Deploy Helm charts and manifests specfied as "deployments"
12 | start_dev prometheus # 4. Start dev mode "prometheus" (see "dev" section)
13 | start_dev kubernautics # 4. Start dev mode "kubernautics" (see "dev" section)
14 | # You can run this pipeline via `devspace deploy` (or `devspace run-pipeline deploy`)
15 | deploy:
16 | run: |-
17 | run_dependencies --all # 1. Deploy any projects this project needs (see "dependencies")
18 | ensure_pull_secrets --all # 2. Ensure pull secrets
19 | build_images --all -t $(git describe --always) # 3. Build, tag (git commit hash) and push all images (see "images")
20 | create_deployments --all # 4. Deploy Helm charts and manifests specfied as "deployments"
21 |
22 | # This is a list of `images` that DevSpace can build for this project
23 | # We recommend to skip image building during development (devspace dev) as much as possible
24 | images:
25 | kubernautics:
26 | image: kubernautics
27 | dockerfile: ./Dockerfile
28 |
29 | # This is a list of `deployments` that DevSpace can create for this project
30 | deployments:
31 | prometheus:
32 | helm:
33 | chart:
34 | name: kube-prometheus-stack
35 | repo: https://prometheus-community.github.io/helm-charts
36 | demo-app:
37 | # This deployment uses `kubectl` but you can also define `helm` deployments
38 | kubectl:
39 | manifests:
40 | - k8s/demo-app/**
41 | kubernautics:
42 | kubectl:
43 | manifests:
44 | - k8s/kubernautics/**
45 |
46 | # This is a list of `dev` containers that are based on the containers created by your deployments
47 | dev:
48 | prometheus:
49 | imageSelector: quay.io/prometheus/prometheus
50 | ports:
51 | - port: '9090'
52 | kubernautics:
53 | # Search for the container that runs this image
54 | imageSelector: kubernautics:latest
55 | # Replace the container image with this dev-optimized image (allows to skip image building during development)
56 | devImage: ghcr.io/loft-sh/devspace-containers/javascript:18-alpine
57 | resources:
58 | requests:
59 | cpu: 1000m
60 | memory: 1Gi
61 | # Sync files between the local filesystem and the development container
62 | sync:
63 | - path: ./
64 | uploadExcludePaths:
65 | - node_modules
66 | uploadExcludeFile: .dockerignore
67 | # Open a terminal and use the following command to start it
68 | terminal:
69 | command: ./devspace_start.sh
70 | # Inject a lightweight SSH server into the container (so your IDE can connect to the remote dev env)
71 | ssh:
72 | enabled: true
73 | # Make the following commands from my local machine available inside the dev container
74 | proxyCommands:
75 | - command: devspace
76 | - command: kubectl
77 | - command: helm
78 | - command: docker
79 | - command: minikube
80 | - gitCredentials: true
81 | # Forward the following ports to be able access your application via localhost
82 | ports:
83 | - port: '9229'
84 | - port: '3000'
85 | # Open the following URLs once they return an HTTP status code other than 502 or 503
86 | open:
87 | - url: http://localhost:3000
88 |
89 | # Use the `commands` section to define repeatable dev workflows for this project
90 | commands:
91 | migrate-db:
92 | command: |-
93 | echo 'This is a cross-platform, shared command that can be used to codify any kind of dev task.'
94 | echo 'Anyone using this project can invoke it via "devspace run migrate-db"'
95 |
96 | # Define dependencies to other projects with a devspace.yaml
97 | # dependencies:
98 | # api:
99 | # git: https://... # Git-based dependencies
100 | # tag: v1.0.0
101 | # ui:
102 | # path: ./ui # Path-based dependencies (for monorepos)
103 |
--------------------------------------------------------------------------------
/devspace_start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set +e # Continue on errors
3 |
4 | export NODE_ENV=development
5 | if [ -f "yarn.lock" ]; then
6 | echo "Installing Yarn Dependencies"
7 | yarn
8 | else
9 | if [ -f "package.json" ]; then
10 | echo "Installing NPM Dependencies"
11 | npm install
12 | fi
13 | fi
14 |
15 | COLOR_BLUE="\033[0;94m"
16 | COLOR_GREEN="\033[0;92m"
17 | COLOR_RESET="\033[0m"
18 |
19 | # Print useful output for user
20 | echo -e "${COLOR_BLUE}
21 | _ _ _ _
22 | | | | | | | (_)
23 | | | ___ _| |__ ___ _ __ _ __ __ _ _ _| |_ _ ___ ___
24 | | |/ / | | | '_ \ / _ \ '__| '_ \ / _\` | | | | __| |/ __/ __|
25 | | <| |_| | |_) | __/ | | | | | (_| | |_| | |_| | (__\__ \_
26 | |_|\_\\\\__,_|_.__/ \___|_| |_| |_|\__,_|\__,_|\__|_|\___|___(_)
27 | ${COLOR_RESET}"
28 |
29 |
30 | echo -e "Welcome to the Kubernautics development container!
31 |
32 | This is how you can work with it:
33 | - Project files will be synchronized between your local machine and this container
34 | - The kubernautics dashboard is exposed at http://localhost:3000
35 | - Run \`${COLOR_GREEN}npm run dev${COLOR_RESET}\` to start the application
36 | - To exit the container, run \`${COLOR_BLUE}exit${COLOR_RESET}\`
37 | "
38 |
39 | # Set terminal prompt
40 | export PS1="\[${COLOR_BLUE}\]devspace\[${COLOR_RESET}\] ./\W \[${COLOR_BLUE}\]\\$\[${COLOR_RESET}\] "
41 | if [ -z "$BASH" ]; then export PS1="$ "; fi
42 |
43 | # Include project's bin/ folder in PATH
44 | export PATH="./bin:$PATH"
45 |
46 | # Open shell
47 | bash --norc
48 |
--------------------------------------------------------------------------------
/jest.all.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | projects: [
3 | './server/',
4 | './src/',
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/k8s/demo-app/demo-app-mongo-config.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: demo-app-mongo-config
5 | data:
6 | mongo-url: demo-app-mongo-service
7 |
--------------------------------------------------------------------------------
/k8s/demo-app/demo-app-mongo-secret.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: demo-app-mongo-secret
5 | type: Opaque
6 | data:
7 | mongo-user: bW9uZ291c2Vy
8 | mongo-password: bW9uZ29wYXNzd29yZA==
9 |
--------------------------------------------------------------------------------
/k8s/demo-app/demo-app-mongo.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: demo-app-mongo-deployment
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: demo-app-mongo
10 | template:
11 | metadata:
12 | labels:
13 | app: demo-app-mongo
14 | spec:
15 | containers:
16 | - name: mongodb
17 | image: mongo:5.0
18 | resources:
19 | limits:
20 | memory: "128Mi"
21 | cpu: "500m"
22 | ports:
23 | - containerPort: 27017
24 | env:
25 | - name: MONGO_INITDB_ROOT_USERNAME
26 | valueFrom:
27 | secretKeyRef:
28 | name: demo-app-mongo-secret
29 | key: mongo-user
30 | - name: MONGO_INITDB_ROOT_PASSWORD
31 | valueFrom:
32 | secretKeyRef:
33 | name: demo-app-mongo-secret
34 | key: mongo-password
35 | ---
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: demo-app-mongo-service
40 | spec:
41 | selector:
42 | app: demo-app-mongo
43 | ports:
44 | - port: 27017
45 | targetPort: 27017
46 |
--------------------------------------------------------------------------------
/k8s/demo-app/demo-app.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: demo-app-deployment
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: demo-app
10 | template:
11 | metadata:
12 | labels:
13 | app: demo-app
14 | spec:
15 | containers:
16 | - name: demo-app
17 | image: nanajanashia/k8s-demo-app:v1.0
18 | resources:
19 | limits:
20 | memory: "128Mi"
21 | cpu: "500m"
22 | ports:
23 | - containerPort: 3000
24 | env:
25 | - name: USER_NAME
26 | valueFrom:
27 | secretKeyRef:
28 | name: demo-app-mongo-secret
29 | key: mongo-user
30 | - name: USER_PWD
31 | valueFrom:
32 | secretKeyRef:
33 | name: demo-app-mongo-secret
34 | key: mongo-password
35 | - name: DB_URL
36 | valueFrom:
37 | configMapKeyRef:
38 | name: demo-app-mongo-config
39 | key: mongo-url
40 | ---
41 | apiVersion: v1
42 | kind: Service
43 | metadata:
44 | name: demo-app-service
45 | spec:
46 | type: NodePort
47 | selector:
48 | app: demo-app
49 | ports:
50 | - port: 3000
51 | targetPort: 3000
52 | # nodePort here will expose a static port on each node in the cluster
53 | # to faciliate incoming traffic to the service from external sources.
54 | # this port must be in the range 30000-32767.
55 | nodePort: 30000
56 |
--------------------------------------------------------------------------------
/k8s/kubernautics/cluster-role-binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: list-resources-binding
5 | subjects:
6 | - kind: ServiceAccount
7 | name: default
8 | namespace: kubernautics
9 | roleRef:
10 | kind: ClusterRole
11 | name: list-resources
12 | apiGroup: rbac.authorization.k8s.io
13 |
--------------------------------------------------------------------------------
/k8s/kubernautics/cluster-role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: list-resources
5 | rules:
6 | - apiGroups: [""]
7 | resources: ["pods", "services", "nodes", "deployments"]
8 | verbs: ["list"]
9 |
--------------------------------------------------------------------------------
/k8s/kubernautics/kubernautics.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: kubernautics-deployment
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: kubernautics
10 | template:
11 | metadata:
12 | labels:
13 | app: kubernautics
14 | spec:
15 | containers:
16 | - name: kubernautics
17 | image: kubernautics:latest
18 | resources:
19 | limits:
20 | memory: '1024Mi'
21 | cpu: '1000m'
22 | ports:
23 | - containerPort: 3000
24 | ---
25 | apiVersion: v1
26 | kind: Service
27 | metadata:
28 | name: kubernautics-service
29 | spec:
30 | type: NodePort
31 | selector:
32 | app: kubernautics
33 | ports:
34 | - port: 3030
35 | targetPort: 3000
36 | nodePort: 30100
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kubernautics",
3 | "version": "1.0.0-beta",
4 | "description": "Kubernautics",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "NODE_ENV=production webpack",
8 | "cluster": "./scripts/dev_cluster.sh",
9 | "cluster:prod": "./scripts/prod_cluster.sh",
10 | "dev": "NODE_ENV=development EXPRESS_PORT=3010 concurrently 'nodemon server/index.js' 'webpack-dev-server --open'",
11 | "start": "NODE_ENV=production node server/index.js",
12 | "test": "jest --verbose -c ./jest.all.config.js"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/oslabs-beta/kubernautics.git"
17 | },
18 | "author": "Tyler Shelton, Matin Schams, Jordan Buranskas, Edward Li",
19 | "license": "ISC",
20 | "bugs": {
21 | "url": "https://github.com/oslabs-beta/kubernautics/issues"
22 | },
23 | "homepage": "https://github.com/oslabs-beta/kubernautics#readme",
24 | "dependencies": {
25 | "@emotion/react": "^11.11.1",
26 | "@emotion/styled": "^11.11.0",
27 | "@kubernetes/client-node": "^0.20.0",
28 | "axios": "^1.6.1",
29 | "chart.js": "^4.4.0",
30 | "chartjs": "^0.3.24",
31 | "chartjs-adapter-date-fns": "^3.0.0",
32 | "cors": "^2.8.5",
33 | "date-fns": "^2.30.0",
34 | "express": "^4.18.2",
35 | "prom-client": "^15.0.0",
36 | "react": "^18.2.0",
37 | "react-chartjs-2": "^5.2.0",
38 | "react-dom": "^18.2.0",
39 | "react-graph-vis": "^1.0.7",
40 | "react-router-dom": "^6.20.0"
41 | },
42 | "devDependencies": {
43 | "@babel/core": "^7.23.3",
44 | "@babel/preset-env": "^7.23.3",
45 | "@babel/preset-react": "^7.23.3",
46 | "@emotion/react": "^11.11.1",
47 | "@emotion/styled": "^11.11.0",
48 | "@mui/material": "^5.14.18",
49 | "@mui/system": "^5.14.18",
50 | "@testing-library/jest-dom": "^6.1.4",
51 | "@testing-library/react": "^14.1.2",
52 | "@testing-library/user-event": "^14.5.1",
53 | "babel-jest": "^29.7.0",
54 | "babel-loader": "^9.1.3",
55 | "concurrently": "^8.2.2",
56 | "css-loader": "^6.8.1",
57 | "eslint": "^8.53.0",
58 | "file-loader": "^6.2.0",
59 | "html-webpack-plugin": "^5.5.3",
60 | "jest": "^29.7.0",
61 | "jest-environment-jsdom": "^29.7.0",
62 | "nodemon": "^3.0.1",
63 | "react-test-renderer": "^18.2.0",
64 | "regenerator-runtime": "^0.14.0",
65 | "sass": "^1.69.5",
66 | "sass-loader": "^13.3.2",
67 | "style-loader": "^3.3.3",
68 | "supertest": "^6.3.3",
69 | "webpack": "^5.89.0",
70 | "webpack-cli": "^5.1.4",
71 | "webpack-dev-server": "^4.15.1"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/scripts/dev_cluster.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # dev_cluster.sh -- bring the dev environment for the project up or down
4 | #
5 | # This script is responsible for taking any steps necessary to set up the
6 | # development environment for the project, and tear down that environment
7 | # when the developer is done working.
8 | #
9 | # Running this script in "down" mode should remove any modifications and deployments
10 | # made to the cluster related to this project, but not delete the minikube
11 | # cluster completely (in case it's being used for other purposes on this
12 | # machine as well). It will, however, shut the minikube cluster down to free
13 | # up those resources.
14 | #
15 | # The script presumes minikube and devspace are is installed on the machine,
16 | # and will fail if those programs can't be found.
17 |
18 | MINIKUBE_PROFILE=kubernautics-dev
19 |
20 | profile_exists() {
21 | profile_match=$(minikube profile list | cut -d "|" -f 2 | grep -E "^\s$MINIKUBE_PROFILE\s$" | wc -l)
22 | if [ $profile_match -eq 0 ]; then
23 | return 1 # profile doesn't exist
24 | else
25 | return 0 # profile exists
26 | fi
27 | }
28 |
29 | cluster_is_running() {
30 | minikube status > /dev/null
31 | return $?
32 | }
33 |
34 | # check that required input is present
35 | if [ "$1" != "up" ] && [ "$1" != "down" ]; then
36 | # provide usage guidance to the user and exit
37 | # with an error code
38 | echo "Usage: dev_cluster.sh (up|down)"
39 | exit 1
40 | fi
41 |
42 | # confirm minikube is installed
43 | if ! command -v minikube &> /dev/null; then
44 | echo "Error: Could not locate minikube on the system. Is it installed?"
45 | exit 1
46 | fi
47 |
48 | # confirm devspace is installed
49 | if ! command -v devspace &> /dev/null; then
50 | echo "Error: Could not locate devspace on the system. Is it installed?"
51 | echo "devspace can be installed by running:"
52 | echo " npm install -g devspace"
53 | exit 1
54 | fi
55 |
56 | # bring cluster environment up or down
57 | if [ $1 == 'up' ]; then
58 | # check if project's minikube profile exists
59 | if profile_exists; then
60 | echo "==> Switching the active minikube profile to '$MINIKUBE_PROFILE'..."
61 | minikube profile $MINIKUBE_PROFILE
62 | else
63 | echo "==> minikube profile '$MINIKUBE_PROFILE' doesn't yet exist. Will create."
64 | fi
65 |
66 | # start the minikube cluster if it's not already running
67 | if ! cluster_is_running; then
68 | echo "==> Starting up the minikube cluster..."
69 | minikube start -p $MINIKUBE_PROFILE
70 | fi
71 |
72 | # start up the devspace dev environment in a project-specific k8s namespace
73 | devspace use namespace kubernautics
74 | devspace dev --kube-context $MINIKUBE_PROFILE
75 | elif [ $1 == 'down' ]; then
76 | # ensure the project's cluster is still present
77 | if profile_exists; then
78 | echo "==> Ensuring $MINIKUBE_PROFILE is the active minikube profile..."
79 | minikube profile $MINIKUBE_PROFILE
80 | if cluster_is_running; then
81 | echo "==> Shutting down the minikube cluster..."
82 | minikube stop -p $MINIKUBE_PROFILE
83 | fi
84 | else
85 | echo "==> The $MINIKUBE_PROFILE cluster seems to not exist. If it"
86 | echo " is still running, you may need to shut it down manually."
87 | fi
88 | fi
89 |
90 | # no errors; exit cleanly
91 | exit 0
92 |
--------------------------------------------------------------------------------
/scripts/prod_cluster.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # check that required input is present
4 | if [ "$1" != "up" ] && [ "$1" != "down" ]; then
5 | # provide usage guidance to the user and exit
6 | # with an error code
7 | echo "Usage: prod_cluster.sh (up|down)"
8 | exit 1
9 | fi
10 |
11 | # confirm minikube is installed
12 | if ! command -v minikube &> /dev/null; then
13 | echo "Error: Could not locate minikube on the system. Is it installed?"
14 | exit 1
15 | fi
16 |
17 | # confirm devspace is installed
18 | if ! command -v devspace &> /dev/null; then
19 | echo "Error: Could not locate devspace on the system. Is it installed?"
20 | echo "devspace can be installed by running:"
21 | echo " npm install -g devspace"
22 | exit 1
23 | fi
24 |
25 | # bring cluster environment up or down
26 | if [ $1 == 'up' ]; then
27 | # start/restart
28 | minikube delete -p kubernautics-prod
29 | minikube start -p kubernautics-prod
30 | minikube profile kubernautics-prod
31 | devspace cleanup images
32 | rm -rf dist/
33 | npm run build
34 | devspace use namespace kubernautics
35 | devspace deploy -b --kube-context kubernautics-prod
36 | minikube service kubernautics-service -n kubernautics
37 | elif [ $1 == 'down' ]; then
38 | # stop
39 | minikube delete -p kubernautics-prod
40 | fi
41 |
42 | exit 0
43 |
--------------------------------------------------------------------------------
/server/__tests__/server.test.js:
--------------------------------------------------------------------------------
1 | const request = require('supertest');
2 | const server = require('../server');
3 |
4 | describe("POST /api/pull", () => {
5 | describe("Given a promQL query string,", () => {
6 | test("should respond with a status code of 200", async () => {
7 | const body = 'query=container_cpu_usage_seconds_total{namespace="default"}[20m]'
8 | const response = await request(server).post('/api/pull').send(body)
9 | expect(response.statusCode).toBe(200);
10 | });
11 | });
12 | describe("In the absence of a query string,", () => {
13 | test("The server should return status 404", async () => {
14 | const body = '';
15 | const response = await request(server).post('/api/pull').send(body);
16 | expect(response.statusCode).toBe(404);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/server/controllers/clusterController.js:
--------------------------------------------------------------------------------
1 | const k8s = require('@kubernetes/client-node');
2 | const clusterController = {};
3 |
4 | const kc = new k8s.KubeConfig();
5 | kc.loadFromDefault();
6 |
7 | const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
8 |
9 | // Fetch all Pods within the kubernetes cluster
10 | clusterController.getPods = async (req, res, next) => {
11 | try {
12 | const podsRes = await k8sApi.listPodForAllNamespaces();
13 | const pods = podsRes.body.items;
14 | res.locals.pods = pods;
15 | return next();
16 | } catch (err) {
17 | return next({
18 | log: `Error found in clusterController.getPods ${err}`,
19 | status: 500,
20 | message: { err: 'An error occurred' },
21 | });
22 | }
23 | };
24 |
25 | // Fetch all Nodes within the kubernetes cluster
26 | clusterController.getNodes = async (req, res, next) => {
27 | try {
28 | const nodesRes = await k8sApi.listNode();
29 | const nodes = nodesRes.body.items;
30 | res.locals.nodes = nodes;
31 | return next();
32 | } catch (err) {
33 | return next({
34 | log: `Error found in clusterController.getNodes ${err}`,
35 | status: 500,
36 | message: { err: 'An error occurred' },
37 | });
38 | }
39 | };
40 |
41 | // Fetch all Services within the kubernetes cluster
42 | clusterController.getServices = async (req, res, next) => {
43 | try {
44 | const servicesResponse = await k8sApi.listServiceForAllNamespaces();
45 | if (!servicesResponse.body) {
46 | console.error('No response body found in services response:', servicesResponse);
47 | res.locals.services = [];
48 | return next();
49 | }
50 | const services = servicesResponse.body.items;
51 | res.locals.services = services;
52 | return next();
53 | } catch (err) {
54 | return next({
55 | log: `Error found in clusterController.getServices ${err}`,
56 | status: 500,
57 | message: { err: 'An error occurred' },
58 | });
59 | }
60 | };
61 |
62 | module.exports = clusterController;
63 |
--------------------------------------------------------------------------------
/server/controllers/dataController.js:
--------------------------------------------------------------------------------
1 | const dataController = {};
2 |
3 | // Submit a query to Prometheus
4 | dataController.getData = async (req, res, next) => {
5 | try {
6 | const response = await fetch(
7 | 'http://prometheus-kube-prometheus-prometheus:9090/api/v1/query',
8 | {
9 | method: 'POST',
10 | headers: {
11 | 'Content-Type': 'application/x-www-form-urlencoded',
12 | },
13 | body: `query=${req.body.query}`,
14 | }
15 | );
16 | res.locals.metric = await response.json();
17 | return next();
18 | } catch (err) {
19 | return next({
20 | log: `Error found in dataController.getData ${err}`,
21 | status: 500,
22 | message: { err: 'An error occurred' },
23 | });
24 | }
25 | };
26 |
27 | module.exports = dataController;
28 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const app = require('./server');
2 | const PORT = process.env.EXPRESS_PORT || 3000;
3 |
4 | // Server Listening Port
5 | app.listen(PORT, () => {
6 | console.log(`Kubernautics listening on http://localhost:${PORT}`);
7 | });
8 |
--------------------------------------------------------------------------------
/server/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 | };
4 |
--------------------------------------------------------------------------------
/server/routers/clusterRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const clusterRouter = express.Router();
3 | const clusterController = require('../controllers/clusterController')
4 |
5 | clusterRouter.get('/nodes', clusterController.getNodes, (req, res) => {
6 | return res.status(200).json(res.locals.nodes);
7 | });
8 |
9 | clusterRouter.get('/pods', clusterController.getPods, (req, res) => {
10 | return res.status(200).json(res.locals.pods);
11 | });
12 |
13 | clusterRouter.get('/services', clusterController.getServices, (req, res) => {
14 | return res.status(200).json(res.locals.services);
15 | });
16 |
17 | module.exports = clusterRouter;
--------------------------------------------------------------------------------
/server/routers/dataRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const dataRouter = express.Router();
3 | const dataController = require('../controllers/dataController')
4 |
5 | dataRouter.post('/', dataController.getData, (req, res) => {
6 | res.status(200).json(res.locals.metric);
7 | });
8 |
9 | module.exports = dataRouter;
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const cors = require('cors');
4 | const dataRoutes = require('./routers/dataRoutes');
5 | const clusterRoutes = require('./routers/clusterRoutes');
6 |
7 | const app = express();
8 |
9 | app.use(cors());
10 | app.use(express.json());
11 | app.use(express.static(path.join(__dirname, '../dist')));
12 |
13 | // Route Handlers
14 | app.use('/api/pull', dataRoutes);
15 |
16 | app.use('/api/clusterMap', clusterRoutes);
17 |
18 | app.get('/', (req, res) => {
19 | res.status(200).sendFile('src/components/pages/Dashboard/index.html');
20 | });
21 |
22 | // Unknown Route Handler
23 | app.use((req, res) => {
24 | return res.status(404).send('Page not found');
25 | });
26 |
27 | // Global Error Handler
28 | app.use((err, req, res, next) => {
29 | const globalError = {
30 | log: 'Global Error Found',
31 | status: 500,
32 | message: { err: 'An error occurred' },
33 | };
34 | const errorObj = Object.assign({}, globalError, err);
35 | console.log(errorObj.log);
36 | return res.status(errorObj.status).json(errorObj.message);
37 | });
38 |
39 | module.exports = app;
40 |
--------------------------------------------------------------------------------
/src/__tests__/ClearCharts.test.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 |
4 | import ClearChart from '../components/ClearCharts';
5 |
6 | describe ('ClearChart', () => {
7 | test('Button should be labelled correctly', () => {
8 | render( );
9 | expect(screen.getByText("Reset to Defaults")).toBeInTheDocument();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/__tests__/CustomChart.test.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render } from '@testing-library/react';
3 |
4 | import { CustomChart } from '../components/CustomChart';
5 |
6 | describe ('CustomChart', () => {
7 | test('Should provide a "Reset" button', () => {
8 | const { getByRole } = render( );
9 | expect(getByRole('button', { name: 'Reset to Defaults'})).toBeInTheDocument();
10 | });
11 | test('Should provide an "Add Metric" button', () => {
12 | const { getByRole } = render( );
13 | expect(getByRole('button', { name: 'Add Metric'})).toBeInTheDocument();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/__tests__/NavBar.test.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { MemoryRouter } from 'react-router-dom';
3 | import { render, screen } from '@testing-library/react';
4 |
5 | import NavBar from '../components/NavBar';
6 |
7 | describe ('NavBar', () => {
8 | test('Renders correctly', () => {
9 | render(
10 | // MemoryRouter is used here to stand in for the actual (React-)Router component
11 | // used in the app. Some documentation on how to test with/around React Router
12 | // can be found here --
13 | // https://testing-library.com/docs/example-react-router/ (we use react-router v6)
14 | //
15 | // These were also helpful to a point, but do note the URL suggests they're for
16 | // react-router v5. --
17 | // https://v5.reactrouter.com/web/guides/testing
18 |
19 |
20 |
21 | );
22 | expect(screen.getByText("Kubernautics")).toBeInTheDocument();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import NavBar from './NavBar';
3 | import Dashboard from './Dashboard';
4 | import { BrowserRouter, Router, Route, Link, Routes } from 'react-router-dom';
5 | import { CustomChart } from './CustomChart';
6 | import { ClusterVisualizer } from './ClusterVisualizer';
7 |
8 | function App() {
9 | return (
10 |
11 |
12 |
13 |
14 | } />
15 | } />
16 | } />
17 |
18 |
19 |
20 | );
21 | }
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/src/components/ClearCharts.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Button } from '@mui/material';
3 |
4 | // Clear any additional rendered charts with a "Reset to Defaults" button click
5 | const ClearChart = () => {
6 | return (
7 | {
11 | localStorage.clear();
12 | location.reload();
13 | }}
14 | >
15 | Reset to Defaults
16 |
17 | );
18 | };
19 |
20 | export default ClearChart;
21 |
--------------------------------------------------------------------------------
/src/components/ClusterVisualizer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Graph from 'react-graph-vis';
3 |
4 | export const ClusterVisualizer = () => {
5 | const [nodes, setNodes] = useState([]);
6 | const [pods, setPods] = useState([]);
7 | const [services, setServices] = useState([]);
8 | const [shortenName, setShortenName] = useState(true);
9 |
10 | useEffect(() => {
11 | // Fetch all Pods, Services, and Nodes
12 | const fetchData = async () => {
13 | try {
14 | const podsResponse = await fetch('/api/clusterMap/pods');
15 | const servicesResponse = await fetch('/api/clusterMap/services');
16 | const nodesResponse = await fetch('/api/clusterMap/nodes');
17 | const podsData = await podsResponse.json();
18 | const servicesData = await servicesResponse.json();
19 | const nodesData = await nodesResponse.json();
20 | setPods(podsData);
21 | setServices(servicesData);
22 | setNodes(nodesData);
23 | } catch (error) {
24 | console.error('Failed fetching data');
25 | }
26 | };
27 | fetchData();
28 | }, []);
29 |
30 | // Store the fetched data in the below arrays to create connection links between the elements
31 | const createGraph = () => {
32 | const createdNodes = [];
33 | const createdEdges = [];
34 |
35 | nodes.forEach((node, index) => {
36 | const nodeId = `node-${index}`;
37 | createdNodes.push({
38 | id: nodeId,
39 | label: node.metadata.name,
40 | shape: 'circle',
41 | color: 'grey',
42 | });
43 |
44 | services.forEach((service, serviceIndex) => {
45 | const serviceNodeId = `service-${serviceIndex}`;
46 | createdNodes.push({
47 | id: serviceNodeId,
48 | label: shortenName
49 | ? service.metadata.name.slice(0, 10)
50 | : service.metadata.name,
51 | shape: 'box',
52 | color: '#6FB1FC',
53 | });
54 | createdEdges.push({
55 | from: nodeId,
56 | to: serviceNodeId,
57 | id: `edge-service-${serviceIndex}`,
58 | });
59 |
60 | pods.forEach((pod, podIndex) => {
61 | if (pod.metadata.name.includes(service.metadata.name.toLowerCase())) {
62 | const podNodeId = `pod-${podIndex}`;
63 | createdNodes.push({
64 | id: podNodeId,
65 | label: shortenName
66 | ? pod.metadata.name.slice(0, 10)
67 | : pod.metadata.name,
68 | shape: 'box',
69 | color: 'white',
70 | });
71 | createdEdges.push({
72 | from: serviceNodeId,
73 | to: podNodeId,
74 | id: `edge-${podIndex}`,
75 | });
76 | }
77 | });
78 | });
79 | });
80 |
81 | return { nodes: createdNodes, edges: createdEdges };
82 | };
83 |
84 | const graph = createGraph();
85 |
86 | // Interactivity and visuals of the network map
87 | const options = {
88 | layout: {
89 | randomSeed: 10,
90 | },
91 | height: '1000px',
92 | physics: {
93 | barnesHut: {
94 | gravitationalConstant: -1000,
95 | centralGravity: 0,
96 | springLength: 150,
97 | springConstant: 0.003,
98 | damping: 0.09,
99 | },
100 | },
101 | edges: {
102 | color: 'white',
103 | },
104 | };
105 |
106 | // Shorten the name of the element based on click toggle
107 | const events = {
108 | select: function (event) {
109 | setShortenName(!shortenName);
110 | console.log('You selected me');
111 | },
112 | };
113 |
114 | return (
115 |
121 | );
122 | };
123 |
--------------------------------------------------------------------------------
/src/components/CustomChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | Container,
4 | FormControl,
5 | InputLabel,
6 | Select,
7 | MenuItem,
8 | Button,
9 | Grid,
10 | Dialog,
11 | DialogTitle,
12 | DialogContent,
13 | DialogActions,
14 | } from '@mui/material';
15 | import { styled } from '@mui/system';
16 | import actions from './action';
17 | import ClearChart from './ClearCharts';
18 |
19 | const DropDownMenu = (props) => {
20 | const [buttonStatus, setButtonStatus] = useState(true);
21 | const [selectedTask, setSelectedTask] = useState('');
22 | const [selectedTimeRange, setSelectedTimeRange] = useState('');
23 | const [selectedStepSize, setSelectedStepSize] = useState();
24 | const [isFormVisible, setFormVisibility] = useState(false);
25 |
26 | const classes = styled((theme) => ({
27 | customDialog: {
28 | width: '1500px',
29 | maxHeight: '350px',
30 | padding: theme.spacing(2),
31 | },
32 | customMenu: {
33 | width: '1400px',
34 | maxHeight: '350px',
35 | padding: theme.spacing(2),
36 | },
37 | }));
38 | const actionTasks = Object.keys(actions.data);
39 | // Provide the possible PromQL metric look ups, types, and help descriptions
40 | const ddActionTasks = [];
41 | for (let i = 0; i < actionTasks.length; i++) {
42 | ddActionTasks.push(
43 |
44 |
45 |
46 | {actionTasks[i]}
47 |
48 |
49 | {actions.data[actionTasks[i]][0].type}
50 |
51 |
52 | {actions.data[actionTasks[i]][0].help}
53 |
54 |
55 |
56 | );
57 | }
58 |
59 | // Options for collection time in dropdown
60 | const timeranges = ['1m', '5m', '10m', '30m', '1h', '2h', '4h', '8h'];
61 | // Options for data step intervals in dropdown
62 | const stepsizes = [1, 5, 10, 30, 60, 120, 240, 480];
63 |
64 | const ddTimeRanges = timeranges.map((range) => (
65 |
66 | {range}
67 |
68 | ));
69 |
70 | const ddStepSizes = stepsizes.map((size) => (
71 |
72 | {size}
73 |
74 | ));
75 |
76 | // Form control has a scroll bar incase too many options are listed
77 | return (
78 |
79 |
setFormVisibility(true)}>
80 | Add Metric
81 |
82 |
83 |
setFormVisibility(false)}
86 | PaperProps={{
87 | className: classes.customDialog,
88 | }}
89 | >
90 | Add Metric
91 |
92 |
93 | {
94 |
95 |
96 | Select a Query
97 | {
101 | setButtonStatus(false);
102 | const taskname = e.target.value;
103 | setSelectedTask(taskname);
104 | }}
105 | label='Select an option'
106 | MenuProps={{
107 | PaperProps: {
108 | style: {
109 | maxHeight: 280,
110 | },
111 | className: classes.customMenu,
112 | },
113 | }}
114 | >
115 | {ddActionTasks}
116 |
117 |
118 |
119 |
120 | Set a time range
121 | {
125 | const timeduration = e.target.value;
126 | setSelectedTimeRange(timeduration);
127 | }}
128 | label='Select an option'
129 | >
130 | {ddTimeRanges}
131 |
132 |
133 |
134 | Set a step size (seconds)
135 | {
139 | const stepsizing = e.target.value;
140 | setSelectedStepSize(stepsizing);
141 | }}
142 | label='Select an option'
143 | >
144 | {ddStepSizes}
145 |
146 |
147 |
148 | }
149 |
150 |
151 |
152 | setFormVisibility(false)}>Cancel
153 |
154 | {
158 | props.onSaveToLocalStorage(
159 | selectedTask,
160 | selectedTimeRange,
161 | selectedStepSize
162 | );
163 | setFormVisibility(false);
164 | }}
165 | >
166 | Create Chart
167 |
168 |
169 |
170 |
171 | );
172 | };
173 |
174 | export const CustomChart = (props) => {
175 | return (
176 |
180 | );
181 | };
182 |
--------------------------------------------------------------------------------
/src/components/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import MonitoringComponent from './MonitoringComponent';
3 | import { CustomChart } from './CustomChart';
4 |
5 | // Initial chart rendered on Homepage load
6 | const Dashboard = (props) => {
7 | const defaultCharts = [
8 | {
9 | query: 'container_cpu_usage_seconds_total',
10 | range: '20m',
11 | stepSize: 80,
12 | },
13 | ];
14 | // Verify or set a storage object with session key
15 | const [charts, setCharts] = useState(() => {
16 | const storedData = JSON.parse(localStorage.getItem('session'));
17 | if (storedData) {
18 | return storedData;
19 | } else {
20 | const newData = [...defaultCharts];
21 | localStorage.setItem('session', JSON.stringify(newData));
22 | return newData;
23 | }
24 | });
25 | // Update existing localStorage
26 | const SaveDataToLocalStorage = (query, range, stepSize) => {
27 | const existingData = JSON.parse(localStorage.getItem('session')) || [];
28 | console.log('existingData', existingData);
29 | const newData = [
30 | ...existingData,
31 | { query: query, range: range, stepSize: stepSize },
32 | ];
33 | // Rewrite the localstorage
34 | localStorage.setItem('session', JSON.stringify(newData));
35 | setCharts(JSON.parse(localStorage.getItem('session')));
36 | };
37 |
38 | return (
39 |
40 |
41 |
42 |
43 | {charts.map((chart, i) => {
44 | return (
45 |
51 | );
52 | })}
53 |
54 |
55 | );
56 | };
57 |
58 | export default Dashboard;
59 |
--------------------------------------------------------------------------------
/src/components/MonitoringComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Line } from 'react-chartjs-2';
3 | import Chart from 'chart.js/auto'; // -> automatic module import based on the file
4 | import { enUS } from 'date-fns/locale';
5 | import 'chartjs-adapter-date-fns';
6 |
7 | const MonitoringComponent = ({ query, range, stepSize }) => {
8 | const [lineData, setLineData] = useState();
9 | const [options, setOptions] = useState();
10 | const [title, setTitle] = useState();
11 |
12 | // Render a monitoring chart based on selected query
13 | const fetchData = async () => {
14 | try {
15 | const rangeStmt = range
16 | ? `[${range}${stepSize ? ':' + stepSize + 's' : ''}]`
17 | : '';
18 | console.log(rangeStmt);
19 | const response = await fetch('/api/pull', {
20 | method: 'POST',
21 | headers: {
22 | Accept: 'application/json',
23 | 'Content-Type': 'application/json',
24 | },
25 | body: JSON.stringify({
26 | query: `${query}${rangeStmt}`,
27 | }),
28 | });
29 | const result = await response.json();
30 | console.log(result);
31 | // Curated list of pre-defined colors
32 | const colorCodes = [
33 | [255, 0, 0],
34 | [0, 255, 0],
35 | [0, 0, 255],
36 | [255, 255, 0],
37 | [255, 0, 255],
38 | [0, 255, 255],
39 | [128, 0, 0],
40 | [0, 128, 0],
41 | [0, 0, 128],
42 | [128, 128, 0],
43 | [128, 0, 128],
44 | [0, 128, 128],
45 | [64, 0, 0],
46 | [0, 64, 0],
47 | [0, 0, 64],
48 | [64, 64, 0],
49 | [64, 0, 64],
50 | [0, 64, 64],
51 | ];
52 | setTitle(result.data.result[0].metric.__name__.replaceAll('_', ' '));
53 | let labels = [];
54 | const datasets = result.data.result.reduce((res, dataset, i) => {
55 | const colorIndex = i;
56 | res.push({
57 | label: dataset.metric.pod,
58 | borderColor: `rgba(${colorCodes[colorIndex].join(',')})`,
59 | data: dataset.values.map((val) => {
60 | const timestamp = val[0] * 1000;
61 | labels.push(timestamp);
62 | return [timestamp, val[1]];
63 | }),
64 | });
65 | return res;
66 | }, []);
67 |
68 | const lineData = {
69 | labels: labels,
70 | datasets: datasets,
71 | };
72 |
73 | // Style configuration for the rendered chart
74 | const options = {
75 | scales: {
76 | x: {
77 | type: 'time',
78 | time: {
79 | unit: 'minute',
80 | displayFormats: {
81 | minute: 'k:mm',
82 | },
83 | },
84 | adapters: {
85 | date: {
86 | locale: enUS,
87 | },
88 | },
89 | title: {
90 | display: true,
91 | text: 'Time (mm:ss)',
92 | },
93 | },
94 | y: {
95 | type: 'linear',
96 | beginAtZero: true,
97 | },
98 | },
99 | animation: true,
100 | plugins: {
101 | legend: {
102 | display: true,
103 | labels: {
104 | boxWidth: 5,
105 | boxHeight: 5,
106 | fontSize: 4,
107 | itemSpacing: 2,
108 | },
109 | },
110 | },
111 | elements: {
112 | backgroundColor: 'rgba(50, 50, 200, 80)',
113 | point: {
114 | radius: 5,
115 | hoverRadius: 2,
116 | },
117 | },
118 | };
119 | setLineData(lineData);
120 | setOptions(options);
121 | } catch (err) {
122 | return console.error('Error fetching data', err);
123 | }
124 | };
125 |
126 | useEffect(() => {
127 | fetchData();
128 | // Set up an interval to fetch data every 30 seconds
129 | const intervalId = setInterval(() => {
130 | fetchData();
131 | }, 1000 * (stepSize + 1 || 15));
132 | return () => clearInterval(intervalId);
133 | }, []);
134 |
135 | // Conditional rendering of lineData and options
136 | return (
137 |
138 |
{title}
139 | {lineData && options && }
140 |
141 | );
142 | };
143 |
144 | export default MonitoringComponent;
145 |
--------------------------------------------------------------------------------
/src/components/NavBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import nautics from '../../assets/images/nautics';
4 |
5 | // Navigation Bar at the top of our Homepage
6 | const NavBar = () => {
7 | return (
8 |
9 |
10 |
11 |
Kubernautics
12 |
13 |
14 |
15 |
16 |
17 | Home
18 |
19 |
20 | Visualizer
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default NavBar;
29 |
--------------------------------------------------------------------------------
/src/components/action.js:
--------------------------------------------------------------------------------
1 | //https://promlabs.com/blog/2020/12/17/promql-queries-for-exploring-your-metrics/
2 | //Promlab offers a type of semantic based meaning behind the metrics but this information is not directly accessible via PromQL
3 |
4 | const actions = {
5 | data: {
6 | '': [
7 | {
8 | type: '',
9 | help: '',
10 | unit: '',
11 | },
12 | ],
13 | cadvisor_version_info: [
14 | {
15 | type: 'gauge',
16 | help: "A metric with a constant '1' value labeled by kernel version, OS version, docker version, cadvisor version \u0026 cadvisor revision.",
17 | unit: '',
18 | },
19 | ],
20 | container_scrape_error: [
21 | {
22 | type: 'gauge',
23 | help: '1 if there was an error while getting container metrics, 0 otherwise',
24 | unit: '',
25 | },
26 | ],
27 | container_cpu_usage_seconds_total: [
28 | {
29 | type: 'counter',
30 | help: 'Cumulative cpu time consumed in seconds.',
31 | unit: '',
32 | },
33 | ],
34 | container_cpu_cfs_periods_total: [
35 | {
36 | type: 'counter',
37 | help: 'Number of elapsed enforcement period intervals.',
38 | unit: '',
39 | },
40 | ],
41 | container_fs_reads_bytes_total: [
42 | { type: 'counter', help: 'Cumulative count of bytes read' },
43 | ],
44 | container_fs_reads_total: [
45 | { type: 'counter', help: 'Cumulative count of reads completed' },
46 | ],
47 | container_fs_writes_bytes_total: [
48 | { type: 'counter', help: 'Cumulative count of bytes written' },
49 | ],
50 | container_fs_writes_total: [
51 | { type: 'counter', help: 'Cumulative count of writes completed' },
52 | ],
53 | container_last_seen: [
54 | { type: 'gauge', help: 'Last time a container was seen by the exporter' },
55 | ],
56 | container_memory_cache: [
57 | { type: 'gauge', help: 'Number of bytes of page cache memory' },
58 | ],
59 | container_memory_failcnt: [
60 | { type: 'gauge', help: 'Number of memory usage hit limits' },
61 | ],
62 | container_memory_failures_total: [
63 | { type: 'gauge', help: 'Cumulative count of memory allocation failures' },
64 | ],
65 | container_memory_max_usage_bytes: [
66 | { type: 'gauge', help: 'Maximum memory usage usage recorded in bytes' },
67 | ],
68 | container_memory_rss: [{ type: 'gauge', help: 'Size of RSS in bytes' }],
69 | container_memory_usage_bytes: [
70 | {
71 | type: 'gauge',
72 | help: 'Current memory usage in bytes, including all memory regardless of when it was accessed',
73 | },
74 | ],
75 | container_memory_working_set_bytes: [
76 | { type: 'gauge', help: 'Current working set in bytes' },
77 | ],
78 |
79 | go_gc_duration_seconds: [
80 | {
81 | type: 'summary',
82 | help: 'A summary of the pause duration of garbage collection cycles.',
83 | unit: '',
84 | },
85 | {
86 | type: 'summary',
87 | help: 'A summary of the GC invocation durations.',
88 | unit: '',
89 | },
90 | ],
91 | go_goroutines: [
92 | {
93 | type: 'gauge',
94 | help: 'Number of goroutines that currently exist.',
95 | unit: '',
96 | },
97 | ],
98 | go_info: [
99 | {
100 | type: 'gauge',
101 | help: 'Information about the Go environment.',
102 | unit: '',
103 | },
104 | ],
105 | go_memstats_alloc_bytes: [
106 | {
107 | type: 'gauge',
108 | help: 'Number of bytes allocated and still in use.',
109 | unit: '',
110 | },
111 | ],
112 | go_memstats_alloc_bytes_total: [
113 | {
114 | type: 'counter',
115 | help: 'Total number of bytes allocated, even if freed.',
116 | unit: '',
117 | },
118 | ],
119 | go_memstats_buck_hash_sys_bytes: [
120 | {
121 | type: 'gauge',
122 | help: 'Number of bytes used by the profiling bucket hash table.',
123 | unit: '',
124 | },
125 | ],
126 | go_memstats_frees_total: [
127 | { type: 'counter', help: 'Total number of frees.', unit: '' },
128 | ],
129 | go_memstats_gc_cpu_fraction: [
130 | {
131 | type: 'gauge',
132 | help: "The fraction of this program's available CPU time used by the GC since the program started.",
133 | unit: '',
134 | },
135 | ],
136 | go_memstats_gc_sys_bytes: [
137 | {
138 | type: 'gauge',
139 | help: 'Number of bytes used for garbage collection system metadata.',
140 | unit: '',
141 | },
142 | ],
143 | go_memstats_heap_alloc_bytes: [
144 | {
145 | type: 'gauge',
146 | help: 'Number of heap bytes allocated and still in use.',
147 | unit: '',
148 | },
149 | ],
150 | go_memstats_heap_idle_bytes: [
151 | {
152 | type: 'gauge',
153 | help: 'Number of heap bytes waiting to be used.',
154 | unit: '',
155 | },
156 | ],
157 | go_memstats_heap_inuse_bytes: [
158 | {
159 | type: 'gauge',
160 | help: 'Number of heap bytes that are in use.',
161 | unit: '',
162 | },
163 | ],
164 | go_memstats_heap_objects: [
165 | { type: 'gauge', help: 'Number of allocated objects.', unit: '' },
166 | ],
167 | go_memstats_heap_released_bytes: [
168 | { type: 'gauge', help: 'Number of heap bytes released to OS.', unit: '' },
169 | ],
170 | go_memstats_heap_sys_bytes: [
171 | {
172 | type: 'gauge',
173 | help: 'Number of heap bytes obtained from system.',
174 | unit: '',
175 | },
176 | ],
177 | go_memstats_last_gc_time_seconds: [
178 | {
179 | type: 'gauge',
180 | help: 'Number of seconds since 1970 of last garbage collection.',
181 | unit: '',
182 | },
183 | ],
184 | go_memstats_lookups_total: [
185 | { type: 'counter', help: 'Total number of pointer lookups.', unit: '' },
186 | ],
187 | go_memstats_mallocs_total: [
188 | { type: 'counter', help: 'Total number of mallocs.', unit: '' },
189 | ],
190 | go_memstats_mcache_inuse_bytes: [
191 | {
192 | type: 'gauge',
193 | help: 'Number of bytes in use by mcache structures.',
194 | unit: '',
195 | },
196 | ],
197 | go_memstats_mcache_sys_bytes: [
198 | {
199 | type: 'gauge',
200 | help: 'Number of bytes used for mcache structures obtained from system.',
201 | unit: '',
202 | },
203 | ],
204 | go_memstats_mspan_inuse_bytes: [
205 | {
206 | type: 'gauge',
207 | help: 'Number of bytes in use by mspan structures.',
208 | unit: '',
209 | },
210 | ],
211 | go_memstats_mspan_sys_bytes: [
212 | {
213 | type: 'gauge',
214 | help: 'Number of bytes used for mspan structures obtained from system.',
215 | unit: '',
216 | },
217 | ],
218 | go_memstats_next_gc_bytes: [
219 | {
220 | type: 'gauge',
221 | help: 'Number of heap bytes when next garbage collection will take place.',
222 | unit: '',
223 | },
224 | ],
225 | go_memstats_other_sys_bytes: [
226 | {
227 | type: 'gauge',
228 | help: 'Number of bytes used for other system allocations.',
229 | unit: '',
230 | },
231 | ],
232 | go_memstats_stack_inuse_bytes: [
233 | {
234 | type: 'gauge',
235 | help: 'Number of bytes in use by the stack allocator.',
236 | unit: '',
237 | },
238 | ],
239 | go_memstats_stack_sys_bytes: [
240 | {
241 | type: 'gauge',
242 | help: 'Number of bytes obtained from system for stack allocator.',
243 | unit: '',
244 | },
245 | ],
246 | go_memstats_sys_bytes: [
247 | {
248 | type: 'gauge',
249 | help: 'Number of bytes obtained from system.',
250 | unit: '',
251 | },
252 | ],
253 | go_threads: [
254 | { type: 'gauge', help: 'Number of OS threads created.', unit: '' },
255 | ],
256 | machine_cpu_cores: [
257 | { type: 'gauge', help: 'Number of CPU cores on the machine.', unit: '' },
258 | ],
259 | machine_memory_bytes: [
260 | {
261 | type: 'gauge',
262 | help: 'Amount of memory installed on the machine.',
263 | unit: '',
264 | },
265 | ],
266 | net_conntrack_dialer_conn_attempted_total: [
267 | {
268 | type: 'counter',
269 | help: 'Total number of connections attempted by the given dialer a given name.',
270 | unit: '',
271 | },
272 | ],
273 | net_conntrack_dialer_conn_closed_total: [
274 | {
275 | type: 'counter',
276 | help: 'Total number of connections closed which originated from the dialer of a given name.',
277 | unit: '',
278 | },
279 | ],
280 | net_conntrack_dialer_conn_established_total: [
281 | {
282 | type: 'counter',
283 | help: 'Total number of connections successfully established by the given dialer a given name.',
284 | unit: '',
285 | },
286 | ],
287 | net_conntrack_dialer_conn_failed_total: [
288 | {
289 | type: 'counter',
290 | help: 'Total number of connections failed to dial by the dialer a given name.',
291 | unit: '',
292 | },
293 | ],
294 | net_conntrack_listener_conn_accepted_total: [
295 | {
296 | type: 'counter',
297 | help: 'Total number of connections opened to the listener of a given name.',
298 | unit: '',
299 | },
300 | ],
301 | net_conntrack_listener_conn_closed_total: [
302 | {
303 | type: 'counter',
304 | help: 'Total number of connections closed that were made to the listener of a given name.',
305 | unit: '',
306 | },
307 | ],
308 | node_arp_entries: [
309 | { type: 'gauge', help: 'ARP entries by device', unit: '' },
310 | ],
311 | node_boot_time_seconds: [
312 | { type: 'gauge', help: 'Node boot time, in unixtime.', unit: '' },
313 | ],
314 | node_context_switches_total: [
315 | { type: 'counter', help: 'Total number of context switches.', unit: '' },
316 | ],
317 | node_cooling_device_cur_state: [
318 | {
319 | type: 'gauge',
320 | help: 'Current throttle state of the cooling device',
321 | unit: '',
322 | },
323 | ],
324 | node_cooling_device_max_state: [
325 | {
326 | type: 'gauge',
327 | help: 'Maximum throttle state of the cooling device',
328 | unit: '',
329 | },
330 | ],
331 | node_cpu_guest_seconds_total: [
332 | {
333 | type: 'counter',
334 | help: 'Seconds the CPUs spent in guests (VMs) for each mode.',
335 | unit: '',
336 | },
337 | ],
338 | node_cpu_seconds_total: [
339 | {
340 | type: 'counter',
341 | help: 'Seconds the CPUs spent in each mode.',
342 | unit: '',
343 | },
344 | ],
345 | node_disk_discard_time_seconds_total: [
346 | {
347 | type: 'counter',
348 | help: 'This is the total number of seconds spent by all discards.',
349 | unit: '',
350 | },
351 | ],
352 | node_disk_discarded_sectors_total: [
353 | {
354 | type: 'counter',
355 | help: 'The total number of sectors discarded successfully.',
356 | unit: '',
357 | },
358 | ],
359 | node_disk_discards_completed_total: [
360 | {
361 | type: 'counter',
362 | help: 'The total number of discards completed successfully.',
363 | unit: '',
364 | },
365 | ],
366 | node_disk_discards_merged_total: [
367 | {
368 | type: 'counter',
369 | help: 'The total number of discards merged.',
370 | unit: '',
371 | },
372 | ],
373 | node_disk_flush_requests_time_seconds_total: [
374 | {
375 | type: 'counter',
376 | help: 'This is the total number of seconds spent by all flush requests.',
377 | unit: '',
378 | },
379 | ],
380 | node_disk_flush_requests_total: [
381 | {
382 | type: 'counter',
383 | help: 'The total number of flush requests completed successfully',
384 | unit: '',
385 | },
386 | ],
387 | node_disk_info: [
388 | {
389 | type: 'gauge',
390 | help: 'Info of /sys/block/\u003cblock_device\u003e.',
391 | unit: '',
392 | },
393 | ],
394 | node_disk_io_now: [
395 | {
396 | type: 'gauge',
397 | help: 'The number of I/Os currently in progress.',
398 | unit: '',
399 | },
400 | ],
401 | node_disk_io_time_seconds_total: [
402 | { type: 'counter', help: 'Total seconds spent doing I/Os.', unit: '' },
403 | ],
404 | node_disk_io_time_weighted_seconds_total: [
405 | {
406 | type: 'counter',
407 | help: 'The weighted # of seconds spent doing I/Os.',
408 | unit: '',
409 | },
410 | ],
411 | node_disk_read_bytes_total: [
412 | {
413 | type: 'counter',
414 | help: 'The total number of bytes read successfully.',
415 | unit: '',
416 | },
417 | ],
418 | node_disk_read_time_seconds_total: [
419 | {
420 | type: 'counter',
421 | help: 'The total number of seconds spent by all reads.',
422 | unit: '',
423 | },
424 | ],
425 | node_disk_reads_completed_total: [
426 | {
427 | type: 'counter',
428 | help: 'The total number of reads completed successfully.',
429 | unit: '',
430 | },
431 | ],
432 | node_disk_reads_merged_total: [
433 | { type: 'counter', help: 'The total number of reads merged.', unit: '' },
434 | ],
435 | node_disk_write_time_seconds_total: [
436 | {
437 | type: 'counter',
438 | help: 'This is the total number of seconds spent by all writes.',
439 | unit: '',
440 | },
441 | ],
442 | node_disk_writes_completed_total: [
443 | {
444 | type: 'counter',
445 | help: 'The total number of writes completed successfully.',
446 | unit: '',
447 | },
448 | ],
449 | node_disk_writes_merged_total: [
450 | { type: 'counter', help: 'The number of writes merged.', unit: '' },
451 | ],
452 | node_disk_written_bytes_total: [
453 | {
454 | type: 'counter',
455 | help: 'The total number of bytes written successfully.',
456 | unit: '',
457 | },
458 | ],
459 | node_dmi_info: [
460 | {
461 | type: 'gauge',
462 | help: "A metric with a constant '1' value labeled by bios_date, bios_release, bios_vendor, bios_version, board_asset_tag, board_name, board_serial, board_vendor, board_version, chassis_asset_tag, chassis_serial, chassis_vendor, chassis_version, product_family, product_name, product_serial, product_sku, product_uuid, product_version, system_vendor if provided by DMI.",
463 | unit: '',
464 | },
465 | ],
466 | node_entropy_available_bits: [
467 | { type: 'gauge', help: 'Bits of available entropy.', unit: '' },
468 | ],
469 | node_entropy_pool_size_bits: [
470 | { type: 'gauge', help: 'Bits of entropy pool.', unit: '' },
471 | ],
472 | node_exporter_build_info: [
473 | {
474 | type: 'gauge',
475 | help: "A metric with a constant '1' value labeled by version, revision, branch, goversion from which node_exporter was built, and the goos and goarch for the build.",
476 | unit: '',
477 | },
478 | ],
479 | node_filefd_allocated: [
480 | {
481 | type: 'gauge',
482 | help: 'File descriptor statistics: allocated.',
483 | unit: '',
484 | },
485 | ],
486 | node_filefd_maximum: [
487 | { type: 'gauge', help: 'File descriptor statistics: maximum.', unit: '' },
488 | ],
489 | node_filesystem_avail_bytes: [
490 | {
491 | type: 'gauge',
492 | help: 'Filesystem space available to non-root users in bytes.',
493 | unit: '',
494 | },
495 | ],
496 | node_filesystem_device_error: [
497 | {
498 | type: 'gauge',
499 | help: 'Whether an error occurred while getting statistics for the given device.',
500 | unit: '',
501 | },
502 | ],
503 | node_filesystem_files: [
504 | { type: 'gauge', help: 'Filesystem total file nodes.', unit: '' },
505 | ],
506 | node_filesystem_files_free: [
507 | { type: 'gauge', help: 'Filesystem total free file nodes.', unit: '' },
508 | ],
509 | node_filesystem_free_bytes: [
510 | { type: 'gauge', help: 'Filesystem free space in bytes.', unit: '' },
511 | ],
512 | node_filesystem_readonly: [
513 | { type: 'gauge', help: 'Filesystem read-only status.', unit: '' },
514 | ],
515 | node_filesystem_size_bytes: [
516 | { type: 'gauge', help: 'Filesystem size in bytes.', unit: '' },
517 | ],
518 | node_forks_total: [
519 | { type: 'counter', help: 'Total number of forks.', unit: '' },
520 | ],
521 | node_intr_total: [
522 | {
523 | type: 'counter',
524 | help: 'Total number of interrupts serviced.',
525 | unit: '',
526 | },
527 | ],
528 | node_load1: [{ type: 'gauge', help: '1m load average.', unit: '' }],
529 | node_load15: [{ type: 'gauge', help: '15m load average.', unit: '' }],
530 | node_load5: [{ type: 'gauge', help: '5m load average.', unit: '' }],
531 | node_memory_Active_anon_bytes: [
532 | {
533 | type: 'gauge',
534 | help: 'Memory information field Active_anon_bytes.',
535 | unit: '',
536 | },
537 | ],
538 | node_memory_Active_bytes: [
539 | {
540 | type: 'gauge',
541 | help: 'Memory information field Active_bytes.',
542 | unit: '',
543 | },
544 | ],
545 | node_memory_Active_file_bytes: [
546 | {
547 | type: 'gauge',
548 | help: 'Memory information field Active_file_bytes.',
549 | unit: '',
550 | },
551 | ],
552 | node_memory_AnonHugePages_bytes: [
553 | {
554 | type: 'gauge',
555 | help: 'Memory information field AnonHugePages_bytes.',
556 | unit: '',
557 | },
558 | ],
559 | node_memory_AnonPages_bytes: [
560 | {
561 | type: 'gauge',
562 | help: 'Memory information field AnonPages_bytes.',
563 | unit: '',
564 | },
565 | ],
566 | node_memory_Bounce_bytes: [
567 | {
568 | type: 'gauge',
569 | help: 'Memory information field Bounce_bytes.',
570 | unit: '',
571 | },
572 | ],
573 | node_memory_Buffers_bytes: [
574 | {
575 | type: 'gauge',
576 | help: 'Memory information field Buffers_bytes.',
577 | unit: '',
578 | },
579 | ],
580 | node_memory_Cached_bytes: [
581 | {
582 | type: 'gauge',
583 | help: 'Memory information field Cached_bytes.',
584 | unit: '',
585 | },
586 | ],
587 | node_memory_CommitLimit_bytes: [
588 | {
589 | type: 'gauge',
590 | help: 'Memory information field CommitLimit_bytes.',
591 | unit: '',
592 | },
593 | ],
594 | node_memory_Committed_AS_bytes: [
595 | {
596 | type: 'gauge',
597 | help: 'Memory information field Committed_AS_bytes.',
598 | unit: '',
599 | },
600 | ],
601 | node_memory_DirectMap2M_bytes: [
602 | {
603 | type: 'gauge',
604 | help: 'Memory information field DirectMap2M_bytes.',
605 | unit: '',
606 | },
607 | ],
608 | node_memory_DirectMap4k_bytes: [
609 | {
610 | type: 'gauge',
611 | help: 'Memory information field DirectMap4k_bytes.',
612 | unit: '',
613 | },
614 | ],
615 | node_memory_Dirty_bytes: [
616 | {
617 | type: 'gauge',
618 | help: 'Memory information field Dirty_bytes.',
619 | unit: '',
620 | },
621 | ],
622 | node_memory_FileHugePages_bytes: [
623 | {
624 | type: 'gauge',
625 | help: 'Memory information field FileHugePages_bytes.',
626 | unit: '',
627 | },
628 | ],
629 | node_memory_FilePmdMapped_bytes: [
630 | {
631 | type: 'gauge',
632 | help: 'Memory information field FilePmdMapped_bytes.',
633 | unit: '',
634 | },
635 | ],
636 | node_memory_HardwareCorrupted_bytes: [
637 | {
638 | type: 'gauge',
639 | help: 'Memory information field HardwareCorrupted_bytes.',
640 | unit: '',
641 | },
642 | ],
643 | node_memory_HugePages_Free: [
644 | {
645 | type: 'gauge',
646 | help: 'Memory information field HugePages_Free.',
647 | unit: '',
648 | },
649 | ],
650 | node_memory_HugePages_Rsvd: [
651 | {
652 | type: 'gauge',
653 | help: 'Memory information field HugePages_Rsvd.',
654 | unit: '',
655 | },
656 | ],
657 | node_memory_HugePages_Surp: [
658 | {
659 | type: 'gauge',
660 | help: 'Memory information field HugePages_Surp.',
661 | unit: '',
662 | },
663 | ],
664 | node_memory_HugePages_Total: [
665 | {
666 | type: 'gauge',
667 | help: 'Memory information field HugePages_Total.',
668 | unit: '',
669 | },
670 | ],
671 | node_memory_Hugepagesize_bytes: [
672 | {
673 | type: 'gauge',
674 | help: 'Memory information field Hugepagesize_bytes.',
675 | unit: '',
676 | },
677 | ],
678 | node_memory_Hugetlb_bytes: [
679 | {
680 | type: 'gauge',
681 | help: 'Memory information field Hugetlb_bytes.',
682 | unit: '',
683 | },
684 | ],
685 | node_memory_Inactive_anon_bytes: [
686 | {
687 | type: 'gauge',
688 | help: 'Memory information field Inactive_anon_bytes.',
689 | unit: '',
690 | },
691 | ],
692 | node_memory_Inactive_bytes: [
693 | {
694 | type: 'gauge',
695 | help: 'Memory information field Inactive_bytes.',
696 | unit: '',
697 | },
698 | ],
699 | node_memory_Inactive_file_bytes: [
700 | {
701 | type: 'gauge',
702 | help: 'Memory information field Inactive_file_bytes.',
703 | unit: '',
704 | },
705 | ],
706 | node_memory_KReclaimable_bytes: [
707 | {
708 | type: 'gauge',
709 | help: 'Memory information field KReclaimable_bytes.',
710 | unit: '',
711 | },
712 | ],
713 | node_memory_KernelStack_bytes: [
714 | {
715 | type: 'gauge',
716 | help: 'Memory information field KernelStack_bytes.',
717 | unit: '',
718 | },
719 | ],
720 | node_memory_Mapped_bytes: [
721 | {
722 | type: 'gauge',
723 | help: 'Memory information field Mapped_bytes.',
724 | unit: '',
725 | },
726 | ],
727 | node_memory_MemAvailable_bytes: [
728 | {
729 | type: 'gauge',
730 | help: 'Memory information field MemAvailable_bytes.',
731 | unit: '',
732 | },
733 | ],
734 | node_memory_MemFree_bytes: [
735 | {
736 | type: 'gauge',
737 | help: 'Memory information field MemFree_bytes.',
738 | unit: '',
739 | },
740 | ],
741 | node_memory_MemTotal_bytes: [
742 | {
743 | type: 'gauge',
744 | help: 'Memory information field MemTotal_bytes.',
745 | unit: '',
746 | },
747 | ],
748 | node_memory_Mlocked_bytes: [
749 | {
750 | type: 'gauge',
751 | help: 'Memory information field Mlocked_bytes.',
752 | unit: '',
753 | },
754 | ],
755 | node_memory_NFS_Unstable_bytes: [
756 | {
757 | type: 'gauge',
758 | help: 'Memory information field NFS_Unstable_bytes.',
759 | unit: '',
760 | },
761 | ],
762 | node_memory_PageTables_bytes: [
763 | {
764 | type: 'gauge',
765 | help: 'Memory information field PageTables_bytes.',
766 | unit: '',
767 | },
768 | ],
769 | node_memory_Percpu_bytes: [
770 | {
771 | type: 'gauge',
772 | help: 'Memory information field Percpu_bytes.',
773 | unit: '',
774 | },
775 | ],
776 | node_memory_SReclaimable_bytes: [
777 | {
778 | type: 'gauge',
779 | help: 'Memory information field SReclaimable_bytes.',
780 | unit: '',
781 | },
782 | ],
783 | node_memory_SUnreclaim_bytes: [
784 | {
785 | type: 'gauge',
786 | help: 'Memory information field SUnreclaim_bytes.',
787 | unit: '',
788 | },
789 | ],
790 | node_memory_ShmemHugePages_bytes: [
791 | {
792 | type: 'gauge',
793 | help: 'Memory information field ShmemHugePages_bytes.',
794 | unit: '',
795 | },
796 | ],
797 | node_memory_ShmemPmdMapped_bytes: [
798 | {
799 | type: 'gauge',
800 | help: 'Memory information field ShmemPmdMapped_bytes.',
801 | unit: '',
802 | },
803 | ],
804 | node_memory_Shmem_bytes: [
805 | {
806 | type: 'gauge',
807 | help: 'Memory information field Shmem_bytes.',
808 | unit: '',
809 | },
810 | ],
811 | node_memory_Slab_bytes: [
812 | { type: 'gauge', help: 'Memory information field Slab_bytes.', unit: '' },
813 | ],
814 | node_memory_SwapCached_bytes: [
815 | {
816 | type: 'gauge',
817 | help: 'Memory information field SwapCached_bytes.',
818 | unit: '',
819 | },
820 | ],
821 | node_memory_SwapFree_bytes: [
822 | {
823 | type: 'gauge',
824 | help: 'Memory information field SwapFree_bytes.',
825 | unit: '',
826 | },
827 | ],
828 | node_memory_SwapTotal_bytes: [
829 | {
830 | type: 'gauge',
831 | help: 'Memory information field SwapTotal_bytes.',
832 | unit: '',
833 | },
834 | ],
835 | node_memory_Unevictable_bytes: [
836 | {
837 | type: 'gauge',
838 | help: 'Memory information field Unevictable_bytes.',
839 | unit: '',
840 | },
841 | ],
842 | node_memory_VmallocChunk_bytes: [
843 | {
844 | type: 'gauge',
845 | help: 'Memory information field VmallocChunk_bytes.',
846 | unit: '',
847 | },
848 | ],
849 | node_memory_VmallocTotal_bytes: [
850 | {
851 | type: 'gauge',
852 | help: 'Memory information field VmallocTotal_bytes.',
853 | unit: '',
854 | },
855 | ],
856 | node_memory_VmallocUsed_bytes: [
857 | {
858 | type: 'gauge',
859 | help: 'Memory information field VmallocUsed_bytes.',
860 | unit: '',
861 | },
862 | ],
863 | node_memory_WritebackTmp_bytes: [
864 | {
865 | type: 'gauge',
866 | help: 'Memory information field WritebackTmp_bytes.',
867 | unit: '',
868 | },
869 | ],
870 | node_memory_Writeback_bytes: [
871 | {
872 | type: 'gauge',
873 | help: 'Memory information field Writeback_bytes.',
874 | unit: '',
875 | },
876 | ],
877 | node_netstat_Icmp6_InErrors: [
878 | { type: 'unknown', help: 'Statistic Icmp6InErrors.', unit: '' },
879 | ],
880 | node_netstat_Icmp6_InMsgs: [
881 | { type: 'unknown', help: 'Statistic Icmp6InMsgs.', unit: '' },
882 | ],
883 | node_netstat_Icmp6_OutMsgs: [
884 | { type: 'unknown', help: 'Statistic Icmp6OutMsgs.', unit: '' },
885 | ],
886 | node_netstat_Icmp_InErrors: [
887 | { type: 'unknown', help: 'Statistic IcmpInErrors.', unit: '' },
888 | ],
889 | node_netstat_Icmp_InMsgs: [
890 | { type: 'unknown', help: 'Statistic IcmpInMsgs.', unit: '' },
891 | ],
892 | node_netstat_Icmp_OutMsgs: [
893 | { type: 'unknown', help: 'Statistic IcmpOutMsgs.', unit: '' },
894 | ],
895 | node_netstat_Ip6_InOctets: [
896 | { type: 'unknown', help: 'Statistic Ip6InOctets.', unit: '' },
897 | ],
898 | node_netstat_Ip6_OutOctets: [
899 | { type: 'unknown', help: 'Statistic Ip6OutOctets.', unit: '' },
900 | ],
901 | node_netstat_IpExt_InOctets: [
902 | { type: 'unknown', help: 'Statistic IpExtInOctets.', unit: '' },
903 | ],
904 | node_netstat_IpExt_OutOctets: [
905 | { type: 'unknown', help: 'Statistic IpExtOutOctets.', unit: '' },
906 | ],
907 | node_netstat_Ip_Forwarding: [
908 | { type: 'unknown', help: 'Statistic IpForwarding.', unit: '' },
909 | ],
910 | node_netstat_TcpExt_ListenDrops: [
911 | { type: 'unknown', help: 'Statistic TcpExtListenDrops.', unit: '' },
912 | ],
913 | node_netstat_TcpExt_ListenOverflows: [
914 | { type: 'unknown', help: 'Statistic TcpExtListenOverflows.', unit: '' },
915 | ],
916 | node_netstat_TcpExt_SyncookiesFailed: [
917 | { type: 'unknown', help: 'Statistic TcpExtSyncookiesFailed.', unit: '' },
918 | ],
919 | node_netstat_TcpExt_SyncookiesRecv: [
920 | { type: 'unknown', help: 'Statistic TcpExtSyncookiesRecv.', unit: '' },
921 | ],
922 | node_netstat_TcpExt_SyncookiesSent: [
923 | { type: 'unknown', help: 'Statistic TcpExtSyncookiesSent.', unit: '' },
924 | ],
925 | node_netstat_TcpExt_TCPSynRetrans: [
926 | { type: 'unknown', help: 'Statistic TcpExtTCPSynRetrans.', unit: '' },
927 | ],
928 | node_netstat_TcpExt_TCPTimeouts: [
929 | { type: 'unknown', help: 'Statistic TcpExtTCPTimeouts.', unit: '' },
930 | ],
931 | node_netstat_Tcp_ActiveOpens: [
932 | { type: 'unknown', help: 'Statistic TcpActiveOpens.', unit: '' },
933 | ],
934 | node_netstat_Tcp_CurrEstab: [
935 | { type: 'unknown', help: 'Statistic TcpCurrEstab.', unit: '' },
936 | ],
937 | node_netstat_Tcp_InErrs: [
938 | { type: 'unknown', help: 'Statistic TcpInErrs.', unit: '' },
939 | ],
940 | node_netstat_Tcp_InSegs: [
941 | { type: 'unknown', help: 'Statistic TcpInSegs.', unit: '' },
942 | ],
943 | node_netstat_Tcp_OutRsts: [
944 | { type: 'unknown', help: 'Statistic TcpOutRsts.', unit: '' },
945 | ],
946 | node_netstat_Tcp_OutSegs: [
947 | { type: 'unknown', help: 'Statistic TcpOutSegs.', unit: '' },
948 | ],
949 | node_netstat_Tcp_PassiveOpens: [
950 | { type: 'unknown', help: 'Statistic TcpPassiveOpens.', unit: '' },
951 | ],
952 | node_netstat_Tcp_RetransSegs: [
953 | { type: 'unknown', help: 'Statistic TcpRetransSegs.', unit: '' },
954 | ],
955 | node_netstat_Udp6_InDatagrams: [
956 | { type: 'unknown', help: 'Statistic Udp6InDatagrams.', unit: '' },
957 | ],
958 | node_netstat_Udp6_InErrors: [
959 | { type: 'unknown', help: 'Statistic Udp6InErrors.', unit: '' },
960 | ],
961 | node_netstat_Udp6_NoPorts: [
962 | { type: 'unknown', help: 'Statistic Udp6NoPorts.', unit: '' },
963 | ],
964 | node_netstat_Udp6_OutDatagrams: [
965 | { type: 'unknown', help: 'Statistic Udp6OutDatagrams.', unit: '' },
966 | ],
967 | node_netstat_Udp6_RcvbufErrors: [
968 | { type: 'unknown', help: 'Statistic Udp6RcvbufErrors.', unit: '' },
969 | ],
970 | node_netstat_Udp6_SndbufErrors: [
971 | { type: 'unknown', help: 'Statistic Udp6SndbufErrors.', unit: '' },
972 | ],
973 | node_netstat_UdpLite6_InErrors: [
974 | { type: 'unknown', help: 'Statistic UdpLite6InErrors.', unit: '' },
975 | ],
976 | node_netstat_UdpLite_InErrors: [
977 | { type: 'unknown', help: 'Statistic UdpLiteInErrors.', unit: '' },
978 | ],
979 | node_netstat_Udp_InDatagrams: [
980 | { type: 'unknown', help: 'Statistic UdpInDatagrams.', unit: '' },
981 | ],
982 | node_netstat_Udp_InErrors: [
983 | { type: 'unknown', help: 'Statistic UdpInErrors.', unit: '' },
984 | ],
985 | node_netstat_Udp_NoPorts: [
986 | { type: 'unknown', help: 'Statistic UdpNoPorts.', unit: '' },
987 | ],
988 | node_netstat_Udp_OutDatagrams: [
989 | { type: 'unknown', help: 'Statistic UdpOutDatagrams.', unit: '' },
990 | ],
991 | node_netstat_Udp_RcvbufErrors: [
992 | { type: 'unknown', help: 'Statistic UdpRcvbufErrors.', unit: '' },
993 | ],
994 | node_netstat_Udp_SndbufErrors: [
995 | { type: 'unknown', help: 'Statistic UdpSndbufErrors.', unit: '' },
996 | ],
997 | node_network_address_assign_type: [
998 | {
999 | type: 'gauge',
1000 | help: 'Network device property: address_assign_type',
1001 | unit: '',
1002 | },
1003 | ],
1004 | node_network_carrier: [
1005 | { type: 'gauge', help: 'Network device property: carrier', unit: '' },
1006 | ],
1007 | node_network_carrier_changes_total: [
1008 | {
1009 | type: 'counter',
1010 | help: 'Network device property: carrier_changes_total',
1011 | unit: '',
1012 | },
1013 | ],
1014 | node_network_carrier_down_changes_total: [
1015 | {
1016 | type: 'counter',
1017 | help: 'Network device property: carrier_down_changes_total',
1018 | unit: '',
1019 | },
1020 | ],
1021 | node_network_carrier_up_changes_total: [
1022 | {
1023 | type: 'counter',
1024 | help: 'Network device property: carrier_up_changes_total',
1025 | unit: '',
1026 | },
1027 | ],
1028 | node_network_device_id: [
1029 | { type: 'gauge', help: 'Network device property: device_id', unit: '' },
1030 | ],
1031 | node_network_dormant: [
1032 | { type: 'gauge', help: 'Network device property: dormant', unit: '' },
1033 | ],
1034 | node_network_flags: [
1035 | { type: 'gauge', help: 'Network device property: flags', unit: '' },
1036 | ],
1037 | node_network_iface_id: [
1038 | { type: 'gauge', help: 'Network device property: iface_id', unit: '' },
1039 | ],
1040 | node_network_iface_link: [
1041 | { type: 'gauge', help: 'Network device property: iface_link', unit: '' },
1042 | ],
1043 | node_network_iface_link_mode: [
1044 | {
1045 | type: 'gauge',
1046 | help: 'Network device property: iface_link_mode',
1047 | unit: '',
1048 | },
1049 | ],
1050 | node_network_info: [
1051 | {
1052 | type: 'gauge',
1053 | help: 'Non-numeric data from /sys/class/net/\u003ciface\u003e, value is always 1.',
1054 | unit: '',
1055 | },
1056 | ],
1057 | node_network_mtu_bytes: [
1058 | { type: 'gauge', help: 'Network device property: mtu_bytes', unit: '' },
1059 | ],
1060 | node_network_name_assign_type: [
1061 | {
1062 | type: 'gauge',
1063 | help: 'Network device property: name_assign_type',
1064 | unit: '',
1065 | },
1066 | ],
1067 | node_network_net_dev_group: [
1068 | {
1069 | type: 'gauge',
1070 | help: 'Network device property: net_dev_group',
1071 | unit: '',
1072 | },
1073 | ],
1074 | node_network_protocol_type: [
1075 | {
1076 | type: 'gauge',
1077 | help: 'Network device property: protocol_type',
1078 | unit: '',
1079 | },
1080 | ],
1081 | node_network_receive_bytes_total: [
1082 | {
1083 | type: 'counter',
1084 | help: 'Network device statistic receive_bytes.',
1085 | unit: '',
1086 | },
1087 | ],
1088 | node_network_receive_compressed_total: [
1089 | {
1090 | type: 'counter',
1091 | help: 'Network device statistic receive_compressed.',
1092 | unit: '',
1093 | },
1094 | ],
1095 | node_network_receive_drop_total: [
1096 | {
1097 | type: 'counter',
1098 | help: 'Network device statistic receive_drop.',
1099 | unit: '',
1100 | },
1101 | ],
1102 | node_network_receive_errs_total: [
1103 | {
1104 | type: 'counter',
1105 | help: 'Network device statistic receive_errs.',
1106 | unit: '',
1107 | },
1108 | ],
1109 | node_network_receive_fifo_total: [
1110 | {
1111 | type: 'counter',
1112 | help: 'Network device statistic receive_fifo.',
1113 | unit: '',
1114 | },
1115 | ],
1116 | node_network_receive_frame_total: [
1117 | {
1118 | type: 'counter',
1119 | help: 'Network device statistic receive_frame.',
1120 | unit: '',
1121 | },
1122 | ],
1123 | node_network_receive_multicast_total: [
1124 | {
1125 | type: 'counter',
1126 | help: 'Network device statistic receive_multicast.',
1127 | unit: '',
1128 | },
1129 | ],
1130 | node_network_receive_nohandler_total: [
1131 | {
1132 | type: 'counter',
1133 | help: 'Network device statistic receive_nohandler.',
1134 | unit: '',
1135 | },
1136 | ],
1137 | node_network_receive_packets_total: [
1138 | {
1139 | type: 'counter',
1140 | help: 'Network device statistic receive_packets.',
1141 | unit: '',
1142 | },
1143 | ],
1144 | node_network_speed_bytes: [
1145 | { type: 'gauge',
1146 | help: 'Network device property: speed_bytes',
1147 | unit: '' },
1148 | ],
1149 | node_network_transmit_bytes_total: [
1150 | {
1151 | type: 'counter',
1152 | help: 'Network device statistic transmit_bytes.',
1153 | unit: '',
1154 | },
1155 | ],
1156 | node_network_transmit_carrier_total: [
1157 | {
1158 | type: 'counter',
1159 | help: 'Network device statistic transmit_carrier.',
1160 | unit: '',
1161 | },
1162 | ],
1163 | node_network_transmit_colls_total: [
1164 | {
1165 | type: 'counter',
1166 | help: 'Network device statistic transmit_colls.',
1167 | unit: '',
1168 | },
1169 | ],
1170 | node_network_transmit_compressed_total: [
1171 | {
1172 | type: 'counter',
1173 | help: 'Network device statistic transmit_compressed.',
1174 | unit: '',
1175 | },
1176 | ],
1177 | node_network_transmit_drop_total: [
1178 | {
1179 | type: 'counter',
1180 | help: 'Network device statistic transmit_drop.',
1181 | unit: '',
1182 | },
1183 | ],
1184 | node_network_transmit_errs_total: [
1185 | {
1186 | type: 'counter',
1187 | help: 'Network device statistic transmit_errs.',
1188 | unit: '',
1189 | },
1190 | ],
1191 | node_network_transmit_fifo_total: [
1192 | {
1193 | type: 'counter',
1194 | help: 'Network device statistic transmit_fifo.',
1195 | unit: '',
1196 | },
1197 | ],
1198 | node_network_transmit_packets_total: [
1199 | {
1200 | type: 'counter',
1201 | help: 'Network device statistic transmit_packets.',
1202 | unit: '',
1203 | },
1204 | ],
1205 | node_network_transmit_queue_length: [
1206 | {
1207 | type: 'gauge',
1208 | help: 'Network device property: transmit_queue_length',
1209 | unit: '',
1210 | },
1211 | ],
1212 | node_network_up: [
1213 | {
1214 | type: 'gauge',
1215 | help: "Value is 1 if operstate is 'up', 0 otherwise.",
1216 | unit: '',
1217 | },
1218 | ],
1219 | node_nf_conntrack_entries: [
1220 | {
1221 | type: 'gauge',
1222 | help: 'Number of currently allocated flow entries for connection tracking.',
1223 | unit: '',
1224 | },
1225 | ],
1226 | node_nf_conntrack_entries_limit: [
1227 | {
1228 | type: 'gauge',
1229 | help: 'Maximum size of connection tracking table.',
1230 | unit: '',
1231 | },
1232 | ],
1233 | node_os_info: [
1234 | {
1235 | type: 'gauge',
1236 | help: "A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id.",
1237 | unit: '',
1238 | },
1239 | ],
1240 | node_os_version: [
1241 | {
1242 | type: 'gauge',
1243 | help: 'Metric containing the major.minor part of the OS version.',
1244 | unit: '',
1245 | },
1246 | ],
1247 | node_pressure_cpu_waiting_seconds_total: [
1248 | {
1249 | type: 'counter',
1250 | help: 'Total time in seconds that processes have waited for CPU time',
1251 | unit: '',
1252 | },
1253 | ],
1254 | node_pressure_io_stalled_seconds_total: [
1255 | {
1256 | type: 'counter',
1257 | help: 'Total time in seconds no process could make progress due to IO congestion',
1258 | unit: '',
1259 | },
1260 | ],
1261 | node_pressure_io_waiting_seconds_total: [
1262 | {
1263 | type: 'counter',
1264 | help: 'Total time in seconds that processes have waited due to IO congestion',
1265 | unit: '',
1266 | },
1267 | ],
1268 | node_pressure_memory_stalled_seconds_total: [
1269 | {
1270 | type: 'counter',
1271 | help: 'Total time in seconds no process could make progress due to memory congestion',
1272 | unit: '',
1273 | },
1274 | ],
1275 | node_pressure_memory_waiting_seconds_total: [
1276 | {
1277 | type: 'counter',
1278 | help: 'Total time in seconds that processes have waited for memory',
1279 | unit: '',
1280 | },
1281 | ],
1282 | node_procs_blocked: [
1283 | {
1284 | type: 'gauge',
1285 | help: 'Number of processes blocked waiting for I/O to complete.',
1286 | unit: '',
1287 | },
1288 | ],
1289 | node_procs_running: [
1290 | {
1291 | type: 'gauge',
1292 | help: 'Number of processes in runnable state.',
1293 | unit: '',
1294 | },
1295 | ],
1296 | node_schedstat_running_seconds_total: [
1297 | {
1298 | type: 'counter',
1299 | help: 'Number of seconds CPU spent running a process.',
1300 | unit: '',
1301 | },
1302 | ],
1303 | node_schedstat_timeslices_total: [
1304 | {
1305 | type: 'counter',
1306 | help: 'Number of timeslices executed by CPU.',
1307 | unit: '',
1308 | },
1309 | ],
1310 | node_schedstat_waiting_seconds_total: [
1311 | {
1312 | type: 'counter',
1313 | help: 'Number of seconds spent by processing waiting for this CPU.',
1314 | unit: '',
1315 | },
1316 | ],
1317 | node_scrape_collector_duration_seconds: [
1318 | {
1319 | type: 'gauge',
1320 | help: 'node_exporter: Duration of a collector scrape.',
1321 | unit: '',
1322 | },
1323 | ],
1324 | node_scrape_collector_success: [
1325 | {
1326 | type: 'gauge',
1327 | help: 'node_exporter: Whether a collector succeeded.',
1328 | unit: '',
1329 | },
1330 | ],
1331 | node_selinux_enabled: [
1332 | {
1333 | type: 'gauge',
1334 | help: 'SELinux is enabled, 1 is true, 0 is false',
1335 | unit: '',
1336 | },
1337 | ],
1338 | node_sockstat_FRAG6_inuse: [
1339 | {
1340 | type: 'gauge',
1341 | help: 'Number of FRAG6 sockets in state inuse.',
1342 | unit: '',
1343 | },
1344 | ],
1345 | node_sockstat_FRAG6_memory: [
1346 | {
1347 | type: 'gauge',
1348 | help: 'Number of FRAG6 sockets in state memory.',
1349 | unit: '',
1350 | },
1351 | ],
1352 | node_sockstat_FRAG_inuse: [
1353 | {
1354 | type: 'gauge',
1355 | help: 'Number of FRAG sockets in state inuse.',
1356 | unit: '',
1357 | },
1358 | ],
1359 | node_sockstat_FRAG_memory: [
1360 | {
1361 | type: 'gauge',
1362 | help: 'Number of FRAG sockets in state memory.',
1363 | unit: '',
1364 | },
1365 | ],
1366 | node_sockstat_RAW6_inuse: [
1367 | {
1368 | type: 'gauge',
1369 | help: 'Number of RAW6 sockets in state inuse.',
1370 | unit: '',
1371 | },
1372 | ],
1373 | node_sockstat_RAW_inuse: [
1374 | {
1375 | type: 'gauge',
1376 | help: 'Number of RAW sockets in state inuse.',
1377 | unit: '',
1378 | },
1379 | ],
1380 | node_sockstat_TCP6_inuse: [
1381 | {
1382 | type: 'gauge',
1383 | help: 'Number of TCP6 sockets in state inuse.',
1384 | unit: '',
1385 | },
1386 | ],
1387 | node_sockstat_TCP_alloc: [
1388 | {
1389 | type: 'gauge',
1390 | help: 'Number of TCP sockets in state alloc.',
1391 | unit: '',
1392 | },
1393 | ],
1394 | node_sockstat_TCP_inuse: [
1395 | {
1396 | type: 'gauge',
1397 | help: 'Number of TCP sockets in state inuse.',
1398 | unit: '',
1399 | },
1400 | ],
1401 | node_sockstat_TCP_mem: [
1402 | { type: 'gauge', help: 'Number of TCP sockets in state mem.', unit: '' },
1403 | ],
1404 | node_sockstat_TCP_mem_bytes: [
1405 | {
1406 | type: 'gauge',
1407 | help: 'Number of TCP sockets in state mem_bytes.',
1408 | unit: '',
1409 | },
1410 | ],
1411 | node_sockstat_TCP_orphan: [
1412 | {
1413 | type: 'gauge',
1414 | help: 'Number of TCP sockets in state orphan.',
1415 | unit: '',
1416 | },
1417 | ],
1418 | node_sockstat_TCP_tw: [
1419 | { type: 'gauge', help: 'Number of TCP sockets in state tw.', unit: '' },
1420 | ],
1421 | node_sockstat_UDP6_inuse: [
1422 | {
1423 | type: 'gauge',
1424 | help: 'Number of UDP6 sockets in state inuse.',
1425 | unit: '',
1426 | },
1427 | ],
1428 | node_sockstat_UDPLITE6_inuse: [
1429 | {
1430 | type: 'gauge',
1431 | help: 'Number of UDPLITE6 sockets in state inuse.',
1432 | unit: '',
1433 | },
1434 | ],
1435 | node_sockstat_UDPLITE_inuse: [
1436 | {
1437 | type: 'gauge',
1438 | help: 'Number of UDPLITE sockets in state inuse.',
1439 | unit: '',
1440 | },
1441 | ],
1442 | node_sockstat_UDP_inuse: [
1443 | {
1444 | type: 'gauge',
1445 | help: 'Number of UDP sockets in state inuse.',
1446 | unit: '',
1447 | },
1448 | ],
1449 | node_sockstat_UDP_mem: [
1450 | { type: 'gauge', help: 'Number of UDP sockets in state mem.', unit: '' },
1451 | ],
1452 | node_sockstat_UDP_mem_bytes: [
1453 | {
1454 | type: 'gauge',
1455 | help: 'Number of UDP sockets in state mem_bytes.',
1456 | unit: '',
1457 | },
1458 | ],
1459 | node_sockstat_sockets_used: [
1460 | { type: 'gauge',
1461 | help: 'Number of IPv4 sockets in use.',
1462 | unit: '' },
1463 | ],
1464 | node_softnet_backlog_len: [
1465 | { type: 'gauge',
1466 | help: 'Softnet backlog status',
1467 | unit: '' },
1468 | ],
1469 | node_softnet_cpu_collision_total: [
1470 | {
1471 | type: 'counter',
1472 | help: 'Number of collision occur while obtaining device lock while transmitting',
1473 | unit: '',
1474 | },
1475 | ],
1476 | node_softnet_dropped_total: [
1477 | { type: 'counter', help: 'Number of dropped packets', unit: '' },
1478 | ],
1479 | node_softnet_flow_limit_count_total: [
1480 | {
1481 | type: 'counter',
1482 | help: 'Number of times flow limit has been reached',
1483 | unit: '',
1484 | },
1485 | ],
1486 | node_softnet_processed_total: [
1487 | { type: 'counter',
1488 | help: 'Number of processed packets',
1489 | unit: '' },
1490 | ],
1491 | node_softnet_received_rps_total: [
1492 | {
1493 | type: 'counter',
1494 | help: 'Number of times cpu woken up received_rps',
1495 | unit: '',
1496 | },
1497 | ],
1498 | node_softnet_times_squeezed_total: [
1499 | {
1500 | type: 'counter',
1501 | help: 'Number of times processing packets ran out of quota',
1502 | unit: '',
1503 | },
1504 | ],
1505 | node_textfile_scrape_error: [
1506 | {
1507 | type: 'gauge',
1508 | help: '1 if there was an error opening or reading a file, 0 otherwise',
1509 | unit: '',
1510 | },
1511 | ],
1512 | node_time_clocksource_available_info: [
1513 | {
1514 | type: 'gauge',
1515 | help: "Available clocksources read from '/sys/devices/system/clocksource'.",
1516 | unit: '',
1517 | },
1518 | ],
1519 | node_time_clocksource_current_info: [
1520 | {
1521 | type: 'gauge',
1522 | help: "Current clocksource read from '/sys/devices/system/clocksource'.",
1523 | unit: '',
1524 | },
1525 | ],
1526 | node_time_seconds: [
1527 | {
1528 | type: 'gauge',
1529 | help: 'System time in seconds since epoch (1970).',
1530 | unit: '',
1531 | },
1532 | ],
1533 | node_time_zone_offset_seconds: [
1534 | { type: 'gauge', help: 'System time zone offset in seconds.', unit: '' },
1535 | ],
1536 | node_timex_estimated_error_seconds: [
1537 | { type: 'gauge', help: 'Estimated error in seconds.', unit: '' },
1538 | ],
1539 | node_timex_frequency_adjustment_ratio: [
1540 | { type: 'gauge', help: 'Local clock frequency adjustment.', unit: '' },
1541 | ],
1542 | node_timex_loop_time_constant: [
1543 | { type: 'gauge', help: 'Phase-locked loop time constant.', unit: '' },
1544 | ],
1545 | node_timex_maxerror_seconds: [
1546 | { type: 'gauge', help: 'Maximum error in seconds.', unit: '' },
1547 | ],
1548 | node_timex_offset_seconds: [
1549 | {
1550 | type: 'gauge',
1551 | help: 'Time offset in between local system and reference clock.',
1552 | unit: '',
1553 | },
1554 | ],
1555 | node_timex_pps_calibration_total: [
1556 | {
1557 | type: 'counter',
1558 | help: 'Pulse per second count of calibration intervals.',
1559 | unit: '',
1560 | },
1561 | ],
1562 | node_timex_pps_error_total: [
1563 | {
1564 | type: 'counter',
1565 | help: 'Pulse per second count of calibration errors.',
1566 | unit: '',
1567 | },
1568 | ],
1569 | node_timex_pps_frequency_hertz: [
1570 | { type: 'gauge', help: 'Pulse per second frequency.', unit: '' },
1571 | ],
1572 | node_timex_pps_jitter_seconds: [
1573 | { type: 'gauge', help: 'Pulse per second jitter.', unit: '' },
1574 | ],
1575 | node_timex_pps_jitter_total: [
1576 | {
1577 | type: 'counter',
1578 | help: 'Pulse per second count of jitter limit exceeded events.',
1579 | unit: '',
1580 | },
1581 | ],
1582 | node_timex_pps_shift_seconds: [
1583 | { type: 'gauge', help: 'Pulse per second interval duration.', unit: '' },
1584 | ],
1585 | node_timex_pps_stability_exceeded_total: [
1586 | {
1587 | type: 'counter',
1588 | help: 'Pulse per second count of stability limit exceeded events.',
1589 | unit: '',
1590 | },
1591 | ],
1592 | node_timex_pps_stability_hertz: [
1593 | {
1594 | type: 'gauge',
1595 | help: 'Pulse per second stability, average of recent frequency changes.',
1596 | unit: '',
1597 | },
1598 | ],
1599 | node_timex_status: [
1600 | { type: 'gauge', help: 'Value of the status array bits.', unit: '' },
1601 | ],
1602 | node_timex_sync_status: [
1603 | {
1604 | type: 'gauge',
1605 | help: 'Is clock synchronized to a reliable server (1 = yes, 0 = no).',
1606 | unit: '',
1607 | },
1608 | ],
1609 | node_timex_tai_offset_seconds: [
1610 | {
1611 | type: 'gauge',
1612 | help: 'International Atomic Time (TAI) offset.',
1613 | unit: '',
1614 | },
1615 | ],
1616 | node_timex_tick_seconds: [
1617 | { type: 'gauge', help: 'Seconds between clock ticks.', unit: '' },
1618 | ],
1619 | node_udp_queues: [
1620 | {
1621 | type: 'gauge',
1622 | help: 'Number of allocated memory in the kernel for UDP datagrams in bytes.',
1623 | unit: '',
1624 | },
1625 | ],
1626 | node_uname_info: [
1627 | {
1628 | type: 'gauge',
1629 | help: 'Labeled system information as provided by the uname system call.',
1630 | unit: '',
1631 | },
1632 | ],
1633 | node_vmstat_oom_kill: [
1634 | {
1635 | type: 'unknown',
1636 | help: '/proc/vmstat information field oom_kill.',
1637 | unit: '',
1638 | },
1639 | ],
1640 | node_vmstat_pgfault: [
1641 | {
1642 | type: 'unknown',
1643 | help: '/proc/vmstat information field pgfault.',
1644 | unit: '',
1645 | },
1646 | ],
1647 | node_vmstat_pgmajfault: [
1648 | {
1649 | type: 'unknown',
1650 | help: '/proc/vmstat information field pgmajfault.',
1651 | unit: '',
1652 | },
1653 | ],
1654 | node_vmstat_pgpgin: [
1655 | {
1656 | type: 'unknown',
1657 | help: '/proc/vmstat information field pgpgin.',
1658 | unit: '',
1659 | },
1660 | ],
1661 | node_vmstat_pgpgout: [
1662 | {
1663 | type: 'unknown',
1664 | help: '/proc/vmstat information field pgpgout.',
1665 | unit: '',
1666 | },
1667 | ],
1668 | node_vmstat_pswpin: [
1669 | {
1670 | type: 'unknown',
1671 | help: '/proc/vmstat information field pswpin.',
1672 | unit: '',
1673 | },
1674 | ],
1675 | node_vmstat_pswpout: [
1676 | {
1677 | type: 'unknown',
1678 | help: '/proc/vmstat information field pswpout.',
1679 | unit: '',
1680 | },
1681 | ],
1682 | process_cpu_seconds_total: [
1683 | {
1684 | type: 'counter',
1685 | help: 'Total user and system CPU time spent in seconds.',
1686 | unit: '',
1687 | },
1688 | ],
1689 | process_max_fds: [
1690 | {
1691 | type: 'gauge',
1692 | help: 'Maximum number of open file descriptors.',
1693 | unit: '',
1694 | },
1695 | ],
1696 | process_open_fds: [
1697 | { type: 'gauge', help: 'Number of open file descriptors.', unit: '' },
1698 | ],
1699 | process_resident_memory_bytes: [
1700 | { type: 'gauge', help: 'Resident memory size in bytes.', unit: '' },
1701 | ],
1702 | },
1703 | };
1704 |
1705 | export default actions;
1706 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | KUBERNAUTICS
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './components/App';
4 | import styles from '../assets/styles/styles.scss';
5 |
6 | const rootElement = document.getElementById('root');
7 | const root = ReactDOM.createRoot(rootElement);
8 |
9 | root.render(
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/src/jest-setup.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 |
--------------------------------------------------------------------------------
/src/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'jest-environment-jsdom',
3 | setupFilesAfterEnv: [
4 | '/jest-setup.js'
5 | ],
6 | transform: {
7 | '\\.jsx?$': 'babel-jest',
8 | }
9 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const htmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | module.exports = {
5 | mode: process.env.NODE_ENV,
6 | entry: './src/index.js',
7 | output: {
8 | path: path.resolve(__dirname, 'dist'),
9 | filename: 'bundle.js',
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.(js|jsx)$/,
15 | exclude: /node_modules/,
16 | use: {
17 | loader: 'babel-loader',
18 | options: {
19 | presets: ['@babel/preset-env', '@babel/preset-react'],
20 | },
21 | },
22 | },
23 | {
24 | test: /\.scss$/,
25 | use: ['style-loader', 'css-loader', 'sass-loader'],
26 | },
27 | {
28 | test: /\.(png|jpg|jpeg|gif)$/,
29 | use: ['file-loader'],
30 | },
31 | ],
32 | },
33 | resolve: {
34 | extensions: ['.*', '.js', '.jsx', '.scss', '.sass', '.png'],
35 | },
36 | plugins: [
37 | new htmlWebpackPlugin({
38 | template: './src/index.html',
39 | }),
40 | ],
41 | devServer: {
42 | static: {
43 | directory: path.join(__dirname, 'dist'),
44 | },
45 | compress: true,
46 | port: 3000,
47 | proxy: {
48 | '/api/**': `http://localhost:${process.env.EXPRESS_PORT}`,
49 | },
50 | },
51 | devtool: 'eval-source-map',
52 | };
53 |
--------------------------------------------------------------------------------