├── .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 |
  1. 15 | Built with 16 |
  2. 17 |
  3. 18 | Features 19 |
  4. 20 |
  5. Getting Started with Kubernautics
  6. 21 |
  7. Progress
  8. 22 |
  9. How to Contribute
  10. 23 |
  11. Our Team
  12. 24 |
  13. License
  14. 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 | ![homepage](https://i.imgur.com/Iqkai3C.gif) 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 | ![visualizer](https://i.imgur.com/wF4wX7t.gif) 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 | 132 | 140 | 148 | 156 | 157 | 158 |
125 | “Jordan” 126 |
127 | Jordan Buranskas 128 |
129 | 130 | 131 |
133 | Edward 134 |
135 | Edward Li 136 |
137 | 138 | 139 |
141 | Matin 142 |
143 | Matin Schams 144 |
145 | 146 | 147 |
149 | Tyler 150 |
151 | Tyler Shelton 152 |
153 | 154 | 155 |
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 | 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 | 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 | 117 | 118 | 119 | 120 | Set a time range 121 | 132 | 133 | 134 | Set a step size (seconds) 135 | 146 | 147 |
148 | } 149 |
150 | 151 | 152 | 153 | 154 | 168 | 169 |
170 |
171 | ); 172 | }; 173 | 174 | export const CustomChart = (props) => { 175 | return ( 176 | 177 | 178 | 179 | 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 | 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 | --------------------------------------------------------------------------------