├── coverage
├── lcov.info
├── coverage-final.json
├── lcov-report
│ ├── favicon.png
│ ├── sort-arrow-sprite.png
│ ├── prettify.css
│ ├── block-navigation.js
│ └── index.html
└── clover.xml
├── client
├── metricspage
│ └── components
│ │ ├── CPUUsageByNode.js
│ │ ├── BytesReceivedByNode.js
│ │ ├── FreeMemoryByNode.js
│ │ ├── PodMetrics
│ │ ├── FreeMemoryByPod.js
│ │ ├── CPUUsageByPod.js
│ │ ├── BytesReceivedByPod.js
│ │ ├── BytesTransmittedbyPod.js
│ │ └── MemoryUsageByPod.js
│ │ ├── NodeMetrics
│ │ ├── CPUUsageByNode.js
│ │ ├── BytesReceivedByNode.js
│ │ ├── BytesTransmittedByNode.js
│ │ └── MemoryUsageByNode.js
│ │ └── NamespaceMetrics
│ │ ├── CPUUsageByNamespace.js
│ │ ├── BytesTransmittedByNamespace.js
│ │ ├── BytesReceivedByNamespace.js
│ │ └── MemoryUsageByNamespace.js
├── actions
│ ├── constants
│ │ ├── chartConstants.js
│ │ └── actionTypes.js
│ └── actions.js
├── .DS_Store
├── store.js
├── styles.css
├── index.js
├── utils
│ ├── parseStatus.js
│ ├── ComponentWrapper.js
│ ├── renderAlert.js
│ ├── MetricsComponentWrapper.js
│ ├── constants
│ │ └── index.js
│ └── table
│ │ └── helpers.js
├── reducers
│ ├── pods.js
│ ├── nodes.js
│ ├── services.js
│ ├── promMetrics.js
│ ├── deployments.js
│ ├── index.js
│ └── namespace.js
├── homepage
│ ├── components
│ │ ├── Utilizations
│ │ │ ├── CpuUtilization.js
│ │ │ ├── MemoryUtilization.js
│ │ │ ├── CpuTotal.js
│ │ │ └── MemoryTotal.js
│ │ ├── Counts
│ │ │ ├── PodsCount.js
│ │ │ ├── DeploymentsCount.js
│ │ │ ├── ServicesCount.js
│ │ │ └── NodesCount.js
│ │ ├── CriticalNodes
│ │ │ ├── CPUIntensiveNodes.js
│ │ │ ├── BytesReceivedPerNode.js
│ │ │ ├── BytesTransmittedPerNode.js
│ │ │ ├── MemoryIntensiveNodes.js
│ │ │ └── ProblematicItem.js
│ │ ├── CriticalPods
│ │ │ ├── CPUIntensivePods.js
│ │ │ └── MemoryIntensivePods.js
│ │ ├── Refresh.js
│ │ ├── Charts
│ │ │ ├── BarChartTemplate.js
│ │ │ ├── GaugeChartTemplate.js
│ │ │ └── LineChartTemplate.js
│ │ ├── Statuses
│ │ │ ├── PodsStatus.js
│ │ │ └── NodesStatus.js
│ │ └── Problematic
│ │ │ ├── ProblematicPods.js
│ │ │ └── ProblematicNodes.js
│ └── containers
│ │ ├── ProblematicContainer.js
│ │ ├── StatusContainer.js
│ │ ├── CountsContainer.js
│ │ ├── CriticalPodsContainer.js
│ │ ├── CriticalNodesContainer.js
│ │ └── UtilizationContainer.js
├── logspage
│ ├── containers
│ │ └── LogsContainer.js
│ └── components
│ │ └── LogsTable.js
├── custompage
│ └── components
│ │ ├── StepBar.js
│ │ ├── SearchBar.js
│ │ └── TimeRangeBar.js
├── App.js
├── namespaceSelection
│ └── SelectNamespace.js
├── components
│ ├── Statuses
│ │ └── NodesStatus.js
│ └── CriticalNodes
│ │ └── ProblematicNodes.js
├── demoData
│ ├── rawLogs.json
│ ├── logs.json
│ ├── networkTransmitBytes.json
│ ├── networkTransmitFormatted.json
│ └── clusterFreeMemory.json
└── alertspage
│ └── ExpandableRow.js
├── .gitignore
├── .DS_Store
├── .dockerignore
├── assets
└── Logo.png
├── server
├── .DS_Store
├── utils
│ ├── constants.js
│ ├── formatVectorData.js
│ ├── formatLogs.js
│ ├── formatTimeToAvg.js
│ ├── metricsFetch.js
│ └── formatChartData.js
├── PrometheusData
│ └── dataSources.js
├── controllers
│ ├── alertsController.js
│ ├── logsController.js
│ └── getLists.js
└── server.js
├── manifests
├── setup
│ └── namespace.yaml
├── grafana-serviceAccount.yaml
├── nodeExporter-serviceAccount.yaml
├── blackboxExporter-serviceAccount.yaml
├── kubeStateMetrics-serviceAccount.yaml
├── prometheusAdapter-serviceAccount.yaml
├── prometheusOperator-serviceAccount.yaml
├── prometheus-serviceAccount.yaml
├── alertmanager-serviceAccount.yaml
├── blackboxExporter-clusterRole.yaml
├── grafana-config.yaml
├── grafana-serviceMonitor.yaml
├── prometheus-roleConfig.yaml
├── prometheusAdapter-clusterRoleServerResources.yaml
├── grafana-service.yaml
├── prometheusAdapter-clusterRole.yaml
├── kubeStateMetrics-clusterRoleBinding.yaml
├── prometheus-clusterRole.yaml
├── nodeExporter-clusterRoleBinding.yaml
├── prometheusOperator-clusterRoleBinding.yaml
├── prometheusAdapter-apiService.yaml
├── blackboxExporter-clusterRoleBinding.yaml
├── nodeExporter-service.yaml
├── prometheusAdapter-clusterRoleBinding.yaml
├── prometheusAdapter-service.yaml
├── nodeExporter-clusterRole.yaml
├── prometheusAdapter-podDisruptionBudget.yaml
├── prometheus-clusterRoleBinding.yaml
├── prometheus-roleBindingConfig.yaml
├── prometheusOperator-service.yaml
├── prometheusAdapter-roleBindingAuthReader.yaml
├── prometheusAdapter-clusterRoleBindingDelegator.yaml
├── prometheus-podDisruptionBudget.yaml
├── blackboxExporter-service.yaml
├── alertmanager-podDisruptionBudget.yaml
├── kubeStateMetrics-service.yaml
├── kubernetesControlPlane-serviceMonitorKubeScheduler.yaml
├── kubernetesControlPlane-serviceMonitorCoreDNS.yaml
├── prometheus-serviceMonitor.yaml
├── prometheusAdapter-clusterRoleAggregatedMetricsReader.yaml
├── alertmanager-serviceMonitor.yaml
├── prometheus-service.yaml
├── alertmanager-service.yaml
├── grafana-dashboardSources.yaml
├── blackboxExporter-serviceMonitor.yaml
├── grafana-dashboardDatasources.yaml
├── prometheusOperator-serviceMonitor.yaml
├── nodeExporter-serviceMonitor.yaml
├── prometheusAdapter-serviceMonitor.yaml
├── alertmanager-alertmanager.yaml
├── kubeStateMetrics-serviceMonitor.yaml
├── alertmanager-secret.yaml
├── prometheus-prometheus.yaml
├── blackboxExporter-configuration.yaml
├── prometheusOperator-clusterRole.yaml
├── prometheus-roleBindingSpecificNamespaces.yaml
├── kubeStateMetrics-clusterRole.yaml
├── prometheusAdapter-configMap.yaml
├── prometheusOperator-deployment.yaml
├── prometheus-roleSpecificNamespaces.yaml
├── prometheusAdapter-deployment.yaml
├── kubeStateMetrics-deployment.yaml
├── kubeStateMetrics-prometheusRule.yaml
├── blackboxExporter-deployment.yaml
├── nodeExporter-daemonset.yaml
└── kubePrometheus-prometheusRule.yaml
├── infra
├── prometheus
│ ├── alert.yml
│ └── prometheus.yml
├── grafana
│ └── provisioning
│ │ └── datasources
│ │ └── prometheus_ds.yml
├── alertmanager
│ └── alertmanager.yml
└── docker-compose.yml
├── docker-compose.yml
├── Dockerfile-kube
├── index.html
├── fabric8-rbac.yaml
├── Dockerfile
├── armada-depl.yaml
├── package.json
├── webpack.config.js
└── __tests__
└── backendTests.js
/coverage/lcov.info:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/coverage/coverage-final.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/client/metricspage/components/CPUUsageByNode.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | out
4 | infra
--------------------------------------------------------------------------------
/client/metricspage/components/BytesReceivedByNode.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/metricspage/components/FreeMemoryByNode.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Armada/HEAD/.DS_Store
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | infra
3 | out
4 | main.js
5 | preload.js
--------------------------------------------------------------------------------
/client/actions/constants/chartConstants.js:
--------------------------------------------------------------------------------
1 | export const MAX_SERIES = 5;
2 |
--------------------------------------------------------------------------------
/assets/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Armada/HEAD/assets/Logo.png
--------------------------------------------------------------------------------
/client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Armada/HEAD/client/.DS_Store
--------------------------------------------------------------------------------
/server/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Armada/HEAD/server/.DS_Store
--------------------------------------------------------------------------------
/manifests/setup/namespace.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: monitoring
5 |
--------------------------------------------------------------------------------
/coverage/lcov-report/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Armada/HEAD/coverage/lcov-report/favicon.png
--------------------------------------------------------------------------------
/coverage/lcov-report/sort-arrow-sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Armada/HEAD/coverage/lcov-report/sort-arrow-sprite.png
--------------------------------------------------------------------------------
/infra/prometheus/alert.yml:
--------------------------------------------------------------------------------
1 | groups:
2 | - name: DemoAlerts
3 | rules:
4 | - alert: InstanceDown
5 | expr: up{job="services"} < 1
6 | for: 5m
7 |
--------------------------------------------------------------------------------
/infra/grafana/provisioning/datasources/prometheus_ds.yml:
--------------------------------------------------------------------------------
1 | datasources:
2 | - name: Prometheus
3 | access: proxy
4 | type: prometheus
5 | url: http://prometheus:9090
6 | isDefault: true
7 |
--------------------------------------------------------------------------------
/client/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import reducers from "./reducers/index.js";
3 | const store = configureStore({
4 | reducer: reducers,
5 | });
6 | export default store;
7 |
--------------------------------------------------------------------------------
/client/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #fafafa;
3 | font-family: Arial, Helvetica, sans-serif;
4 | }
5 |
6 | .card-content {
7 | display: flex;
8 | flex-wrap: wrap;
9 | }
10 |
11 | h4 {
12 | margin: 1px;
13 | }
14 |
--------------------------------------------------------------------------------
/server/utils/constants.js:
--------------------------------------------------------------------------------
1 | const constants = {};
2 |
3 | constants.TIMESTEP = '10m';
4 | constants.prometheusURL = 'http://127.0.0.1:9090/api/v1/';
5 | // constants.prometheusURL = 'host.docker.internal:9090/api/v1/';
6 |
7 | module.exports = constants;
8 |
--------------------------------------------------------------------------------
/server/PrometheusData/dataSources.js:
--------------------------------------------------------------------------------
1 | const PrometheusAPI = require('../controllers/promethusController')
2 |
3 | const memory = {
4 | isPrometheusUp: { check: false },
5 | };
6 |
7 | module.exports = () => {
8 | return {
9 | prometheusAPI: new PrometheusAPI(memory),
10 | };
11 | };
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | services:
3 | armada:
4 | image: 'armadak8s/armada'
5 | container_name: armada
6 | ports:
7 | - '3001:3001'
8 | volumes:
9 | - .:/usr/src/app
10 | - node_modules:/usr/src/app/node_modules
11 | volumes:
12 | node_modules:
13 |
--------------------------------------------------------------------------------
/manifests/grafana-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: grafana
6 | app.kubernetes.io/name: grafana
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 8.3.3
9 | name: grafana
10 | namespace: monitoring
11 |
--------------------------------------------------------------------------------
/manifests/nodeExporter-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: node-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 1.3.1
9 | name: node-exporter
10 | namespace: monitoring
11 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: blackbox-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.19.0
9 | name: blackbox-exporter
10 | namespace: monitoring
11 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | name: kube-state-metrics
10 | namespace: monitoring
11 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 |
--------------------------------------------------------------------------------
/manifests/prometheusOperator-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: controller
6 | app.kubernetes.io/name: prometheus-operator
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.53.1
9 | name: prometheus-operator
10 | namespace: monitoring
11 |
--------------------------------------------------------------------------------
/manifests/prometheus-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s
11 | namespace: monitoring
12 |
--------------------------------------------------------------------------------
/manifests/alertmanager-serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: alert-router
6 | app.kubernetes.io/instance: main
7 | app.kubernetes.io/name: alertmanager
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 0.23.0
10 | name: alertmanager-main
11 | namespace: monitoring
12 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom/client';
3 | import { Provider } from 'react-redux';
4 |
5 | import store from './store';
6 | import App from './App';
7 | import './styles.css';
8 |
9 | const root = ReactDom.createRoot(document.getElementById('root'));
10 |
11 | root.render(
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: blackbox-exporter
5 | rules:
6 | - apiGroups:
7 | - authentication.k8s.io
8 | resources:
9 | - tokenreviews
10 | verbs:
11 | - create
12 | - apiGroups:
13 | - authorization.k8s.io
14 | resources:
15 | - subjectaccessreviews
16 | verbs:
17 | - create
18 |
--------------------------------------------------------------------------------
/client/utils/parseStatus.js:
--------------------------------------------------------------------------------
1 | const parseStatus = (nodes) => {
2 | const nodesObj = {};
3 | for (let node of nodes) {
4 | nodesObj[node.metadata.name] = {};
5 | let conditions = node.status.conditions;
6 | for (let condition of conditions) {
7 | nodesObj[node.metadata.name][condition.type] = condition.status;
8 | }
9 | }
10 | return nodesObj;
11 | };
12 | export default parseStatus;
13 |
--------------------------------------------------------------------------------
/coverage/clover.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/manifests/grafana-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: grafana
6 | app.kubernetes.io/name: grafana
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 8.3.3
9 | name: grafana-config
10 | namespace: monitoring
11 | stringData:
12 | grafana.ini: |
13 | [date_formats]
14 | default_timezone = UTC
15 | type: Opaque
16 |
--------------------------------------------------------------------------------
/server/utils/formatVectorData.js:
--------------------------------------------------------------------------------
1 | // Format vector data for bar charts
2 | const formatVectorData = (arr, metricName) => {
3 | return arr
4 | .sort((a, b) => parseFloat(b.value[1]) - parseFloat(a.value[1]))
5 | .map((node) => {
6 | return {
7 | label: node.metric[metricName] ? node.metric[metricName] : 'unnamed',
8 | data: parseFloat(node.value[1]),
9 | };
10 | });
11 | };
12 |
13 | module.exports = formatVectorData;
14 |
--------------------------------------------------------------------------------
/client/utils/ComponentWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Paper, Typography } from '@mui/material';
3 |
4 | function ComponentWrapper({ title, children }) {
5 | return (
6 |
7 |
8 | {title}
9 |
10 | {children}
11 |
12 | );
13 | }
14 |
15 | export default ComponentWrapper;
16 |
--------------------------------------------------------------------------------
/server/utils/formatLogs.js:
--------------------------------------------------------------------------------
1 | // Format log data for presentation
2 | const formatLogs = (arr) => {
3 | arr.pop();
4 | const trimmed = arr.map((el) => el.split(/[ ]{2,}/));
5 | const headers = trimmed[0].map((el) => el.toLowerCase().replace(' ', '_'));
6 | trimmed.shift();
7 | return trimmed.map((row) => {
8 | let obj = {};
9 | row.forEach((r, i) => {
10 | obj[headers[i]] = row[i];
11 | });
12 |
13 | return obj;
14 | });
15 | };
16 |
17 | module.exports = formatLogs;
18 |
--------------------------------------------------------------------------------
/client/reducers/pods.js:
--------------------------------------------------------------------------------
1 | import { FETCH_PODS_LIST } from '../actions/constants/actionTypes';
2 |
3 | const initialState = {
4 | items: [],
5 | lastUpdated: null,
6 | };
7 |
8 | const podsReducer = (state = initialState, action) => {
9 | switch (action.type) {
10 | case FETCH_PODS_LIST: {
11 | return { items: action.payload, lastUpdated: new Date().toISOString() };
12 | }
13 | default: {
14 | return state;
15 | }
16 | }
17 | };
18 |
19 | export default podsReducer;
20 |
--------------------------------------------------------------------------------
/manifests/grafana-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: grafana
6 | app.kubernetes.io/name: grafana
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 8.3.3
9 | name: grafana
10 | namespace: monitoring
11 | spec:
12 | endpoints:
13 | - interval: 15s
14 | port: http
15 | selector:
16 | matchLabels:
17 | app.kubernetes.io/name: grafana
18 |
--------------------------------------------------------------------------------
/client/reducers/nodes.js:
--------------------------------------------------------------------------------
1 | import { FETCH_NODES_LIST } from '../actions/constants/actionTypes';
2 |
3 | const initialState = {
4 | items: [],
5 | lastUpdated: null,
6 | };
7 |
8 | const nodesReducer = (state = initialState, action) => {
9 | switch (action.type) {
10 | case FETCH_NODES_LIST: {
11 | return { items: action.payload, lastUpdated: new Date().toISOString() };
12 | }
13 | default: {
14 | return state;
15 | }
16 | }
17 | };
18 |
19 | export default nodesReducer;
20 |
--------------------------------------------------------------------------------
/client/utils/renderAlert.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Alert, Grid } from '@mui/material';
3 |
4 | const renderAlert = (length, message, type) => {
5 | if (length === 0) {
6 | return (
7 |
8 |
9 |
10 | {message}
11 |
12 |
13 |
14 | );
15 | } else return null;
16 | };
17 |
18 | export default renderAlert;
19 |
--------------------------------------------------------------------------------
/infra/prometheus/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 30s
3 | scrape_timeout: 10s
4 |
5 | rule_files:
6 | - alert.yml
7 |
8 | scrape_configs:
9 | - job_name: services
10 | metrics_path: /metrics
11 | static_configs:
12 | - targets:
13 | - 'prometheus:9090'
14 | - 'node-exporter:9100'
15 | - 'host.docker.internal'
16 |
17 | alerting:
18 | alertmanagers:
19 | - scheme: http
20 | static_configs:
21 | - targets: ['alertmanager:9093']
22 |
--------------------------------------------------------------------------------
/manifests/prometheus-roleConfig.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: Role
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s-config
11 | namespace: monitoring
12 | rules:
13 | - apiGroups:
14 | - ""
15 | resources:
16 | - configmaps
17 | verbs:
18 | - get
19 |
--------------------------------------------------------------------------------
/Dockerfile-kube:
--------------------------------------------------------------------------------
1 |
2 | #building and adding kubectl
3 | FROM alpine:3.8 as kubectl
4 |
5 | ADD https://storage.googleapis.com/kubernetes-release/release/v1.6.4/bin/linux/amd64/kubectl /usr/local/bin/kubectl
6 | ENV HOME=/config \
7 | KUBECONFIG=/etc/kubernetes/admin.conf
8 | RUN set -x && \
9 | apk add --no-cache curl ca-certificates && \
10 | chmod +x /usr/local/bin/kubectl
11 |
12 | #your app container
13 | FROM armadak8s/armada:latest
14 |
15 | COPY --from=kubectl /usr/local/bin/kubectl /usr/local/bin/kubectl
--------------------------------------------------------------------------------
/client/reducers/services.js:
--------------------------------------------------------------------------------
1 | import { FETCH_SERVICES_LIST } from '../actions/constants/actionTypes';
2 |
3 | const initialState = {
4 | items: [],
5 | lastUpdated: null,
6 | };
7 |
8 | const servicesReducer = (state = initialState, action) => {
9 | switch (action.type) {
10 | case FETCH_SERVICES_LIST: {
11 | return { items: action.payload, lastUpdated: new Date().toISOString() };
12 | }
13 | default: {
14 | return state;
15 | }
16 | }
17 | };
18 |
19 | export default servicesReducer;
20 |
--------------------------------------------------------------------------------
/client/reducers/promMetrics.js:
--------------------------------------------------------------------------------
1 | import { FETCH_PROM_METRICS } from '../actions/constants/actionTypes';
2 |
3 | const initialState = {
4 | items: [],
5 | lastUpdated: null,
6 | };
7 |
8 | const promMetricsReducer = (state = initialState, action) => {
9 | switch (action.type) {
10 | case FETCH_PROM_METRICS: {
11 | return { items: action.payload, lastUpdated: new Date().toISOString() };
12 | }
13 | default: {
14 | return state;
15 | }
16 | }
17 | };
18 |
19 | export default promMetricsReducer;
20 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-clusterRoleServerResources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: resource-metrics-server-resources
10 | namespace: monitoring
11 | rules:
12 | - apiGroups:
13 | - metrics.k8s.io
14 | resources:
15 | - '*'
16 | verbs:
17 | - '*'
18 |
--------------------------------------------------------------------------------
/client/utils/MetricsComponentWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Box, Paper, Typography } from '@mui/material';
3 |
4 | function MetricsComponentWrapper({ title, children }) {
5 | return (
6 |
17 | {children}
18 |
19 | );
20 | }
21 |
22 | export default MetricsComponentWrapper;
23 |
--------------------------------------------------------------------------------
/client/reducers/deployments.js:
--------------------------------------------------------------------------------
1 | import { FETCH_DEPLOYMENTS_LIST } from '../actions/constants/actionTypes';
2 |
3 | const initialState = {
4 | items: [],
5 | lastUpdated: null,
6 | };
7 |
8 | const deploymentsReducer = (state = initialState, action) => {
9 | switch (action.type) {
10 | case FETCH_DEPLOYMENTS_LIST: {
11 | return { items: action.payload, lastUpdated: new Date().toISOString() };
12 | }
13 | default: {
14 | return state;
15 | }
16 | }
17 | };
18 |
19 | export default deploymentsReducer;
20 |
--------------------------------------------------------------------------------
/client/homepage/components/Utilizations/CpuUtilization.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Card, Typography } from '@mui/material';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 | import GaugeChartTemplate from '../Charts/GaugeChartTemplate';
5 |
6 | function CpuUtilization({ cpu }) {
7 | return (
8 |
13 | );
14 | }
15 |
16 | export default CpuUtilization;
17 |
--------------------------------------------------------------------------------
/manifests/grafana-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: grafana
6 | app.kubernetes.io/name: grafana
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 8.3.3
9 | name: grafana
10 | namespace: monitoring
11 | spec:
12 | ports:
13 | - name: http
14 | port: 3000
15 | targetPort: http
16 | selector:
17 | app.kubernetes.io/component: grafana
18 | app.kubernetes.io/name: grafana
19 | app.kubernetes.io/part-of: kube-prometheus
20 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 | rules:
12 | - apiGroups:
13 | - ""
14 | resources:
15 | - nodes
16 | - namespaces
17 | - pods
18 | - services
19 | verbs:
20 | - get
21 | - list
22 | - watch
23 |
--------------------------------------------------------------------------------
/client/homepage/components/Utilizations/MemoryUtilization.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Card, Typography } from '@mui/material';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 | import GaugeChartTemplate from '../Charts/GaugeChartTemplate';
5 |
6 | function MemoryUtilization({ memory }) {
7 | return (
8 |
13 | );
14 | }
15 |
16 | export default MemoryUtilization;
17 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Armada
7 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/client/homepage/components/Counts/PodsCount.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Card, Typography } from '@mui/material';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 |
5 | // Displays count of pods in cluster
6 | function PodsCount(props) {
7 | const { pods } = props;
8 | const podsCount = pods.length ? pods.length : '-';
9 | return (
10 |
11 | {podsCount}
12 |
13 | );
14 | }
15 |
16 | export default PodsCount;
17 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | name: kube-state-metrics
10 | roleRef:
11 | apiGroup: rbac.authorization.k8s.io
12 | kind: ClusterRole
13 | name: kube-state-metrics
14 | subjects:
15 | - kind: ServiceAccount
16 | name: kube-state-metrics
17 | namespace: monitoring
18 |
--------------------------------------------------------------------------------
/manifests/prometheus-clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s
11 | namespace: monitoring
12 | rules:
13 | - apiGroups:
14 | - ""
15 | resources:
16 | - nodes/metrics
17 | verbs:
18 | - get
19 | - nonResourceURLs:
20 | - /metrics
21 | verbs:
22 | - get
23 |
--------------------------------------------------------------------------------
/manifests/nodeExporter-clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: node-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 1.3.1
9 | name: node-exporter
10 | namespace: monitoring
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: node-exporter
15 | subjects:
16 | - kind: ServiceAccount
17 | name: node-exporter
18 | namespace: monitoring
19 |
--------------------------------------------------------------------------------
/manifests/prometheusOperator-clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: controller
6 | app.kubernetes.io/name: prometheus-operator
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.53.1
9 | name: prometheus-operator
10 | roleRef:
11 | apiGroup: rbac.authorization.k8s.io
12 | kind: ClusterRole
13 | name: prometheus-operator
14 | subjects:
15 | - kind: ServiceAccount
16 | name: prometheus-operator
17 | namespace: monitoring
18 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-apiService.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apiregistration.k8s.io/v1
2 | kind: APIService
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: v1beta1.metrics.k8s.io
10 | spec:
11 | group: metrics.k8s.io
12 | groupPriorityMinimum: 100
13 | insecureSkipTLSVerify: true
14 | service:
15 | name: prometheus-adapter
16 | namespace: monitoring
17 | version: v1beta1
18 | versionPriority: 100
19 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: blackbox-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.19.0
9 | name: blackbox-exporter
10 | namespace: monitoring
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: blackbox-exporter
15 | subjects:
16 | - kind: ServiceAccount
17 | name: blackbox-exporter
18 | namespace: monitoring
19 |
--------------------------------------------------------------------------------
/manifests/nodeExporter-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: node-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 1.3.1
9 | name: node-exporter
10 | namespace: monitoring
11 | spec:
12 | clusterIP: None
13 | ports:
14 | - name: https
15 | port: 9100
16 | targetPort: https
17 | selector:
18 | app.kubernetes.io/component: exporter
19 | app.kubernetes.io/name: node-exporter
20 | app.kubernetes.io/part-of: kube-prometheus
21 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: prometheus-adapter
15 | subjects:
16 | - kind: ServiceAccount
17 | name: prometheus-adapter
18 | namespace: monitoring
19 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 | spec:
12 | ports:
13 | - name: https
14 | port: 443
15 | targetPort: 6443
16 | selector:
17 | app.kubernetes.io/component: metrics-adapter
18 | app.kubernetes.io/name: prometheus-adapter
19 | app.kubernetes.io/part-of: kube-prometheus
20 |
--------------------------------------------------------------------------------
/manifests/nodeExporter-clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: node-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 1.3.1
9 | name: node-exporter
10 | namespace: monitoring
11 | rules:
12 | - apiGroups:
13 | - authentication.k8s.io
14 | resources:
15 | - tokenreviews
16 | verbs:
17 | - create
18 | - apiGroups:
19 | - authorization.k8s.io
20 | resources:
21 | - subjectaccessreviews
22 | verbs:
23 | - create
24 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-podDisruptionBudget.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: policy/v1
2 | kind: PodDisruptionBudget
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 | spec:
12 | minAvailable: 1
13 | selector:
14 | matchLabels:
15 | app.kubernetes.io/component: metrics-adapter
16 | app.kubernetes.io/name: prometheus-adapter
17 | app.kubernetes.io/part-of: kube-prometheus
18 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalNodes/CPUIntensiveNodes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ComponentWrapper from '../../../utils/ComponentWrapper';
3 | import BarChart from '../Charts/BarChartTemplate';
4 |
5 | // Horizontal bar chart showing CPU usage by node
6 | const CPUIntensiveNodes = ({ nodes }) => {
7 | return (
8 |
9 |
14 |
15 | );
16 | };
17 |
18 | export default CPUIntensiveNodes;
19 |
--------------------------------------------------------------------------------
/client/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import namespaceReducer from './namespace';
3 | import nodesReducer from './nodes';
4 | import podsReducer from './pods';
5 | import deploymentsReducer from './deployments';
6 | import servicesReducer from './services';
7 | import promMetricsReducer from './promMetrics';
8 |
9 | const reducers = combineReducers({
10 | nodes: nodesReducer,
11 | pods: podsReducer,
12 | namespace: namespaceReducer,
13 | deployments: deploymentsReducer,
14 | services: servicesReducer,
15 | promMetrics: promMetricsReducer,
16 | });
17 |
18 | export default reducers;
19 |
--------------------------------------------------------------------------------
/manifests/prometheus-clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s
11 | namespace: monitoring
12 | roleRef:
13 | apiGroup: rbac.authorization.k8s.io
14 | kind: ClusterRole
15 | name: prometheus-k8s
16 | subjects:
17 | - kind: ServiceAccount
18 | name: prometheus-k8s
19 | namespace: monitoring
20 |
--------------------------------------------------------------------------------
/manifests/prometheus-roleBindingConfig.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s-config
11 | namespace: monitoring
12 | roleRef:
13 | apiGroup: rbac.authorization.k8s.io
14 | kind: Role
15 | name: prometheus-k8s-config
16 | subjects:
17 | - kind: ServiceAccount
18 | name: prometheus-k8s
19 | namespace: monitoring
20 |
--------------------------------------------------------------------------------
/client/actions/constants/actionTypes.js:
--------------------------------------------------------------------------------
1 | // export const first = 'first'
2 | // export const first = 'first'
3 | // export const first = 'first'
4 | // export const first = 'first'
5 |
6 | export const FETCH_NODES_LIST = 'FETCH_NODES_LIST';
7 |
8 | export const FETCH_PODS_LIST = 'FETCH_PODS_LIST';
9 |
10 | export const FETCH_SERVICES_LIST = 'FETCH_SERVICES_LIST';
11 |
12 | export const FETCH_DEPLOYMENTS_LIST = 'FETCH_DEPLOYMENTS_LIST';
13 |
14 | export const SET_NAMESPACE = 'SET_NAMESPACE';
15 |
16 | export const FETCH_NAMESPACES_LIST = 'FETCH_NAMESPACES_LIST';
17 |
18 | export const FETCH_PROM_METRICS = 'FETCH_PROM_METRICS';
19 |
--------------------------------------------------------------------------------
/client/homepage/containers/ProblematicContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ProblematicNodes from '../components/Problematic/ProblematicNodes';
3 | import ProblematicPods from '../components/Problematic/ProblematicPods';
4 | import { Grid } from '@mui/material';
5 |
6 | const ProblematicContainer = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default ProblematicContainer;
20 |
--------------------------------------------------------------------------------
/manifests/prometheusOperator-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: controller
6 | app.kubernetes.io/name: prometheus-operator
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.53.1
9 | name: prometheus-operator
10 | namespace: monitoring
11 | spec:
12 | clusterIP: None
13 | ports:
14 | - name: https
15 | port: 8443
16 | targetPort: https
17 | selector:
18 | app.kubernetes.io/component: controller
19 | app.kubernetes.io/name: prometheus-operator
20 | app.kubernetes.io/part-of: kube-prometheus
21 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-roleBindingAuthReader.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: resource-metrics-auth-reader
10 | namespace: kube-system
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: Role
14 | name: extension-apiserver-authentication-reader
15 | subjects:
16 | - kind: ServiceAccount
17 | name: prometheus-adapter
18 | namespace: monitoring
19 |
--------------------------------------------------------------------------------
/client/homepage/components/Counts/DeploymentsCount.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Card, Typography } from '@mui/material';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 |
5 | // Displays count of deployments in cluster
6 | function DeploymentsCount(props) {
7 | const { deployments } = props;
8 | const deploymentsCount = deployments.length ? deployments.length : '-';
9 | return (
10 |
11 | {deploymentsCount}
12 |
13 | );
14 | }
15 |
16 | export default DeploymentsCount;
17 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-clusterRoleBindingDelegator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: resource-metrics:system:auth-delegator
10 | namespace: monitoring
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: system:auth-delegator
15 | subjects:
16 | - kind: ServiceAccount
17 | name: prometheus-adapter
18 | namespace: monitoring
19 |
--------------------------------------------------------------------------------
/client/homepage/components/Counts/ServicesCount.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Card from '@mui/material/Card';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 | import { Typography } from '@mui/material';
5 |
6 | // Displays count of services in cluster
7 | function ServicesCount(props) {
8 | const { services } = props;
9 | const servicesCount = services.length ? services.length : '-';
10 | return (
11 |
12 | {servicesCount}
13 |
14 | );
15 | }
16 |
17 | export default ServicesCount;
18 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalPods/CPUIntensivePods.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BarChart from '../Charts/BarChartTemplate';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 | import { MAX_SERIES } from '../../../actions/constants/chartConstants';
5 |
6 | const CPUIntensivePods = ({ pods }) => {
7 | return (
8 |
9 |
14 |
15 | );
16 | };
17 |
18 | export default CPUIntensivePods;
19 |
--------------------------------------------------------------------------------
/client/reducers/namespace.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCH_NAMESPACES_LIST,
3 | SET_NAMESPACE,
4 | } from '../actions/constants/actionTypes';
5 |
6 | const initialState = {
7 | selectedNamespace: 'All',
8 | namespacesList: [],
9 | };
10 |
11 | const namespaceReducer = (state = initialState, action) => {
12 | switch (action.type) {
13 | case SET_NAMESPACE: {
14 | return { ...state, selectedNamespace: action.payload };
15 | }
16 | case FETCH_NAMESPACES_LIST: {
17 | return { ...state, namespacesList: action.payload };
18 | }
19 | default: {
20 | return state;
21 | }
22 | }
23 | };
24 |
25 | export default namespaceReducer;
26 |
--------------------------------------------------------------------------------
/manifests/prometheus-podDisruptionBudget.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: policy/v1
2 | kind: PodDisruptionBudget
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s
11 | namespace: monitoring
12 | spec:
13 | minAvailable: 1
14 | selector:
15 | matchLabels:
16 | app.kubernetes.io/component: prometheus
17 | app.kubernetes.io/instance: k8s
18 | app.kubernetes.io/name: prometheus
19 | app.kubernetes.io/part-of: kube-prometheus
20 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: blackbox-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.19.0
9 | name: blackbox-exporter
10 | namespace: monitoring
11 | spec:
12 | ports:
13 | - name: https
14 | port: 9115
15 | targetPort: https
16 | - name: probe
17 | port: 19115
18 | targetPort: http
19 | selector:
20 | app.kubernetes.io/component: exporter
21 | app.kubernetes.io/name: blackbox-exporter
22 | app.kubernetes.io/part-of: kube-prometheus
23 |
--------------------------------------------------------------------------------
/manifests/alertmanager-podDisruptionBudget.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: policy/v1
2 | kind: PodDisruptionBudget
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: alert-router
6 | app.kubernetes.io/instance: main
7 | app.kubernetes.io/name: alertmanager
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 0.23.0
10 | name: alertmanager-main
11 | namespace: monitoring
12 | spec:
13 | maxUnavailable: 1
14 | selector:
15 | matchLabels:
16 | app.kubernetes.io/component: alert-router
17 | app.kubernetes.io/instance: main
18 | app.kubernetes.io/name: alertmanager
19 | app.kubernetes.io/part-of: kube-prometheus
20 |
--------------------------------------------------------------------------------
/coverage/lcov-report/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
2 |
--------------------------------------------------------------------------------
/client/homepage/containers/StatusContainer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Grid, Box } from '@mui/material';
3 | import NodesStatus from '../components/Statuses/NodesStatus';
4 | import PodsStatus from '../components/Statuses/PodsStatus';
5 |
6 | const StatusContainer = (props) => {
7 | const { nodes, pods, services } = props;
8 |
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default StatusContainer;
22 |
--------------------------------------------------------------------------------
/client/metricspage/components/PodMetrics/FreeMemoryByPod.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 |
4 | const FreeMemoryByPod = ({ metrics }) => {
5 | /*
6 | Renders Free Memory Per Node line chart on the Metrics Page
7 | */
8 | return (
9 |
10 |
11 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default FreeMemoryByPod;
23 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalNodes/BytesReceivedPerNode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ComponentWrapper from '../../../utils/ComponentWrapper';
3 | import BarChart from '../Charts/BarChartTemplate';
4 |
5 | // Horizontal bar chart of bytes received per node
6 | const BytesReceivedPerNode = ({ promMetrics }) => {
7 | return (
8 |
9 |
14 |
15 | );
16 | };
17 |
18 | export default BytesReceivedPerNode;
19 |
--------------------------------------------------------------------------------
/client/homepage/components/Counts/NodesCount.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Card, Typography } from '@mui/material';
4 | import ComponentWrapper from '../../../utils/ComponentWrapper';
5 |
6 | const mapStateToProps = ({ nodes }) => {
7 | return nodes;
8 | };
9 |
10 | // Displays count of nodes in cluster
11 | function NodesCount({ items }) {
12 | const nodesCount = items.length ? items.length : '-';
13 | return (
14 |
15 | {nodesCount}
16 |
17 | );
18 | }
19 |
20 | export default connect(mapStateToProps)(NodesCount);
21 |
--------------------------------------------------------------------------------
/fabric8-rbac.yaml:
--------------------------------------------------------------------------------
1 | # NOTE: The service account `default:default` already exists in k8s cluster.
2 | # You can create a new account following like this:
3 | #---
4 | #apiVersion: v1
5 | #kind: ServiceAccount
6 | #metadata:
7 | # name:
8 | # namespace:
9 |
10 | ---
11 | apiVersion: rbac.authorization.k8s.io/v1
12 | kind: ClusterRoleBinding
13 | metadata:
14 | name: fabric8-rbac
15 | subjects:
16 | - kind: ServiceAccount
17 | # Reference to upper's `metadata.name`
18 | name: default
19 | # Reference to upper's `metadata.namespace`
20 | namespace: default
21 | roleRef:
22 | kind: ClusterRole
23 | name: cluster-admin
24 | apiGroup: rbac.authorization.k8s.io
25 |
--------------------------------------------------------------------------------
/client/utils/constants/index.js:
--------------------------------------------------------------------------------
1 | /** TODO: Make sure all bar charts import MAX_SERIES from this file and remove actions/constants/chartConstants.js */
2 | export const MAX_SERIES = 5;
3 |
4 | export const POD_STATUS = {
5 | Pending:
6 | 'The pod is waiting to get scheduled on a node, or for at least one of its containers to initialize.',
7 | Running:
8 | 'The pod has been assigned to a node and has one or more running containers.',
9 | Succeeded: 'All of the pod’s containers exited without errors.',
10 | Failed: 'One or more of the pod’s containers terminated with an error.',
11 | Unknown:
12 | 'Usually occurs when the Kubernetes API server could not communicate with the pod’s node.',
13 | };
14 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | name: kube-state-metrics
10 | namespace: monitoring
11 | spec:
12 | clusterIP: None
13 | ports:
14 | - name: https-main
15 | port: 8443
16 | targetPort: https-main
17 | - name: https-self
18 | port: 9443
19 | targetPort: https-self
20 | selector:
21 | app.kubernetes.io/component: exporter
22 | app.kubernetes.io/name: kube-state-metrics
23 | app.kubernetes.io/part-of: kube-prometheus
24 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalNodes/BytesTransmittedPerNode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ComponentWrapper from '../../../utils/ComponentWrapper';
3 | import BarChart from '../Charts/BarChartTemplate';
4 |
5 | // Horizontal bar chart of bytes transmitted per node
6 | const BytesTransmittedPerNode = ({ promMetrics }) => {
7 | return (
8 |
9 |
14 |
15 | );
16 | };
17 |
18 | export default BytesTransmittedPerNode;
19 |
--------------------------------------------------------------------------------
/manifests/kubernetesControlPlane-serviceMonitorKubeScheduler.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: kube-scheduler
6 | app.kubernetes.io/part-of: kube-prometheus
7 | name: kube-scheduler
8 | namespace: monitoring
9 | spec:
10 | endpoints:
11 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
12 | interval: 30s
13 | port: https-metrics
14 | scheme: https
15 | tlsConfig:
16 | insecureSkipVerify: true
17 | jobLabel: app.kubernetes.io/name
18 | namespaceSelector:
19 | matchNames:
20 | - kube-system
21 | selector:
22 | matchLabels:
23 | app.kubernetes.io/name: kube-scheduler
24 |
--------------------------------------------------------------------------------
/manifests/kubernetesControlPlane-serviceMonitorCoreDNS.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: coredns
6 | app.kubernetes.io/part-of: kube-prometheus
7 | name: coredns
8 | namespace: monitoring
9 | spec:
10 | endpoints:
11 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
12 | interval: 15s
13 | metricRelabelings:
14 | - action: drop
15 | regex: coredns_cache_misses_total
16 | sourceLabels:
17 | - __name__
18 | port: metrics
19 | jobLabel: app.kubernetes.io/name
20 | namespaceSelector:
21 | matchNames:
22 | - kube-system
23 | selector:
24 | matchLabels:
25 | k8s-app: kube-dns
26 |
--------------------------------------------------------------------------------
/manifests/prometheus-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s
11 | namespace: monitoring
12 | spec:
13 | endpoints:
14 | - interval: 30s
15 | port: web
16 | - interval: 30s
17 | port: reloader-web
18 | selector:
19 | matchLabels:
20 | app.kubernetes.io/component: prometheus
21 | app.kubernetes.io/instance: k8s
22 | app.kubernetes.io/name: prometheus
23 | app.kubernetes.io/part-of: kube-prometheus
24 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-clusterRoleAggregatedMetricsReader.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | rbac.authorization.k8s.io/aggregate-to-admin: "true"
10 | rbac.authorization.k8s.io/aggregate-to-edit: "true"
11 | rbac.authorization.k8s.io/aggregate-to-view: "true"
12 | name: system:aggregated-metrics-reader
13 | namespace: monitoring
14 | rules:
15 | - apiGroups:
16 | - metrics.k8s.io
17 | resources:
18 | - pods
19 | - nodes
20 | verbs:
21 | - get
22 | - list
23 | - watch
24 |
--------------------------------------------------------------------------------
/client/metricspage/components/PodMetrics/CPUUsageByPod.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const CPUUsageByPod = ({ metrics }) => {
6 | /*
7 | Renders CPU Usage % by Pod line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default CPUUsageByPod;
24 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalNodes/MemoryIntensiveNodes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ComponentWrapper from '../../../utils/ComponentWrapper';
3 | import BarChart from '../Charts/BarChartTemplate';
4 |
5 | // Horizontal bar chart showing memory usage bby node
6 | const MemoryIntensiveNodes = ({ nodes }) => {
7 | const unitConverted = nodes.map((el) => {
8 | return { ...el, data: el.data / 1000000000 };
9 | });
10 | return (
11 |
12 |
17 |
18 | );
19 | };
20 |
21 | export default MemoryIntensiveNodes;
22 |
--------------------------------------------------------------------------------
/manifests/alertmanager-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: alert-router
6 | app.kubernetes.io/instance: main
7 | app.kubernetes.io/name: alertmanager
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 0.23.0
10 | name: alertmanager-main
11 | namespace: monitoring
12 | spec:
13 | endpoints:
14 | - interval: 30s
15 | port: web
16 | - interval: 30s
17 | port: reloader-web
18 | selector:
19 | matchLabels:
20 | app.kubernetes.io/component: alert-router
21 | app.kubernetes.io/instance: main
22 | app.kubernetes.io/name: alertmanager
23 | app.kubernetes.io/part-of: kube-prometheus
24 |
--------------------------------------------------------------------------------
/manifests/prometheus-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: prometheus-k8s
11 | namespace: monitoring
12 | spec:
13 | ports:
14 | - name: web
15 | port: 9090
16 | targetPort: web
17 | - name: reloader-web
18 | port: 8080
19 | targetPort: reloader-web
20 | selector:
21 | app.kubernetes.io/component: prometheus
22 | app.kubernetes.io/instance: k8s
23 | app.kubernetes.io/name: prometheus
24 | app.kubernetes.io/part-of: kube-prometheus
25 | sessionAffinity: ClientIP
26 |
--------------------------------------------------------------------------------
/client/metricspage/components/NodeMetrics/CPUUsageByNode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const CPUUsageByNode = ({ metrics }) => {
6 | /*
7 | Renders the CPU Usage % Per Node line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default CPUUsageByNode;
24 |
--------------------------------------------------------------------------------
/manifests/alertmanager-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: alert-router
6 | app.kubernetes.io/instance: main
7 | app.kubernetes.io/name: alertmanager
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 0.23.0
10 | name: alertmanager-main
11 | namespace: monitoring
12 | spec:
13 | ports:
14 | - name: web
15 | port: 9093
16 | targetPort: web
17 | - name: reloader-web
18 | port: 8080
19 | targetPort: reloader-web
20 | selector:
21 | app.kubernetes.io/component: alert-router
22 | app.kubernetes.io/instance: main
23 | app.kubernetes.io/name: alertmanager
24 | app.kubernetes.io/part-of: kube-prometheus
25 | sessionAffinity: ClientIP
26 |
--------------------------------------------------------------------------------
/server/controllers/alertsController.js:
--------------------------------------------------------------------------------
1 | const fetch = (...args) =>
2 | import('node-fetch').then(({ default: fetch }) => fetch(...args));
3 |
4 | const { prometheusURL } = require('../utils/constants');
5 |
6 | // const prometheusURL = 'http://127.0.0.1:9090/api/v1/';
7 |
8 | const alertsController = {};
9 |
10 | // fetch alerts from Prometheus for Alerts page
11 | alertsController.fetchAlerts = async (req, res, next) => {
12 | try {
13 | const data = await fetch(`${prometheusURL}/rules`);
14 | res.locals.alerts = await data.json();
15 | return next();
16 | } catch (err) {
17 | return next({
18 | log: 'Error in alertsController.fetchAlerts',
19 | message: { err: err.message },
20 | });
21 | }
22 | };
23 |
24 | module.exports = alertsController;
25 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalPods/MemoryIntensivePods.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BarChart from '../Charts/BarChartTemplate';
3 | import { MAX_SERIES } from '../../../actions/constants/chartConstants';
4 | import ComponentWrapper from '../../../utils/ComponentWrapper';
5 |
6 | const MemoryIntensivePods = ({ pods }) => {
7 | const unitConverted = pods.map((el) => {
8 | return { ...el, data: el.data / 1000000000 };
9 | });
10 | return (
11 |
12 |
17 |
18 | );
19 | };
20 |
21 | export default MemoryIntensivePods;
22 |
--------------------------------------------------------------------------------
/server/controllers/logsController.js:
--------------------------------------------------------------------------------
1 | const cmd = require('node-cmd');
2 | const logsController = {};
3 | const formatLogs = require('../utils/formatLogs');
4 |
5 | const HEADERS = [
6 | 'NAMESPACE',
7 | 'LAST SEEN',
8 | 'TYPE',
9 | 'REASON',
10 | 'OBJECT',
11 | 'MESSAGE',
12 | ];
13 |
14 | // get logs for logs page through kubectl
15 | logsController.getLogs = (req, res, next) => {
16 | try {
17 | const rawLogs = cmd
18 | .runSync('kubectl get events --all-namespaces')
19 | .data.split('\n');
20 | const formattedLogs = formatLogs(rawLogs);
21 | res.locals.logs = formattedLogs;
22 | return next();
23 | } catch (err) {
24 | next({ logs: 'Error with getting logs', message: { err: err.message } });
25 | }
26 | };
27 |
28 | module.exports = logsController;
29 |
--------------------------------------------------------------------------------
/client/metricspage/components/NamespaceMetrics/CPUUsageByNamespace.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const CPUUsageByNamespace = ({ metrics }) => {
6 | /*
7 | Renders CPU Usage % by Namespace line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default CPUUsageByNamespace;
24 |
--------------------------------------------------------------------------------
/manifests/grafana-dashboardSources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | data:
3 | dashboards.yaml: |-
4 | {
5 | "apiVersion": 1,
6 | "providers": [
7 | {
8 | "folder": "Default",
9 | "folderUid": "",
10 | "name": "0",
11 | "options": {
12 | "path": "/grafana-dashboard-definitions/0"
13 | },
14 | "orgId": 1,
15 | "type": "file"
16 | }
17 | ]
18 | }
19 | kind: ConfigMap
20 | metadata:
21 | labels:
22 | app.kubernetes.io/component: grafana
23 | app.kubernetes.io/name: grafana
24 | app.kubernetes.io/part-of: kube-prometheus
25 | app.kubernetes.io/version: 8.3.3
26 | name: grafana-dashboards
27 | namespace: monitoring
28 |
--------------------------------------------------------------------------------
/client/metricspage/components/PodMetrics/BytesReceivedByPod.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const BytesReceivedByPod = ({ metrics }) => {
6 | /*
7 | Renders the Network IO (Bps) Received by Pod line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default BytesReceivedByPod;
24 |
--------------------------------------------------------------------------------
/client/metricspage/components/NodeMetrics/BytesReceivedByNode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const BytesReceivedByNode = ({ metrics }) => {
6 | /*
7 | Renders the Network IO (Bps) Received by Node line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default BytesReceivedByNode;
24 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: blackbox-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.19.0
9 | name: blackbox-exporter
10 | namespace: monitoring
11 | spec:
12 | endpoints:
13 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
14 | interval: 30s
15 | path: /metrics
16 | port: https
17 | scheme: https
18 | tlsConfig:
19 | insecureSkipVerify: true
20 | selector:
21 | matchLabels:
22 | app.kubernetes.io/component: exporter
23 | app.kubernetes.io/name: blackbox-exporter
24 | app.kubernetes.io/part-of: kube-prometheus
25 |
--------------------------------------------------------------------------------
/client/metricspage/components/PodMetrics/BytesTransmittedbyPod.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const BytesTransmittedbyPod = ({ metrics }) => {
6 | /*
7 | Renders Network IO (Bps) Transmitted by Pod line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default BytesTransmittedbyPod;
24 |
--------------------------------------------------------------------------------
/manifests/grafana-dashboardDatasources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: grafana
6 | app.kubernetes.io/name: grafana
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 8.3.3
9 | name: grafana-datasources
10 | namespace: monitoring
11 | stringData:
12 | datasources.yaml: |-
13 | {
14 | "apiVersion": 1,
15 | "datasources": [
16 | {
17 | "access": "proxy",
18 | "editable": false,
19 | "name": "prometheus",
20 | "orgId": 1,
21 | "type": "prometheus",
22 | "url": "http://prometheus-k8s.monitoring.svc:9090",
23 | "version": 1
24 | }
25 | ]
26 | }
27 | type: Opaque
28 |
--------------------------------------------------------------------------------
/client/metricspage/components/NodeMetrics/BytesTransmittedByNode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const BytesTransmittedByNode = ({ metrics }) => {
6 | /*
7 | Renders the Network IO (Bps) Transmitted by Node line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default BytesTransmittedByNode;
24 |
--------------------------------------------------------------------------------
/infra/alertmanager/alertmanager.yml:
--------------------------------------------------------------------------------
1 | route:
2 | group_by: [alertname]
3 | receiver: 'mail' # default receiver
4 | repeat_interval: 24h
5 | routes:
6 | - receiver: 'teams'
7 | repeat_interval: 12h
8 | matchers:
9 | - severity="medium"
10 |
11 | - receiver: 'teams'
12 | repeat_interval: 4h
13 | matchers:
14 | - severity="high"
15 |
16 | receivers:
17 | - name: 'mail'
18 | email_configs:
19 | - smarthost: 'yourmailhost.com:465'
20 | auth_username: 'yourmail@yourmailhost.com'
21 | auth_password: 'your mail password'
22 | from: 'yourmail@yourmailhost.com'
23 | to: 'someonesmail@yourmailhost.com'
24 | require_tls: false
25 |
26 | - name: 'teams'
27 | webhook_configs:
28 | - url: 'http://prom2teams:8089'
29 | send_resolved: true
30 |
--------------------------------------------------------------------------------
/manifests/prometheusOperator-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: controller
6 | app.kubernetes.io/name: prometheus-operator
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.53.1
9 | name: prometheus-operator
10 | namespace: monitoring
11 | spec:
12 | endpoints:
13 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
14 | honorLabels: true
15 | port: https
16 | scheme: https
17 | tlsConfig:
18 | insecureSkipVerify: true
19 | selector:
20 | matchLabels:
21 | app.kubernetes.io/component: controller
22 | app.kubernetes.io/name: prometheus-operator
23 | app.kubernetes.io/part-of: kube-prometheus
24 | app.kubernetes.io/version: 0.53.1
25 |
--------------------------------------------------------------------------------
/client/metricspage/components/NamespaceMetrics/BytesTransmittedByNamespace.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const BytesTransmittedByNamespace = ({ metrics }) => {
6 | /*
7 | Renders Network IO (Bps) Transmitted by Namespace line chart on the Metrics Page
8 | */
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default BytesTransmittedByNamespace;
24 |
--------------------------------------------------------------------------------
/client/homepage/components/Utilizations/CpuTotal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { Card, Typography } from '@mui/material';
4 | import ComponentWrapper from '../../../utils/ComponentWrapper';
5 |
6 | // CPU used out of total
7 | const CpuTotal = ({ cpu, cpuusage }) => {
8 | return (
9 | <>
10 |
11 | {cpu * (cpuusage / 100)}
12 |
13 | used
14 |
15 | {cpu}
16 |
17 | total
18 |
19 |
20 | >
21 | );
22 | };
23 |
24 | export default CpuTotal;
25 |
--------------------------------------------------------------------------------
/client/metricspage/components/NamespaceMetrics/BytesReceivedByNamespace.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 | import { Button } from '@mui/material';
5 |
6 | const BytesReceivedByNamespace = ({ metrics }) => {
7 | /*
8 | Renders the Network IO (Bps) Received by Namespace line chart on the Metrics Page
9 | */
10 | return (
11 |
12 |
13 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default BytesReceivedByNamespace;
25 |
--------------------------------------------------------------------------------
/client/homepage/containers/CountsContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Grid } from '@mui/material';
3 | import NodesCount from '../components/Counts/NodesCount';
4 | import DeploymentsCount from '../components/Counts/DeploymentsCount';
5 | import PodsCount from '../components/Counts/PodsCount';
6 | import ServicesCount from '../components/Counts/ServicesCount';
7 |
8 | const CountsContainer = (props) => {
9 | const { nodes, deployments, pods, services } = props;
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default CountsContainer;
29 |
--------------------------------------------------------------------------------
/manifests/nodeExporter-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: node-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 1.3.1
9 | name: node-exporter
10 | namespace: monitoring
11 | spec:
12 | endpoints:
13 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
14 | interval: 15s
15 | port: https
16 | relabelings:
17 | - action: replace
18 | regex: (.*)
19 | replacement: $1
20 | sourceLabels:
21 | - __meta_kubernetes_pod_node_name
22 | targetLabel: instance
23 | scheme: https
24 | tlsConfig:
25 | insecureSkipVerify: true
26 | jobLabel: app.kubernetes.io/name
27 | selector:
28 | matchLabels:
29 | app.kubernetes.io/component: exporter
30 | app.kubernetes.io/name: node-exporter
31 | app.kubernetes.io/part-of: kube-prometheus
32 |
--------------------------------------------------------------------------------
/client/homepage/components/Utilizations/MemoryTotal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { Card, Typography } from '@mui/material';
4 | import ComponentWrapper from '../../../utils/ComponentWrapper';
5 |
6 | const MemoryTotal = ({ memory, memusage }) => {
7 | let memoryinGb;
8 | if (memory) {
9 | memoryinGb = Number((memory / 10 ** 9).toFixed(0));
10 | }
11 |
12 | return (
13 | <>
14 |
15 | {memoryinGb}
16 |
17 | used
18 |
19 |
20 | {(memoryinGb / (memusage / 100)).toFixed(0)}
21 |
22 |
23 | total
24 |
25 |
26 | >
27 | );
28 | };
29 |
30 | export default MemoryTotal;
31 |
--------------------------------------------------------------------------------
/server/utils/formatTimeToAvg.js:
--------------------------------------------------------------------------------
1 | const formatChartData = require('./formatChartData');
2 |
3 | // Format time series data from Prometheus as averages over period of time
4 | function formatTimeToAvg(resp) {
5 | const formatted = formatChartData(resp.data.result);
6 | const seriesValues = formatted.seriesValues;
7 | const newValues = [];
8 |
9 | // Take an average of time series data over the length of the array
10 | seriesValues.forEach((value) => {
11 | const converted = value.map((el) => parseFloat(el));
12 | newValues.push(converted.reduce((a, b) => a + b) / converted.length);
13 | });
14 | formatted.seriesValues = newValues;
15 | const resultArr = [];
16 |
17 | // Reformat averaged result with label and data keys for bar graphs on client side
18 | for (let i = 0; i < formatted.seriesLabels.length; i++) {
19 | resultArr.push({
20 | label: formatted.seriesLabels[i],
21 | data: formatted.seriesValues[i],
22 | });
23 | }
24 | return resultArr;
25 | }
26 |
27 | module.exports = formatTimeToAvg;
28 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 | spec:
12 | endpoints:
13 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
14 | interval: 30s
15 | metricRelabelings:
16 | - action: drop
17 | regex: (apiserver_client_certificate_.*|apiserver_envelope_.*|apiserver_flowcontrol_.*|apiserver_storage_.*|apiserver_webhooks_.*|workqueue_.*)
18 | sourceLabels:
19 | - __name__
20 | port: https
21 | scheme: https
22 | tlsConfig:
23 | insecureSkipVerify: true
24 | selector:
25 | matchLabels:
26 | app.kubernetes.io/component: metrics-adapter
27 | app.kubernetes.io/name: prometheus-adapter
28 | app.kubernetes.io/part-of: kube-prometheus
29 |
--------------------------------------------------------------------------------
/client/homepage/components/CriticalNodes/ProblematicItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ListItemButton, List, Typography, ListItemText } from '@mui/material';
3 |
4 | // Component used within ProblematicNodes and ProblematicPods to render each status element
5 | function ProblematicItem({ name, conditions }) {
6 | const conditionsText = () => {
7 | return conditions
8 | .sort(
9 | (a, b) =>
10 | Date.parse(a.lastTransitionTime) - Date.parse(b.lastTransitionTime)
11 | )
12 | .map((c) => (
13 | {`${c.type}: ${
14 | c.status
15 | } at ${c.lastTransitionTime.toString()}`}
16 | ));
17 | };
18 |
19 | return (
20 |
21 |
22 |
26 |
27 |
28 | );
29 | }
30 |
31 | export default ProblematicItem;
32 |
--------------------------------------------------------------------------------
/server/utils/metricsFetch.js:
--------------------------------------------------------------------------------
1 | const formatChartData = require('./formatChartData');
2 | const fetch = (...args) =>
3 | import('node-fetch').then(({ default: fetch }) => fetch(...args));
4 |
5 | // Abstracted middleware function that executes metrics queries, formats response to chart data, and stores result in res.locals
6 | const metricsFetch = (query, title, req, res, next) => {
7 | try {
8 | fetch(query)
9 | .then((resp) => resp.json())
10 | // Return formatted data and query string on res.locals
11 | .then((resp) => {
12 | res.locals[title] = {};
13 | if (resp.data.result.length !== 0) {
14 | const formatted = formatChartData(resp.data.result);
15 | res.locals[title].data = formatted;
16 | } else res.locals[title].data = null;
17 | res.locals[title].queryString = query;
18 | })
19 | .then(() => next());
20 | } catch (err) {
21 | next({
22 | log: `Error with ${title}`,
23 | message: { err: err.message },
24 | });
25 | }
26 | };
27 |
28 | module.exports = metricsFetch;
29 |
--------------------------------------------------------------------------------
/client/metricspage/components/PodMetrics/MemoryUsageByPod.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const MemoryUsageByPod = ({ metrics }) => {
6 | /*
7 | Converts Memory Usage from Bytes to GB
8 | */
9 | if (metrics.data) {
10 | for (let i = 0; i < metrics.data.seriesValues.length; i++) {
11 | metrics.data.seriesValues[i] = metrics.data.seriesValues[i].map((el) => {
12 | return el / 1000000000;
13 | });
14 | }
15 | }
16 | /*
17 | Renders Memory Usage (Gb) by Pod line chart on the Metrics Page
18 | */
19 | return (
20 |
21 |
22 |
28 |
29 |
30 | );
31 | };
32 |
33 | export default MemoryUsageByPod;
34 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:16 as kubebuild
2 | RUN apt-get update && apt-get install -y apt-transport-https ca-certificates curl
3 | RUN curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
4 | RUN echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list
5 | RUN apt-get update && apt-get install -y kubectl
6 |
7 |
8 |
9 | #ADD https://storage.googleapis.com/kubernetes-release/release/v1.6.4/bin/linux/amd64/kubectl /usr/local/bin/kubectl
10 | #ENV HOME=/config \
11 | # KUBECONFIG=/etc/kubernetes/admin.conf
12 | #RUN set -x && \
13 | # apk add --no-cache curl ca-certificates && \
14 | # chmod +x /usr/local/bin/kubectl
15 |
16 | #your app container
17 | FROM kubebuild as armada
18 |
19 | WORKDIR /usr/src/app
20 |
21 | RUN npm install -g webpack nodemon
22 |
23 | COPY . /usr/src/app/
24 |
25 | RUN npm install
26 |
27 | RUN npm run build
28 |
29 | EXPOSE 3001
30 |
31 | # CMD [ "npm", "run", "dev" ]
32 | CMD [ "npm", "run", "start" ]
33 |
--------------------------------------------------------------------------------
/client/metricspage/components/NodeMetrics/MemoryUsageByNode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const MemoryUsageByNode = ({ metrics }) => {
6 | /*
7 | Converts Memory Usage from Bytes to GB
8 | */
9 | if (metrics.data) {
10 | for (let i = 0; i < metrics.data.seriesValues.length; i++) {
11 | metrics.data.seriesValues[i] = metrics.data.seriesValues[i].map((el) => {
12 | return el / 1000000000;
13 | });
14 | }
15 | }
16 |
17 | /*
18 | Renders the Memory Usage (Gb) By Node line chart on the Metrics Page
19 | */
20 |
21 | return (
22 |
23 |
24 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default MemoryUsageByNode;
36 |
--------------------------------------------------------------------------------
/client/homepage/components/Refresh.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Box, Typography, Button } from '@mui/material';
3 |
4 | // Refresh omponent to refetch data. Passed down handleRefresh from MainContainer
5 | const Refresh = ({ handleRefresh, lastUpdated }) => {
6 | useEffect(() => {}, [handleRefresh, lastUpdated]);
7 | return (
8 |
11 |
16 | Last updated at {Date(lastUpdated).toString()}
17 |
18 |
30 |
31 | );
32 | };
33 |
34 | export default Refresh;
35 |
--------------------------------------------------------------------------------
/client/logspage/containers/LogsContainer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import LogsTable from '../components/LogsTable';
4 |
5 | function LogsContainer({ namespace }) {
6 | const [logs, setLogs] = useState([]);
7 | const getLogs = () => {
8 | fetch('/api/logs')
9 | .then((res) => res.json())
10 | .then((data) => setLogs(data))
11 | .catch((err) => console.log(err));
12 | };
13 | useEffect(() => {
14 | getLogs();
15 | }, [namespace]);
16 |
17 | const filterByNamespace = () => {
18 | if (namespace !== 'All' && namespace !== '') {
19 | return logs.filter((log) => log.namespace === namespace);
20 | }
21 | return logs;
22 | };
23 | // console.log('logs filtered', filterByNamespace());
24 | return ;
25 | }
26 |
27 | const mapStateToProps = ({ namespace }) => {
28 | console.log('namespace', namespace);
29 | return { namespace: namespace.selectedNamespace };
30 | };
31 |
32 | export default connect(mapStateToProps)(LogsContainer);
33 |
--------------------------------------------------------------------------------
/manifests/alertmanager-alertmanager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: Alertmanager
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: alert-router
6 | app.kubernetes.io/instance: main
7 | app.kubernetes.io/name: alertmanager
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 0.23.0
10 | name: main
11 | namespace: monitoring
12 | spec:
13 | image: quay.io/prometheus/alertmanager:v0.23.0
14 | nodeSelector:
15 | kubernetes.io/os: linux
16 | podMetadata:
17 | labels:
18 | app.kubernetes.io/component: alert-router
19 | app.kubernetes.io/instance: main
20 | app.kubernetes.io/name: alertmanager
21 | app.kubernetes.io/part-of: kube-prometheus
22 | app.kubernetes.io/version: 0.23.0
23 | replicas: 3
24 | resources:
25 | limits:
26 | cpu: 100m
27 | memory: 100Mi
28 | requests:
29 | cpu: 4m
30 | memory: 100Mi
31 | securityContext:
32 | fsGroup: 2000
33 | runAsNonRoot: true
34 | runAsUser: 1000
35 | serviceAccountName: alertmanager-main
36 | version: 0.23.0
37 |
--------------------------------------------------------------------------------
/client/metricspage/components/NamespaceMetrics/MemoryUsageByNamespace.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LineChart from '../../../homepage/components/Charts/LineChartTemplate';
3 | import MetricsComponentWrapper from '../../../utils/MetricsComponentWrapper';
4 |
5 | const MemoryUsageByNamespace = ({ metrics }) => {
6 | /*
7 | Converts Memory Usage from Bytes to GB
8 | */
9 | if (metrics.data) {
10 | for (let i = 0; i < metrics.data.seriesValues.length; i++) {
11 | metrics.data.seriesValues[i] = metrics.data.seriesValues[i].map((el) => {
12 | return el / 1000000000;
13 | });
14 | }
15 | }
16 | /*
17 | Renders Memory Usage (Gb) by Namespace line chart on the Metrics Page
18 | */
19 | return (
20 |
21 |
22 |
28 |
29 |
30 | );
31 | };
32 |
33 | export default MemoryUsageByNamespace;
34 |
--------------------------------------------------------------------------------
/client/actions/actions.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCH_NODES_LIST,
3 | FETCH_PODS_LIST,
4 | SET_NAMESPACE,
5 | FETCH_DEPLOYMENTS_LIST,
6 | FETCH_NAMESPACES_LIST,
7 | FETCH_SERVICES_LIST,
8 | FETCH_PROM_METRICS,
9 | } from './constants/actionTypes';
10 |
11 | export const first = (payload) => ({
12 | type: second,
13 | payload,
14 | });
15 |
16 | export const fetchNodesList = (payload) => ({
17 | type: FETCH_NODES_LIST,
18 | payload,
19 | });
20 |
21 | export const fetchPodsList = (payload) => ({
22 | type: FETCH_PODS_LIST,
23 | payload,
24 | });
25 |
26 | export const setNamespace = (payload) => ({
27 | type: SET_NAMESPACE,
28 | payload,
29 | });
30 |
31 | export const fetchNamespacesList = (payload) => ({
32 | type: FETCH_NAMESPACES_LIST,
33 | payload,
34 | });
35 |
36 | export const fetchServicesList = (payload) => ({
37 | type: FETCH_SERVICES_LIST,
38 | payload,
39 | });
40 |
41 | export const fetchDeploymentsList = (payload) => ({
42 | type: FETCH_DEPLOYMENTS_LIST,
43 | payload,
44 | });
45 |
46 | export const fetchPromMetrics = (payload) => ({
47 | type: FETCH_PROM_METRICS,
48 | payload,
49 | });
50 |
--------------------------------------------------------------------------------
/client/custompage/components/StepBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@mui/material/IconButton';
3 | import TextField from '@mui/material/TextField';
4 | import { Autocomplete } from '@mui/material';
5 | import { Paper } from '@mui/material';
6 |
7 | const optionsArray = [
8 | { label: '30 seconds' },
9 | { label: '1 minute' },
10 | { label: '2 minutes' },
11 | { label: '5 minutes' },
12 | { label: '10 minutes' },
13 | ];
14 | /* Renders step range dropdown on Custom Metrics page */
15 |
16 | const StepBar = ({ searchquerystep, setsearchquerystep }) => (
17 | (
21 | {children}
22 | )}
23 | options={optionsArray}
24 | sx={{ width: 200 }}
25 | renderInput={(params) => }
26 | searchquery={searchquerystep}
27 | onInputChange={(e, newInputValue) => {
28 | e.preventDefault();
29 | setsearchquerystep(newInputValue);
30 | }}
31 | />
32 | );
33 |
34 | export default StepBar;
35 |
--------------------------------------------------------------------------------
/infra/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | prometheus:
5 | image: prom/prometheus:v2.30.3
6 | ports:
7 | - 9090:9090
8 | volumes:
9 | - ./prometheus:/etc/prometheus
10 | - prometheus-data:/prometheus
11 | command: --web.enable-lifecycle --config.file=/etc/prometheus/prometheus.yml
12 |
13 | grafana:
14 | image: grafana/grafana:8.2.2
15 | ports:
16 | - 3000:3000
17 | restart: unless-stopped
18 | volumes:
19 | - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
20 | - grafana-data:/var/lib/grafana
21 |
22 | alertmanager:
23 | image: prom/alertmanager:v0.23.0
24 | restart: unless-stopped
25 | ports:
26 | - '9093:9093'
27 | volumes:
28 | - './alertmanager:/config'
29 | - alertmanager-data:/data
30 | command: --config.file=/config/alertmanager.yml --log.level=debug
31 |
32 | node-exporter:
33 | image: prom/node-exporter:latest
34 | container_name: monitoring_node_exporter
35 | restart: unless-stopped
36 | expose:
37 | - 9100
38 |
39 | volumes:
40 | prometheus-data:
41 |
42 | grafana-data:
43 |
44 | alertmanager-data:
45 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-serviceMonitor.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: ServiceMonitor
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | name: kube-state-metrics
10 | namespace: monitoring
11 | spec:
12 | endpoints:
13 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
14 | honorLabels: true
15 | interval: 30s
16 | port: https-main
17 | relabelings:
18 | - action: labeldrop
19 | regex: (pod|service|endpoint|namespace)
20 | scheme: https
21 | scrapeTimeout: 30s
22 | tlsConfig:
23 | insecureSkipVerify: true
24 | - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
25 | interval: 30s
26 | port: https-self
27 | scheme: https
28 | tlsConfig:
29 | insecureSkipVerify: true
30 | jobLabel: app.kubernetes.io/name
31 | selector:
32 | matchLabels:
33 | app.kubernetes.io/component: exporter
34 | app.kubernetes.io/name: kube-state-metrics
35 | app.kubernetes.io/part-of: kube-prometheus
36 |
--------------------------------------------------------------------------------
/client/custompage/components/SearchBar.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import IconButton from '@mui/material/IconButton';
3 | import TextField from '@mui/material/TextField';
4 | import { Autocomplete } from '@mui/material';
5 | import { Paper } from '@mui/material';
6 |
7 | const SearchBar = ({ searchquery, setsearchquery, options }) => {
8 | let optionsArray = [];
9 | if (options) {
10 | for (let i = 0; i < options.length; i++) {
11 | optionsArray.push({ label: options[i] });
12 | }
13 | }
14 | /* Renders options dropdown on Custom Metrics page */
15 |
16 | return (
17 | (
21 | {children}
22 | )}
23 | options={optionsArray}
24 | sx={{ width: 500 }}
25 | renderInput={(params) => }
26 | searchquery={searchquery}
27 | onInputChange={(e, newInputValue) => {
28 | e.preventDefault();
29 | setsearchquery(newInputValue);
30 | }}
31 | />
32 | );
33 | };
34 |
35 | export default SearchBar;
36 |
--------------------------------------------------------------------------------
/client/custompage/components/TimeRangeBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@mui/material/IconButton';
3 | import TextField from '@mui/material/TextField';
4 | import { Autocomplete } from '@mui/material';
5 | import { Paper } from '@mui/material';
6 |
7 | const optionsArray = [
8 | { label: '30 minutes' },
9 | { label: '1 hour' },
10 | { label: '2 hours' },
11 | { label: '5 hours' },
12 | { label: '12 hours' },
13 | { label: '24 hours' },
14 | { label: '48 hours' },
15 | ];
16 | /* Renders time range dropdown on Custom Metrics page */
17 |
18 | const TimeRangeBar = ({ searchquerytime, setsearchquerytime }) => (
19 | (
23 | {children}
24 | )}
25 | options={optionsArray}
26 | sx={{ width: 200 }}
27 | renderInput={(params) => }
28 | searchquery={searchquerytime}
29 | onInputChange={(e, newInputValue) => {
30 | e.preventDefault();
31 | setsearchquerytime(newInputValue);
32 | }}
33 | />
34 | );
35 |
36 | export default TimeRangeBar;
37 |
--------------------------------------------------------------------------------
/manifests/alertmanager-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: alert-router
6 | app.kubernetes.io/instance: main
7 | app.kubernetes.io/name: alertmanager
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 0.23.0
10 | name: alertmanager-main
11 | namespace: monitoring
12 | stringData:
13 | alertmanager.yaml: |-
14 | "global":
15 | "resolve_timeout": "5m"
16 | "inhibit_rules":
17 | - "equal":
18 | - "namespace"
19 | - "alertname"
20 | "source_matchers":
21 | - "severity = critical"
22 | "target_matchers":
23 | - "severity =~ warning|info"
24 | - "equal":
25 | - "namespace"
26 | - "alertname"
27 | "source_matchers":
28 | - "severity = warning"
29 | "target_matchers":
30 | - "severity = info"
31 | "receivers":
32 | - "name": "Default"
33 | - "name": "Watchdog"
34 | - "name": "Critical"
35 | "route":
36 | "group_by":
37 | - "namespace"
38 | "group_interval": "5m"
39 | "group_wait": "30s"
40 | "receiver": "Default"
41 | "repeat_interval": "12h"
42 | "routes":
43 | - "matchers":
44 | - "alertname = Watchdog"
45 | "receiver": "Watchdog"
46 | - "matchers":
47 | - "severity = critical"
48 | "receiver": "Critical"
49 | type: Opaque
50 |
--------------------------------------------------------------------------------
/server/utils/formatChartData.js:
--------------------------------------------------------------------------------
1 | function formatChartData(data) {
2 | try {
3 | const res = {
4 | timestamps: [],
5 | seriesLabels: [],
6 | seriesValues: [],
7 | };
8 |
9 | // Helper function to convert the Prometheus MS timestamp to HH:MM
10 | const timeFilter = /[0-9][0-9]:[0-9][0-9]/;
11 | const msToTimestamp = (ms) =>
12 | new Date(1000 * ms).toISOString().match(timeFilter)[0];
13 |
14 | // Pop the last series off the query response to extract timestamp and groupBy label
15 | const initialSet = data.pop();
16 | const groupByLabel = Object.keys(initialSet.metric)[0];
17 |
18 | // Add this last series to the response object arrays
19 | res.timestamps = initialSet.values.map((vals) => msToTimestamp(vals[0]));
20 | res.seriesLabels.push(initialSet.metric[groupByLabel] || 'Cluster');
21 | res.seriesValues.push(initialSet.values.map((vals) => vals[1]));
22 |
23 | // For each remaining dataset, push the series label and array of datapoints onto the res object
24 | data.forEach((dataset) => {
25 | res.seriesLabels.push(dataset.metric[groupByLabel]); // add a new dataseries label to our res
26 | res.seriesValues.push(dataset.values.map((vals) => vals[1])); // add the dataseries to our res
27 | });
28 |
29 | // Return the constructed response
30 | return res;
31 | } catch (err) {
32 | console.log(err);
33 | }
34 | }
35 |
36 | module.exports = formatChartData;
37 |
--------------------------------------------------------------------------------
/manifests/prometheus-prometheus.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: Prometheus
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: prometheus
6 | app.kubernetes.io/instance: k8s
7 | app.kubernetes.io/name: prometheus
8 | app.kubernetes.io/part-of: kube-prometheus
9 | app.kubernetes.io/version: 2.32.1
10 | name: k8s
11 | namespace: monitoring
12 | spec:
13 | alerting:
14 | alertmanagers:
15 | - apiVersion: v2
16 | name: alertmanager-main
17 | namespace: monitoring
18 | port: web
19 | enableFeatures: []
20 | externalLabels: {}
21 | image: quay.io/prometheus/prometheus:v2.32.1
22 | nodeSelector:
23 | kubernetes.io/os: linux
24 | podMetadata:
25 | labels:
26 | app.kubernetes.io/component: prometheus
27 | app.kubernetes.io/instance: k8s
28 | app.kubernetes.io/name: prometheus
29 | app.kubernetes.io/part-of: kube-prometheus
30 | app.kubernetes.io/version: 2.32.1
31 | podMonitorNamespaceSelector: {}
32 | podMonitorSelector: {}
33 | probeNamespaceSelector: {}
34 | probeSelector: {}
35 | replicas: 2
36 | resources:
37 | requests:
38 | memory: 400Mi
39 | ruleNamespaceSelector: {}
40 | ruleSelector: {}
41 | securityContext:
42 | fsGroup: 2000
43 | runAsNonRoot: true
44 | runAsUser: 1000
45 | serviceAccountName: prometheus-k8s
46 | serviceMonitorNamespaceSelector: {}
47 | serviceMonitorSelector: {}
48 | version: 2.32.1
49 |
--------------------------------------------------------------------------------
/armada-depl.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: armada
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: armada
10 | template:
11 | metadata:
12 | labels:
13 | app: armada
14 | spec:
15 | containers:
16 | - name: armada
17 | image: armadak8s:latest
18 | ports:
19 | - containerPort: 80
20 | protocol: TCP
21 | imagePullPolicy: IfNotPresent
22 | ---
23 | apiVersion: v1
24 | kind: Service
25 | metadata:
26 | name: armada
27 | labels:
28 | prometheus: cluster-monitoring
29 | k8s-app: kube-state-metrics
30 | spec:
31 | selector:
32 | app: armada
33 | type: LoadBalancer
34 | ports:
35 | - name: armada
36 | protocol: TCP
37 | port: 8080
38 | targetPort: 8080
39 | # NOTE: The service account `default:default` already exists in k8s cluster.
40 | # You can create a new account following like this:
41 | #---
42 | #apiVersion: v1
43 | #kind: ServiceAccount
44 | #metadata:
45 | # name:
46 | # namespace:
47 |
48 | ---
49 | apiVersion: rbac.authorization.k8s.io/v1
50 | kind: ClusterRoleBinding
51 | metadata:
52 | name: fabric8-rbac
53 | subjects:
54 | - kind: ServiceAccount
55 | # Reference to upper's `metadata.name`
56 | name: default
57 | # Reference to upper's `metadata.namespace`
58 | namespace: default
59 | roleRef:
60 | kind: ClusterRole
61 | name: cluster-admin
62 | apiGroup: rbac.authorization.k8s.io
63 |
--------------------------------------------------------------------------------
/client/utils/table/helpers.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // filtering
3 | // default UI for filtering
4 | export function DefaultColumnFilter({
5 | column: { filterValue, preFilteredRows, setFilter },
6 | }) {
7 | const count = preFilteredRows.length;
8 |
9 | return (
10 | {
13 | setFilter(e.target.value || undefined);
14 | }}
15 | placeholder={''}
16 | />
17 | );
18 | }
19 |
20 | //filter for selecting option from list
21 | export function SelectColumnFilter({
22 | column: { filterValue, setFilter, preFilteredRows, id },
23 | }) {
24 | const options = React.useMemo(() => {
25 | const options = new Set();
26 | preFilteredRows.forEach((row) => {
27 | options.add(row.values[id]);
28 | });
29 | return [...options.values()];
30 | }, [id, preFilteredRows]);
31 |
32 | // render a multi-select box
33 | return (
34 |
47 | );
48 | }
49 |
50 | //fuzzy text filter
51 | export function fuzzyTextFilterFn(rows, id, filterValue) {
52 | return matchSorter(rows, filterValue, { keys: [(row) => row.values[id]] });
53 | }
54 |
55 | fuzzyTextFilterFn.autoRemove = (val) => !val;
56 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-configuration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | data:
3 | config.yml: |-
4 | "modules":
5 | "http_2xx":
6 | "http":
7 | "preferred_ip_protocol": "ip4"
8 | "prober": "http"
9 | "http_post_2xx":
10 | "http":
11 | "method": "POST"
12 | "preferred_ip_protocol": "ip4"
13 | "prober": "http"
14 | "irc_banner":
15 | "prober": "tcp"
16 | "tcp":
17 | "preferred_ip_protocol": "ip4"
18 | "query_response":
19 | - "send": "NICK prober"
20 | - "send": "USER prober prober prober :prober"
21 | - "expect": "PING :([^ ]+)"
22 | "send": "PONG ${1}"
23 | - "expect": "^:[^ ]+ 001"
24 | "pop3s_banner":
25 | "prober": "tcp"
26 | "tcp":
27 | "preferred_ip_protocol": "ip4"
28 | "query_response":
29 | - "expect": "^+OK"
30 | "tls": true
31 | "tls_config":
32 | "insecure_skip_verify": false
33 | "ssh_banner":
34 | "prober": "tcp"
35 | "tcp":
36 | "preferred_ip_protocol": "ip4"
37 | "query_response":
38 | - "expect": "^SSH-2.0-"
39 | "tcp_connect":
40 | "prober": "tcp"
41 | "tcp":
42 | "preferred_ip_protocol": "ip4"
43 | kind: ConfigMap
44 | metadata:
45 | labels:
46 | app.kubernetes.io/component: exporter
47 | app.kubernetes.io/name: blackbox-exporter
48 | app.kubernetes.io/part-of: kube-prometheus
49 | app.kubernetes.io/version: 0.19.0
50 | name: blackbox-exporter-configuration
51 | namespace: monitoring
52 |
--------------------------------------------------------------------------------
/client/homepage/containers/CriticalPodsContainer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Grid } from '@mui/material';
3 | import CPUIntensivePods from '../components/CriticalPods/CPUIntensivePods';
4 | import MemoryIntensivePods from '../components/CriticalPods/MemoryIntensivePods';
5 |
6 | const CriticalPodsContainer = (props) => {
7 | const { namespace } = props;
8 |
9 | const [cpu, setCpu] = useState([]);
10 | const [memory, setMemory] = useState([]);
11 | const fetchCpuByPod = () => {
12 | fetch(`/api/prometheus/cpubypod?namespace=${namespace}`)
13 | .then((res) => res.json())
14 | .then((data) => {
15 | // console.log('cpu data ' + data);
16 | setCpu(data);
17 | })
18 | .catch((err) => console.log(err));
19 | };
20 |
21 | const fetchMemoryByPod = () => {
22 | fetch(`/api/prometheus/memorybypod?namespace=${namespace}`)
23 | .then((res) => res.json())
24 | .then((data) => {
25 | setMemory(data);
26 | console.log(data);
27 | })
28 | .catch((err) => console.log(err));
29 | };
30 |
31 | useEffect(() => {
32 | fetchCpuByPod();
33 | fetchMemoryByPod();
34 | }, [namespace]);
35 |
36 | const renderCpuGraph = () => {
37 | if (cpu) {
38 | return ;
39 | }
40 | };
41 |
42 | const renderMemory = () => {
43 | if (memory) {
44 | return ;
45 | }
46 | };
47 |
48 | return (
49 |
50 |
51 | {renderCpuGraph()}
52 |
53 |
54 | {renderMemory()}
55 |
56 |
57 | );
58 | };
59 |
60 | export default CriticalPodsContainer;
61 |
--------------------------------------------------------------------------------
/client/homepage/components/Charts/BarChartTemplate.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 |
13 | ChartJS.register(
14 | CategoryScale,
15 | LinearScale,
16 | BarElement,
17 | Title,
18 | Tooltip,
19 | Legend
20 | );
21 |
22 | const BarChart = ({ chartData, title, label }) => {
23 | const options = {
24 | indexAxis: 'y',
25 | elements: {
26 | bar: {
27 | borderWidth: 2,
28 | },
29 | },
30 | responsive: true,
31 | plugins: {
32 | legend: {
33 | display: false,
34 | },
35 | title: {
36 | display: true,
37 | text: title,
38 | color: 'white',
39 | font: { weight: 'bold' },
40 | },
41 | datalabels: {
42 | // hide datalabels for all datasets
43 | display: false,
44 | },
45 | },
46 | scales: {
47 | xAxes: {
48 | display: true,
49 | ticks: {
50 | color: 'white',
51 | },
52 | },
53 | yAxes: {
54 | display: true,
55 | ticks: {
56 | color: 'white',
57 | },
58 | },
59 | },
60 | };
61 | const data = {
62 | labels: chartData.map((el) => el.label),
63 | datasets: [
64 | {
65 | // label,
66 | data: chartData.map((el) => el.data),
67 | borderColor: 'transparent',
68 | backgroundColor: 'rgb(140, 92, 142)',
69 | },
70 | ],
71 | };
72 |
73 | return ;
74 | };
75 |
76 | export default BarChart;
77 |
78 | // horizontal bar charts
79 |
--------------------------------------------------------------------------------
/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
3 | import { ThemeProvider, createTheme } from '@mui/material/styles';
4 | import MainContainer from './homepage/containers/MainContainer';
5 | import NavBar from './homepage/containers/NavBar';
6 | import MetricsContainer from './metricspage/containers/MetricsContainer';
7 | import Alerts from './alertspage/Alerts';
8 | import { Container, Box } from '@mui/material';
9 | import { blueGrey } from '@mui/material/colors';
10 | import LogsContainer from './logspage/containers/LogsContainer';
11 | import CustomMetricsContainer from './custompage/containers/CustomMetricsContainer';
12 |
13 | /* Applies MUI dark theme */
14 |
15 | const darkTheme = createTheme({
16 | palette: {
17 | mode: 'dark',
18 | background: {
19 | default: '#121212',
20 | paper: 'rgba(255, 255, 255, 0.08)',
21 | },
22 | text: {
23 | primary: '#fff',
24 | },
25 | },
26 | });
27 |
28 | /* Leveraging React Router to create multiple views within single page application */
29 | function App() {
30 | return (
31 |
32 |
33 |
34 |
35 |
36 | } />
37 | } />
38 | } />
39 | } />
40 | } />
41 |
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | export default App;
50 |
--------------------------------------------------------------------------------
/manifests/prometheusOperator-clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: controller
6 | app.kubernetes.io/name: prometheus-operator
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.53.1
9 | name: prometheus-operator
10 | rules:
11 | - apiGroups:
12 | - monitoring.coreos.com
13 | resources:
14 | - alertmanagers
15 | - alertmanagers/finalizers
16 | - alertmanagerconfigs
17 | - prometheuses
18 | - prometheuses/finalizers
19 | - thanosrulers
20 | - thanosrulers/finalizers
21 | - servicemonitors
22 | - podmonitors
23 | - probes
24 | - prometheusrules
25 | verbs:
26 | - '*'
27 | - apiGroups:
28 | - apps
29 | resources:
30 | - statefulsets
31 | verbs:
32 | - '*'
33 | - apiGroups:
34 | - ""
35 | resources:
36 | - configmaps
37 | - secrets
38 | verbs:
39 | - '*'
40 | - apiGroups:
41 | - ""
42 | resources:
43 | - pods
44 | verbs:
45 | - list
46 | - delete
47 | - apiGroups:
48 | - ""
49 | resources:
50 | - services
51 | - services/finalizers
52 | - endpoints
53 | verbs:
54 | - get
55 | - create
56 | - update
57 | - delete
58 | - apiGroups:
59 | - ""
60 | resources:
61 | - nodes
62 | verbs:
63 | - list
64 | - watch
65 | - apiGroups:
66 | - ""
67 | resources:
68 | - namespaces
69 | verbs:
70 | - get
71 | - list
72 | - watch
73 | - apiGroups:
74 | - networking.k8s.io
75 | resources:
76 | - ingresses
77 | verbs:
78 | - get
79 | - list
80 | - watch
81 | - apiGroups:
82 | - authentication.k8s.io
83 | resources:
84 | - tokenreviews
85 | verbs:
86 | - create
87 | - apiGroups:
88 | - authorization.k8s.io
89 | resources:
90 | - subjectaccessreviews
91 | verbs:
92 | - create
93 |
--------------------------------------------------------------------------------
/client/namespaceSelection/SelectNamespace.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { connect } from 'react-redux';
3 | import Select from 'react-select';
4 | import { setNamespace, fetchNamespacesList } from '../actions/actions';
5 |
6 | function SelectNamespace({
7 | selectedNamespace,
8 | setNamespace,
9 | fetchNamespacesList,
10 | }) {
11 | const [namespaceOptions, setNamespaceOptions] = useState([]);
12 | /* Fetch list of Namespaces from backend API endpoint that connects to prometheus*/
13 | const getNamespaceList = () => {
14 | fetch('/api/namespaceList')
15 | .then((res) => res.json())
16 | .then((data) => {
17 | let names = ['All'];
18 | data.items.forEach((item) => {
19 | names.push(item.metadata.name);
20 | });
21 | fetchNamespacesList(names);
22 | return names;
23 | })
24 | .then((list) => {
25 | const options = list.map((el) => {
26 | return { value: el, label: el };
27 | });
28 | setNamespaceOptions(options);
29 | })
30 | .catch((error) => console.log(error));
31 | };
32 | useEffect(() => {
33 | getNamespaceList();
34 | }, []);
35 |
36 | function handleNamespaceChange(namespace) {
37 | setNamespace(namespace.value);
38 | }
39 |
40 | return (
41 |
48 | );
49 | }
50 | /* Exports map state to props to create Global namespace variable*/
51 | const mapStateToProps = ({ namespace }) => {
52 | return { ...namespace };
53 | };
54 |
55 | export default connect(mapStateToProps, { setNamespace, fetchNamespacesList })(
56 | SelectNamespace
57 | );
58 |
--------------------------------------------------------------------------------
/manifests/prometheus-roleBindingSpecificNamespaces.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | items:
3 | - apiVersion: rbac.authorization.k8s.io/v1
4 | kind: RoleBinding
5 | metadata:
6 | labels:
7 | app.kubernetes.io/component: prometheus
8 | app.kubernetes.io/instance: k8s
9 | app.kubernetes.io/name: prometheus
10 | app.kubernetes.io/part-of: kube-prometheus
11 | app.kubernetes.io/version: 2.32.1
12 | name: prometheus-k8s
13 | namespace: default
14 | roleRef:
15 | apiGroup: rbac.authorization.k8s.io
16 | kind: Role
17 | name: prometheus-k8s
18 | subjects:
19 | - kind: ServiceAccount
20 | name: prometheus-k8s
21 | namespace: monitoring
22 | - apiVersion: rbac.authorization.k8s.io/v1
23 | kind: RoleBinding
24 | metadata:
25 | labels:
26 | app.kubernetes.io/component: prometheus
27 | app.kubernetes.io/instance: k8s
28 | app.kubernetes.io/name: prometheus
29 | app.kubernetes.io/part-of: kube-prometheus
30 | app.kubernetes.io/version: 2.32.1
31 | name: prometheus-k8s
32 | namespace: kube-system
33 | roleRef:
34 | apiGroup: rbac.authorization.k8s.io
35 | kind: Role
36 | name: prometheus-k8s
37 | subjects:
38 | - kind: ServiceAccount
39 | name: prometheus-k8s
40 | namespace: monitoring
41 | - apiVersion: rbac.authorization.k8s.io/v1
42 | kind: RoleBinding
43 | metadata:
44 | labels:
45 | app.kubernetes.io/component: prometheus
46 | app.kubernetes.io/instance: k8s
47 | app.kubernetes.io/name: prometheus
48 | app.kubernetes.io/part-of: kube-prometheus
49 | app.kubernetes.io/version: 2.32.1
50 | name: prometheus-k8s
51 | namespace: monitoring
52 | roleRef:
53 | apiGroup: rbac.authorization.k8s.io
54 | kind: Role
55 | name: prometheus-k8s
56 | subjects:
57 | - kind: ServiceAccount
58 | name: prometheus-k8s
59 | namespace: monitoring
60 | kind: RoleBindingList
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "armada",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "./client/index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=production nodemon server/server.js",
8 | "build": "NODE_ENV=production webpack",
9 | "dev": "NODE_ENV=development nodemon server/server.js & NODE_ENV=development webpack serve",
10 | "test": "jest"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/oslabs-beta/Armada"
15 | },
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/oslabs-beta/Armada"
20 | },
21 | "homepage": "https://github.com/oslabs-beta/Armada",
22 | "dependencies": {
23 | "@emotion/react": "^11.9.0",
24 | "@emotion/styled": "^11.8.1",
25 | "@kubernetes/client-node": "^0.16.3",
26 | "@mui/material": "^5.7.0",
27 | "@mui/x-data-grid": "^5.11.0",
28 | "@reduxjs/toolkit": "^1.8.1",
29 | "chart.js": "^3.7.1",
30 | "chartjs-plugin-datalabels": "^2.0.0",
31 | "express": "^4.18.1",
32 | "express-ws": "^5.0.2",
33 | "fs": "0.0.1-security",
34 | "match-sorter": "^6.3.1",
35 | "node-cmd": "^5.0.0",
36 | "node-fetch": "^3.2.4",
37 | "prom-client": "^14.0.1",
38 | "react": "^18.1.0",
39 | "react-chartjs-2": "^4.1.0",
40 | "react-copy-to-clipboard": "^5.1.0",
41 | "react-dom": "^18.1.0",
42 | "react-redux": "^8.0.1",
43 | "react-router-dom": "^6.3.0",
44 | "react-scroll": "^1.8.7",
45 | "react-table": "^7.8.0",
46 | "redux": "^4.2.0",
47 | "socket.io": "^4.5.1",
48 | "socket.io-client": "^4.5.1",
49 | "webpack": "^5.72.1",
50 | "webpack-dev-server": "^4.9.0"
51 | },
52 | "devDependencies": {
53 | "@babel/core": "^7.17.10",
54 | "@babel/preset-env": "^7.17.10",
55 | "@babel/preset-react": "^7.16.7",
56 | "babel-loader": "^8.2.5",
57 | "css-loader": "^6.7.1",
58 | "html-webpack-plugin": "^5.5.0",
59 | "jest": "^28.1.0",
60 | "mini-css-extract-plugin": "^2.6.0",
61 | "react-select": "^5.3.2",
62 | "sass-loader": "^12.6.0",
63 | "style-loader": "^3.3.1",
64 | "supertest": "^6.2.3",
65 | "webpack-cli": "^4.9.2"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/client/components/Statuses/NodesStatus.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import Box from '@mui/material/Box';
4 | import Tooltip from '@mui/material/Tooltip';
5 | import ComponentWrapper from '../../../utils/ComponentWrapper';
6 | import parseStatus from '../../../utils/parseStatus';
7 |
8 | const mapStateToProps = ({ nodes }) => {
9 | return nodes;
10 | };
11 |
12 | const NodesStatus = ({ items }) => {
13 | const nodesObj = parseStatus(items);
14 |
15 | console.log('nodesObj', nodesObj);
16 |
17 | const NodeBoxes = [];
18 | for (let key of Object.keys(nodesObj)) {
19 | let boxStyle = {
20 | backgroundColor: '#3dce2d',
21 | border: '1px solid #39ba2a',
22 | borderRadius: '3px',
23 | width: '20px',
24 | height: '20px',
25 | margin: '1px',
26 | };
27 |
28 | if (nodesObj[key]['Ready'] === 'False') {
29 | boxStyle['backgroundColor'] = '#ea2315';
30 | boxStyle['border'] = '1px solid #d82c20';
31 | } else if (nodesObj[key]['Ready'] === 'Unknown') {
32 | boxStyle['backgroundColor'] = 'yellow';
33 | }
34 |
35 | let tooltipText = `Name: ${key}\n`;
36 | for (let condition of Object.keys(nodesObj[key])) {
37 | tooltipText += `${condition}: ${nodesObj[key][condition]}\n`;
38 | }
39 |
40 | NodeBoxes.push(
41 |
42 |
43 |
50 |
51 |
52 | );
53 | }
54 |
55 | return (
56 |
57 |
58 | {/*
Node Status
*/}
59 |
60 |
{NodeBoxes}
61 |
62 |
63 | );
64 | };
65 |
66 | export default connect(mapStateToProps)(NodesStatus);
67 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
5 |
6 | const config = {
7 | mode: process.env.NODE_ENV,
8 | entry: path.join(__dirname, './client/index.js'),
9 | output: {
10 | path: path.resolve(__dirname, 'build'),
11 | filename: 'bundle.js',
12 | publicPath: '/',
13 | clean: true,
14 | },
15 | plugins: [
16 | new HtmlWebpackPlugin({
17 | template: path.join(__dirname, 'index.html'),
18 | }),
19 | new MiniCssExtractPlugin(),
20 | ],
21 | resolve: {
22 | extensions: ['.jsx', '.js', '.tsx', '.ts'],
23 | },
24 | module: {
25 | rules: [
26 | {
27 | test: /\.(js|jsx)$/,
28 | use: {
29 | loader: 'babel-loader',
30 | options: {
31 | presets: ['@babel/preset-env', '@babel/preset-react'],
32 | plugins: [],
33 | },
34 | },
35 | exclude: /node_modules/,
36 | },
37 | {
38 | test: /\.css$/,
39 | use: [MiniCssExtractPlugin.loader, 'css-loader'],
40 | },
41 | {
42 | test: /\.s[ac]ss$/i,
43 | exclude: /node_modules/,
44 | use: [
45 | MiniCssExtractPlugin.loader,
46 | 'style-loader',
47 | 'css-loader',
48 | 'sass-loader',
49 | ],
50 | },
51 | {
52 | test: /\.(png|svg|jpg|jpeg|gif)$/i,
53 | type: 'asset/resource',
54 | },
55 | ],
56 | },
57 |
58 | devServer: {
59 | static: {
60 | directory: path.resolve(__dirname, 'build'),
61 | publicPath: '/',
62 | },
63 | compress: true,
64 | port: 8080,
65 | hot: true,
66 | historyApiFallback: true,
67 | proxy: {
68 | '/api': {
69 | target: 'http://localhost:3001',
70 | secure: false,
71 | changeOrigin: true,
72 | },
73 | '/socket.io': {
74 | target: 'http://localhost:4000',
75 | ws: true,
76 | headers: { Connection: 'keep-alive' },
77 | },
78 | },
79 | },
80 | };
81 |
82 | module.exports = config;
83 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-clusterRole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | name: kube-state-metrics
10 | rules:
11 | - apiGroups:
12 | - ""
13 | resources:
14 | - configmaps
15 | - secrets
16 | - nodes
17 | - pods
18 | - services
19 | - resourcequotas
20 | - replicationcontrollers
21 | - limitranges
22 | - persistentvolumeclaims
23 | - persistentvolumes
24 | - namespaces
25 | - endpoints
26 | verbs:
27 | - list
28 | - watch
29 | - apiGroups:
30 | - apps
31 | resources:
32 | - statefulsets
33 | - daemonsets
34 | - deployments
35 | - replicasets
36 | verbs:
37 | - list
38 | - watch
39 | - apiGroups:
40 | - batch
41 | resources:
42 | - cronjobs
43 | - jobs
44 | verbs:
45 | - list
46 | - watch
47 | - apiGroups:
48 | - autoscaling
49 | resources:
50 | - horizontalpodautoscalers
51 | verbs:
52 | - list
53 | - watch
54 | - apiGroups:
55 | - authentication.k8s.io
56 | resources:
57 | - tokenreviews
58 | verbs:
59 | - create
60 | - apiGroups:
61 | - authorization.k8s.io
62 | resources:
63 | - subjectaccessreviews
64 | verbs:
65 | - create
66 | - apiGroups:
67 | - policy
68 | resources:
69 | - poddisruptionbudgets
70 | verbs:
71 | - list
72 | - watch
73 | - apiGroups:
74 | - certificates.k8s.io
75 | resources:
76 | - certificatesigningrequests
77 | verbs:
78 | - list
79 | - watch
80 | - apiGroups:
81 | - storage.k8s.io
82 | resources:
83 | - storageclasses
84 | - volumeattachments
85 | verbs:
86 | - list
87 | - watch
88 | - apiGroups:
89 | - admissionregistration.k8s.io
90 | resources:
91 | - mutatingwebhookconfigurations
92 | - validatingwebhookconfigurations
93 | verbs:
94 | - list
95 | - watch
96 | - apiGroups:
97 | - networking.k8s.io
98 | resources:
99 | - networkpolicies
100 | - ingresses
101 | verbs:
102 | - list
103 | - watch
104 | - apiGroups:
105 | - coordination.k8s.io
106 | resources:
107 | - leases
108 | verbs:
109 | - list
110 | - watch
111 |
--------------------------------------------------------------------------------
/client/homepage/components/Charts/GaugeChartTemplate.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | ArcElement,
11 | } from 'chart.js';
12 | import { Doughnut } from 'react-chartjs-2';
13 | import ComponentWrapper from '../../../utils/ComponentWrapper';
14 | import { Card, Typography } from '@mui/material';
15 | import ChartDataLabels from 'chartjs-plugin-datalabels';
16 |
17 | function GaugeChartTemplate({ chartData, title, label }) {
18 | ChartJS.register(
19 | CategoryScale,
20 | LinearScale,
21 | BarElement,
22 | Title,
23 | Tooltip,
24 | Legend,
25 | ArcElement,
26 | ChartDataLabels
27 | );
28 |
29 | const options = {
30 | plugins: {
31 | title: {
32 | display: false,
33 | responsive: true,
34 | animation: {
35 | animateScale: true,
36 | },
37 | },
38 | datalabels: {
39 | // This code is used to display data values
40 | anchor: 'center',
41 | align: 'center',
42 | font: {
43 | weight: 'bold',
44 | size: 20,
45 | },
46 | formatter: (value) => value + '%',
47 | color: 'white',
48 | },
49 | },
50 | layout: {
51 | padding: {
52 | top: '-50',
53 | },
54 | },
55 | pointRadius: 0,
56 | circumference: '180',
57 | rotation: '-90',
58 | };
59 |
60 | let utilColor;
61 |
62 | if (chartData) {
63 | if (100 - chartData <= 33.3) {
64 | utilColor = '#ea2315';
65 | } else if (100 - chartData > 33.3 && 100 - chartData < 66.6) {
66 | utilColor = 'yellow';
67 | } else {
68 | utilColor = '#3dce2d';
69 | }
70 | }
71 |
72 | const data = {
73 | datasets: [
74 | {
75 | data: [chartData, 100 - chartData],
76 | borderColor: ['transparent', 'transparent'],
77 | backgroundColor: [utilColor, '#808080'],
78 | },
79 | ],
80 | };
81 |
82 | const renderChartData = () => {
83 | if (chartData !== undefined) {
84 | return {chartData.toString() + '%'};
85 | }
86 | };
87 |
88 | return (
89 |
90 |
91 |
92 | );
93 | }
94 |
95 | export default GaugeChartTemplate;
96 |
--------------------------------------------------------------------------------
/client/homepage/components/Statuses/PodsStatus.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Box from '@mui/material/Box';
3 | import Tooltip from '@mui/material/Tooltip';
4 | import Card from '@mui/material/Card';
5 | import Popper from '@mui/material/Popper';
6 | import Typography from '@mui/material/Typography';
7 | import ComponentWrapper from '../../../utils/ComponentWrapper';
8 |
9 | const PodsStatus = (props) => {
10 | const { pods } = props;
11 | const podsObj = {};
12 | for (let pod of pods) {
13 | podsObj[pod.metadata.name] = pod.status.phase;
14 | }
15 |
16 | const [anchorEl, setAnchorEl] = React.useState(null);
17 | const [open, setOpen] = React.useState(false);
18 | const [placement, setPlacement] = React.useState();
19 | const [popperText, setPopperText] = React.useState([]);
20 |
21 | // Generate pod boxes
22 | const PodsBoxes = [];
23 | for (let key of Object.keys(podsObj)) {
24 | let boxStyle = {
25 | backgroundColor: '#3dce2d',
26 | border: '1px solid #39ba2a',
27 | borderRadius: '3px',
28 | width: '30px',
29 | height: '30px',
30 | margin: '1px',
31 | };
32 | if (podsObj[key] !== 'Running') {
33 | boxStyle['background-color'] = 'red';
34 | }
35 | PodsBoxes.push(
36 |
37 |
43 |
44 | );
45 | }
46 |
47 | // Handleclick for popper
48 | const handleClick = (event, key) => {
49 | let popperHTML = [];
50 | popperHTML.push(Name: {key}
);
51 | popperHTML.push(Status: {podsObj[key]}
);
52 | setPopperText(popperHTML);
53 | setAnchorEl(event.currentTarget);
54 | setOpen((previousOpen) => !previousOpen);
55 | };
56 |
57 | return (
58 |
59 |
60 |
61 | {popperText}
62 |
63 |
64 |
65 |
66 | {PodsBoxes}
67 |
68 |
69 | );
70 | };
71 |
72 | export default PodsStatus;
73 |
--------------------------------------------------------------------------------
/client/homepage/containers/CriticalNodesContainer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import CPUIntensiveNodes from '../components/CriticalNodes/CPUIntensiveNodes';
3 | import MemoryIntensiveNodes from '../components/CriticalNodes/MemoryIntensiveNodes';
4 | import BytesTransmittedPerNode from '../components/CriticalNodes/BytesTransmittedPerNode';
5 | import BytesReceivedPerNode from '../components/CriticalNodes/BytesReceivedPerNode';
6 | import { Grid } from '@mui/material';
7 |
8 | const CriticalNodesContainer = ({ promMetrics }) => {
9 | const [cpu, setCpu] = useState([]);
10 | const [memory, setMemory] = useState([]);
11 | const getCpuByNode = () => {
12 | fetch('api/prometheus/cpubynode')
13 | .then((res) => {
14 | return res.json();
15 | })
16 | .then((data) => {
17 | setCpu(data);
18 | })
19 | .catch((err) => console.log('error with nodebycpu', err));
20 | };
21 |
22 | const getMemoryByNode = () => {
23 | fetch('/api/prometheus/memorybynode')
24 | .then((res) => res.json())
25 | .then((data) => setMemory(data))
26 | .catch((err) => console.log(err));
27 | };
28 |
29 | useEffect(() => {
30 | getCpuByNode();
31 | getMemoryByNode();
32 | }, []);
33 |
34 | const renderCpuGraph = () => {
35 | if (cpu.length > 0) {
36 | return ;
37 | }
38 | };
39 | const renderMemoryGraph = () => {
40 | if (memory.length > 0) {
41 | return ;
42 | }
43 | };
44 |
45 | const renderNetworkTransmitGraph = () => {
46 | if (promMetrics.bytesTransmittedPerNode) {
47 | return ;
48 | }
49 | };
50 |
51 | const renderNetworkReceivedGraph = () => {
52 | if (promMetrics.bytesReceivedPerNode) {
53 | return ;
54 | }
55 | };
56 |
57 | return (
58 |
59 |
60 | {renderCpuGraph()}
61 |
62 |
63 | {renderMemoryGraph()}
64 |
65 |
66 |
67 | {renderNetworkTransmitGraph()}
68 |
69 |
70 | {renderNetworkReceivedGraph()}
71 |
72 |
73 | );
74 | };
75 |
76 | export default CriticalNodesContainer;
77 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-configMap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | data:
3 | config.yaml: |-
4 | "resourceRules":
5 | "cpu":
6 | "containerLabel": "container"
7 | "containerQuery": |
8 | sum by (<<.GroupBy>>) (
9 | irate (
10 | container_cpu_usage_seconds_total{<<.LabelMatchers>>,container!="",pod!=""}[120s]
11 | )
12 | )
13 | "nodeQuery": |
14 | sum by (<<.GroupBy>>) (
15 | 1 - irate(
16 | node_cpu_seconds_total{mode="idle"}[60s]
17 | )
18 | * on(namespace, pod) group_left(node) (
19 | node_namespace_pod:kube_pod_info:{<<.LabelMatchers>>}
20 | )
21 | )
22 | or sum by (<<.GroupBy>>) (
23 | 1 - irate(
24 | windows_cpu_time_total{mode="idle", job="windows-exporter",<<.LabelMatchers>>}[4m]
25 | )
26 | )
27 | "resources":
28 | "overrides":
29 | "namespace":
30 | "resource": "namespace"
31 | "node":
32 | "resource": "node"
33 | "pod":
34 | "resource": "pod"
35 | "memory":
36 | "containerLabel": "container"
37 | "containerQuery": |
38 | sum by (<<.GroupBy>>) (
39 | container_memory_working_set_bytes{<<.LabelMatchers>>,container!="",pod!=""}
40 | )
41 | "nodeQuery": |
42 | sum by (<<.GroupBy>>) (
43 | node_memory_MemTotal_bytes{job="node-exporter",<<.LabelMatchers>>}
44 | -
45 | node_memory_MemAvailable_bytes{job="node-exporter",<<.LabelMatchers>>}
46 | )
47 | or sum by (<<.GroupBy>>) (
48 | windows_cs_physical_memory_bytes{job="windows-exporter",<<.LabelMatchers>>}
49 | -
50 | windows_memory_available_bytes{job="windows-exporter",<<.LabelMatchers>>}
51 | )
52 | "resources":
53 | "overrides":
54 | "instance":
55 | "resource": "node"
56 | "namespace":
57 | "resource": "namespace"
58 | "pod":
59 | "resource": "pod"
60 | "window": "5m"
61 | kind: ConfigMap
62 | metadata:
63 | labels:
64 | app.kubernetes.io/component: metrics-adapter
65 | app.kubernetes.io/name: prometheus-adapter
66 | app.kubernetes.io/part-of: kube-prometheus
67 | app.kubernetes.io/version: 0.9.1
68 | name: adapter-config
69 | namespace: monitoring
70 |
--------------------------------------------------------------------------------
/client/homepage/containers/UtilizationContainer.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Grid } from '@mui/material';
3 | import CpuUtilization from '../components/Utilizations/CpuUtilization.js';
4 | import MemoryUtilization from '../components/Utilizations/MemoryUtilization.js';
5 | import CpuTotal from '../components/Utilizations/CpuTotal.js';
6 | import MemoryTotal from '../components/Utilizations/MemoryTotal.js';
7 |
8 | const UtilizationContainer = (props) => {
9 | const [clusterData, setClusterData] = useState([]);
10 |
11 | const getUtilization = () => {
12 | fetch('/api/prometheus/clustermetrics')
13 | .then((data) => data.json())
14 | .then((data) => {
15 | const obj = {};
16 | console.log(data);
17 | for (const key in data) {
18 | if (key == 'cpuUtilization' || key == 'memoryUtilization') {
19 | obj[key] = Number(data[key] * 100).toFixed(0);
20 | } else {
21 | obj[key] = data[key];
22 | }
23 | }
24 | setClusterData(obj);
25 | })
26 | .catch((error) => console.log(error));
27 | };
28 |
29 | useEffect(() => {
30 | getUtilization();
31 | }, []);
32 |
33 | const renderCpuUtilization = () => {
34 | if (clusterData) {
35 | return ;
36 | }
37 | };
38 |
39 | const renderMemoryUtilization = () => {
40 | if (clusterData) {
41 | return ;
42 | }
43 | };
44 |
45 | const renderCpuTotal = () => {
46 | if (clusterData) {
47 | return (
48 |
52 | );
53 | }
54 | };
55 |
56 | const renderMemoryTotal = () => {
57 | if (clusterData) {
58 | return (
59 |
63 | );
64 | }
65 | };
66 |
67 | return (
68 |
69 |
70 | {renderCpuUtilization()}
71 |
72 |
73 | {renderMemoryUtilization()}
74 |
75 |
76 | {renderCpuTotal()}
77 |
78 |
79 | {renderMemoryTotal()}
80 |
81 |
82 | );
83 | };
84 |
85 | export default UtilizationContainer;
86 |
--------------------------------------------------------------------------------
/client/demoData/rawLogs.json:
--------------------------------------------------------------------------------
1 | [
2 | "NAMESPACE LAST SEEN TYPE REASON OBJECT MESSAGE",
3 | "kube-system 112s Warning Unhealthy pod/metrics-server-847dcc659d-brstf Readiness probe failed: HTTP probe failed with statuscode: 500",
4 | "monitoring 116s Warning Unhealthy pod/alertmanager-main-1 Readiness probe failed: Get \"http://10.244.2.10:9093/-/ready\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)",
5 | "monitoring 6m55s Warning Unhealthy pod/alertmanager-main-1 Readiness probe failed: Get \"http://10.244.2.10:9093/-/ready\": dial tcp 10.244.2.10:9093: i/o timeout (Client.Timeout exceeded while awaiting headers)",
6 | "monitoring 3m31s Warning FailedKillPod pod/node-exporter-h6nww error killing pod: [failed to \"KillContainer\" for \"kube-rbac-proxy\" with KillContainerError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\", failed to \"KillPodSandbox\" for \"27cde0ab-5a07-459d-b594-ea3b9bd4d78c\" with KillPodSandboxError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\"]",
7 | "monitoring 21m Warning FailedKillPod pod/node-exporter-h6nww error killing pod: [failed to \"KillContainer\" for \"kube-rbac-proxy\" with KillContainerError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\", failed to \"KillPodSandbox\" for \"27cde0ab-5a07-459d-b594-ea3b9bd4d78c\" with KillPodSandboxError: \"rpc error: code = DeadlineExceeded desc = failed to stop container \\\"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\\\": an error occurs during waiting for container \\\"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\\\" to be killed: wait container \\\"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\\\": context deadline exceeded\"]",
8 | "monitoring 12m Warning FailedKillPod pod/node-exporter-h6nww error killing pod: [failed to \"KillContainer\" for \"kube-rbac-proxy\" with KillContainerError: \"rpc error: code = DeadlineExceeded desc = an error occurs during waiting for container \\\"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\\\" to be killed: wait container \\\"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\\\": context deadline exceeded\", failed to \"KillPodSandbox\" for \"27cde0ab-5a07-459d-b594-ea3b9bd4d78c\" with KillPodSandboxError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\"]",
9 | ""
10 | ]
11 |
--------------------------------------------------------------------------------
/manifests/prometheusOperator-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: controller
6 | app.kubernetes.io/name: prometheus-operator
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.53.1
9 | name: prometheus-operator
10 | namespace: monitoring
11 | spec:
12 | replicas: 1
13 | selector:
14 | matchLabels:
15 | app.kubernetes.io/component: controller
16 | app.kubernetes.io/name: prometheus-operator
17 | app.kubernetes.io/part-of: kube-prometheus
18 | template:
19 | metadata:
20 | annotations:
21 | kubectl.kubernetes.io/default-container: prometheus-operator
22 | labels:
23 | app.kubernetes.io/component: controller
24 | app.kubernetes.io/name: prometheus-operator
25 | app.kubernetes.io/part-of: kube-prometheus
26 | app.kubernetes.io/version: 0.53.1
27 | spec:
28 | containers:
29 | - args:
30 | - --kubelet-service=kube-system/kubelet
31 | - --prometheus-config-reloader=quay.io/prometheus-operator/prometheus-config-reloader:v0.53.1
32 | image: quay.io/prometheus-operator/prometheus-operator:v0.53.1
33 | name: prometheus-operator
34 | ports:
35 | - containerPort: 8080
36 | name: http
37 | resources:
38 | limits:
39 | cpu: 200m
40 | memory: 200Mi
41 | requests:
42 | cpu: 100m
43 | memory: 100Mi
44 | securityContext:
45 | allowPrivilegeEscalation: false
46 | - args:
47 | - --logtostderr
48 | - --secure-listen-address=:8443
49 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
50 | - --upstream=http://127.0.0.1:8080/
51 | image: quay.io/brancz/kube-rbac-proxy:v0.11.0
52 | name: kube-rbac-proxy
53 | ports:
54 | - containerPort: 8443
55 | name: https
56 | resources:
57 | limits:
58 | cpu: 20m
59 | memory: 40Mi
60 | requests:
61 | cpu: 10m
62 | memory: 20Mi
63 | securityContext:
64 | runAsGroup: 65532
65 | runAsNonRoot: true
66 | runAsUser: 65532
67 | nodeSelector:
68 | kubernetes.io/os: linux
69 | securityContext:
70 | runAsNonRoot: true
71 | runAsUser: 65534
72 | serviceAccountName: prometheus-operator
73 |
--------------------------------------------------------------------------------
/client/demoData/logs.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | "kube-system",
4 | "112s",
5 | "Warning",
6 | "Unhealthy",
7 | "pod/metrics-server-847dcc659d-brstf",
8 | "Readiness probe failed: HTTP probe failed with statuscode: 500"
9 | ],
10 | [
11 | "monitoring",
12 | "116s",
13 | "Warning",
14 | "Unhealthy",
15 | "pod/alertmanager-main-1",
16 | "Readiness probe failed: Get \"http://10.244.2.10:9093/-/ready\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)"
17 | ],
18 | [
19 | "monitoring",
20 | "6m55s",
21 | "Warning",
22 | "Unhealthy",
23 | "pod/alertmanager-main-1",
24 | "Readiness probe failed: Get \"http://10.244.2.10:9093/-/ready\": dial tcp 10.244.2.10:9093: i/o timeout (Client.Timeout exceeded while awaiting headers)"
25 | ],
26 | [
27 | "monitoring",
28 | "3m31s",
29 | "Warning",
30 | "FailedKillPod",
31 | "pod/node-exporter-h6nww",
32 | "error killing pod: [failed to \"KillContainer\" for \"kube-rbac-proxy\" with KillContainerError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\", failed to \"KillPodSandbox\" for \"27cde0ab-5a07-459d-b594-ea3b9bd4d78c\" with KillPodSandboxError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\"]"
33 | ],
34 | [
35 | "monitoring",
36 | "21m",
37 | "Warning",
38 | "FailedKillPod",
39 | "pod/node-exporter-h6nww",
40 | "error killing pod: [failed to \"KillContainer\" for \"kube-rbac-proxy\" with KillContainerError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\", failed to \"KillPodSandbox\" for \"27cde0ab-5a07-459d-b594-ea3b9bd4d78c\" with KillPodSandboxError: \"rpc error: code = DeadlineExceeded desc = failed to stop container \"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\": an error occurs during waiting for container \"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\" to be killed: wait container \"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\": context deadline exceeded\"]"
41 | ],
42 | [
43 | "monitoring",
44 | "12m",
45 | "Warning",
46 | "FailedKillPod",
47 | "pod/node-exporter-h6nww",
48 | "error killing pod: [failed to \"KillContainer\" for \"kube-rbac-proxy\" with KillContainerError: \"rpc error: code = DeadlineExceeded desc = an error occurs during waiting for container \"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\" to be killed: wait container \"7cad4627df92d5c09ac630040ccfab6c76e4e0ca12bc55390faed4cec1e10935\": context deadline exceeded\", failed to \"KillPodSandbox\" for \"27cde0ab-5a07-459d-b594-ea3b9bd4d78c\" with KillPodSandboxError: \"rpc error: code = DeadlineExceeded desc = context deadline exceeded\"]"
49 | ]
50 | ]
51 |
--------------------------------------------------------------------------------
/server/controllers/getLists.js:
--------------------------------------------------------------------------------
1 | const k8s = require('@kubernetes/client-node');
2 | // K8s API
3 | const kc = new k8s.KubeConfig();
4 | kc.loadFromDefault();
5 | const k8sApiCore = kc.makeApiClient(k8s.CoreV1Api);
6 | const k8sApiApps = kc.makeApiClient(k8s.AppsV1Api);
7 |
8 | // using k8s API client, get list of components in cluster for homepage
9 | const getLists = {
10 | // get list of all nodes in cluster
11 | getNodesList: (getNodesList = (req, res, next) => {
12 | k8sApiCore
13 | .listNode('default')
14 | .then((data) => {
15 | res.locals.nodeList = data;
16 | k8sApiCore
17 | .listComponentStatus()
18 | .then((data) => {
19 | res.locals.nodeList.nodeProcesses = data;
20 | return next();
21 | })
22 | .catch((err) => {
23 | res
24 | .status(500)
25 | .send(
26 | `error found in get request to /nodeList at listComponentStatus(), ${err}`
27 | );
28 | });
29 | })
30 | .catch((err) => {
31 | res
32 | .status(500)
33 | .send(`error found in get request to /nodeList at listNode, ${err}`);
34 | });
35 | }),
36 |
37 | // get list of all namespaces in cluster
38 | getNamespaceList: (getNamespaceList = (req, res, next) => {
39 | k8sApiCore.listNamespace().then((data) => {
40 | // console.log(data);
41 | res.locals.namespaces = data.body;
42 | return next();
43 | });
44 | }),
45 |
46 | // get list of all deployments in cluster
47 | getDeploymentsList: (getDeploymentsList = (req, res, next) => {
48 | k8sApiApps
49 | .listDeploymentForAllNamespaces()
50 | .then((data) => {
51 | res.locals.deploymentsList = data.body;
52 | return next();
53 | })
54 | .catch((err) =>
55 | next({
56 | log: 'error in get request to /deploymentsList',
57 | message: { err: 'An error occured in getLists.getDeploymentsList' },
58 | })
59 | );
60 | }),
61 |
62 | // get list of all services in cluster
63 | getServicesList: (getServicesList = (req, res, next) => {
64 | k8sApiCore
65 | .listServiceForAllNamespaces()
66 | .then((data) => {
67 | res.locals.servicesList = data.body;
68 | return next();
69 | })
70 | .catch((err) => {
71 | next({
72 | log: 'error in get request to /serviceList',
73 | message: { err: err.message },
74 | });
75 | });
76 | }),
77 |
78 | // get list of all pods in cluster
79 | getPodsList: (getPodsList = (req, res, next) => {
80 | k8sApiCore
81 | .listPodForAllNamespaces()
82 | .then((data) => {
83 | res.locals.podsList = data.body;
84 | return next();
85 | })
86 | .catch((err) => {
87 | next({
88 | log: 'error in get request to /podList',
89 | message: { err: err.message },
90 | });
91 | });
92 | }),
93 | };
94 |
95 | module.exports = getLists;
96 |
--------------------------------------------------------------------------------
/manifests/prometheus-roleSpecificNamespaces.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | items:
3 | - apiVersion: rbac.authorization.k8s.io/v1
4 | kind: Role
5 | metadata:
6 | labels:
7 | app.kubernetes.io/component: prometheus
8 | app.kubernetes.io/instance: k8s
9 | app.kubernetes.io/name: prometheus
10 | app.kubernetes.io/part-of: kube-prometheus
11 | app.kubernetes.io/version: 2.32.1
12 | name: prometheus-k8s
13 | namespace: default
14 | rules:
15 | - apiGroups:
16 | - ""
17 | resources:
18 | - services
19 | - endpoints
20 | - pods
21 | verbs:
22 | - get
23 | - list
24 | - watch
25 | - apiGroups:
26 | - extensions
27 | resources:
28 | - ingresses
29 | verbs:
30 | - get
31 | - list
32 | - watch
33 | - apiGroups:
34 | - networking.k8s.io
35 | resources:
36 | - ingresses
37 | verbs:
38 | - get
39 | - list
40 | - watch
41 | - apiVersion: rbac.authorization.k8s.io/v1
42 | kind: Role
43 | metadata:
44 | labels:
45 | app.kubernetes.io/component: prometheus
46 | app.kubernetes.io/instance: k8s
47 | app.kubernetes.io/name: prometheus
48 | app.kubernetes.io/part-of: kube-prometheus
49 | app.kubernetes.io/version: 2.32.1
50 | name: prometheus-k8s
51 | namespace: kube-system
52 | rules:
53 | - apiGroups:
54 | - ""
55 | resources:
56 | - services
57 | - endpoints
58 | - pods
59 | verbs:
60 | - get
61 | - list
62 | - watch
63 | - apiGroups:
64 | - extensions
65 | resources:
66 | - ingresses
67 | verbs:
68 | - get
69 | - list
70 | - watch
71 | - apiGroups:
72 | - networking.k8s.io
73 | resources:
74 | - ingresses
75 | verbs:
76 | - get
77 | - list
78 | - watch
79 | - apiVersion: rbac.authorization.k8s.io/v1
80 | kind: Role
81 | metadata:
82 | labels:
83 | app.kubernetes.io/component: prometheus
84 | app.kubernetes.io/instance: k8s
85 | app.kubernetes.io/name: prometheus
86 | app.kubernetes.io/part-of: kube-prometheus
87 | app.kubernetes.io/version: 2.32.1
88 | name: prometheus-k8s
89 | namespace: monitoring
90 | rules:
91 | - apiGroups:
92 | - ""
93 | resources:
94 | - services
95 | - endpoints
96 | - pods
97 | verbs:
98 | - get
99 | - list
100 | - watch
101 | - apiGroups:
102 | - extensions
103 | resources:
104 | - ingresses
105 | verbs:
106 | - get
107 | - list
108 | - watch
109 | - apiGroups:
110 | - networking.k8s.io
111 | resources:
112 | - ingresses
113 | verbs:
114 | - get
115 | - list
116 | - watch
117 | kind: RoleList
118 |
--------------------------------------------------------------------------------
/manifests/prometheusAdapter-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: metrics-adapter
6 | app.kubernetes.io/name: prometheus-adapter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.9.1
9 | name: prometheus-adapter
10 | namespace: monitoring
11 | spec:
12 | replicas: 2
13 | selector:
14 | matchLabels:
15 | app.kubernetes.io/component: metrics-adapter
16 | app.kubernetes.io/name: prometheus-adapter
17 | app.kubernetes.io/part-of: kube-prometheus
18 | strategy:
19 | rollingUpdate:
20 | maxSurge: 1
21 | maxUnavailable: 1
22 | template:
23 | metadata:
24 | labels:
25 | app.kubernetes.io/component: metrics-adapter
26 | app.kubernetes.io/name: prometheus-adapter
27 | app.kubernetes.io/part-of: kube-prometheus
28 | app.kubernetes.io/version: 0.9.1
29 | spec:
30 | containers:
31 | - args:
32 | - --cert-dir=/var/run/serving-cert
33 | - --config=/etc/adapter/config.yaml
34 | - --logtostderr=true
35 | - --metrics-relist-interval=1m
36 | - --prometheus-url=http://prometheus-k8s.monitoring.svc:9090/
37 | - --secure-port=6443
38 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA
39 | image: k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1
40 | name: prometheus-adapter
41 | ports:
42 | - containerPort: 6443
43 | resources:
44 | limits:
45 | cpu: 250m
46 | memory: 180Mi
47 | requests:
48 | cpu: 102m
49 | memory: 180Mi
50 | volumeMounts:
51 | - mountPath: /tmp
52 | name: tmpfs
53 | readOnly: false
54 | - mountPath: /var/run/serving-cert
55 | name: volume-serving-cert
56 | readOnly: false
57 | - mountPath: /etc/adapter
58 | name: config
59 | readOnly: false
60 | nodeSelector:
61 | kubernetes.io/os: linux
62 | serviceAccountName: prometheus-adapter
63 | volumes:
64 | - emptyDir: {}
65 | name: tmpfs
66 | - emptyDir: {}
67 | name: volume-serving-cert
68 | - configMap:
69 | name: adapter-config
70 | name: config
71 |
--------------------------------------------------------------------------------
/client/demoData/networkTransmitBytes.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "success",
3 | "data": {
4 | "resultType": "matrix",
5 | "result": [
6 | {
7 | "metric": {},
8 | "values": [
9 | [1652291563.841, "108047.37818671988"],
10 | [1652291863.841, "107245.73685371155"],
11 | [1652292163.841, "115793.2121276655"],
12 | [1652292463.841, "107417.87263466745"],
13 | [1652292763.841, "106885.49746078838"],
14 | [1652293063.841, "107819.25092709798"],
15 | [1652293363.841, "107500.40455804803"],
16 | [1652293663.841, "105742.52568548956"],
17 | [1652293963.841, "106094.6490355648"],
18 | [1652294263.841, "105952.9800882636"],
19 | [1652294563.841, "106021.33717589565"],
20 | [1652294863.841, "108055.97356071987"],
21 | [1652295163.841, "107324.71147641522"],
22 | [1652295463.841, "104939.25937906258"],
23 | [1652295763.841, "106672.7025092899"],
24 | [1652296063.841, "106815.68797786515"],
25 | [1652296363.841, "105591.91965435349"],
26 | [1652296663.841, "107217.4807600847"],
27 | [1652296963.841, "106441.27638208476"],
28 | [1652297263.841, "283298.7828569581"],
29 | [1652297563.841, "105449.50820170264"],
30 | [1652297863.841, "106187.50164594207"],
31 | [1652298163.841, "105299.54218415526"],
32 | [1652298463.841, "106133.66994888916"],
33 | [1652298763.841, "105563.59034610921"],
34 | [1652299063.841, "105051.59433548707"],
35 | [1652299363.841, "105517.20021135123"],
36 | [1652299663.841, "155268.34741625754"],
37 | [1652299963.841, "111418.85300662197"],
38 | [1652300263.841, "262735.6354487903"],
39 | [1652300563.841, "106789.99485058717"],
40 | [1652300863.841, "123126.63090096643"],
41 | [1652301163.841, "115045.53009920717"],
42 | [1652301463.841, "114698.06225393618"],
43 | [1652301763.841, "108580.04575136791"],
44 | [1652302063.841, "110642.62421863398"],
45 | [1652302363.841, "106355.11955381442"],
46 | [1652302663.841, "104785.11794104397"],
47 | [1652302963.841, "110237.29251806396"],
48 | [1652303263.841, "115000.76219683315"],
49 | [1652303563.841, "112675.5176313883"],
50 | [1652303863.841, "107342.67801346486"],
51 | [1652304163.841, "107589.0264483073"],
52 | [1652304463.841, "104962.70096899717"],
53 | [1652304763.841, "108172.17537240099"],
54 | [1652305063.841, "109968.55106923045"],
55 | [1652305363.841, "107879.54411274314"],
56 | [1652305663.841, "110755.64498746092"],
57 | [1652305963.841, "108127.34814925384"]
58 | ]
59 | }
60 | ]
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const client = require('prom-client');
3 |
4 | const path = require('path');
5 |
6 | const getLists = require('./controllers/getLists');
7 | const prometheusRouter = require('./routers/prometheusRouter');
8 | const alertsController = require('./controllers/alertsController');
9 | const logsController = require('./controllers/logsController');
10 |
11 | const PORT = process.env.PORT || 3001;
12 | const app = express();
13 |
14 | // Collect default metrics using Prom API
15 | client.collectDefaultMetrics();
16 |
17 | app.use(express.json());
18 | app.use(express.urlencoded({ extended: true }));
19 | app.use(express.static(path.resolve(__dirname, '../build')));
20 |
21 | app.get('/api/fetchMetrics', async (req, res) => {
22 | const metrics = await client.register.getMetricsAsJSON();
23 | res.status(200).json(metrics);
24 | });
25 |
26 | app.get('/api/nodesList', getLists.getNodesList, (req, res) => {
27 | res.status(201).send(res.locals.nodeList.response.body.items);
28 | });
29 |
30 | app.get('/api/deploymentsList', getLists.getDeploymentsList, (req, res) => {
31 | res.status(201).send(res.locals.deploymentsList.items);
32 | });
33 |
34 | app.get('/api/podsList', getLists.getPodsList, (req, res) => {
35 | res.status(201).send(res.locals.podsList.items);
36 | });
37 |
38 | app.get('/api/servicesList', getLists.getServicesList, (req, res) => {
39 | res.status(201).send(res.locals.servicesList.items);
40 | });
41 |
42 | app.get('/api/namespaceList', getLists.getNamespaceList, (req, res) => {
43 | res.status(201).send(res.locals.namespaces);
44 | });
45 |
46 | app.use('/api/prometheus', prometheusRouter);
47 |
48 | app.get('/api/alerts', alertsController.fetchAlerts, (req, res) => {
49 | res.status(201).json(res.locals.alerts);
50 | });
51 |
52 | app.get('/api/logs', logsController.getLogs, (req, res) => {
53 | res.status(200).json(res.locals.logs);
54 | });
55 |
56 | //For all routes access the index.html file
57 | app.get('*', (req, res) => {
58 | res
59 | .status(200)
60 | .sendFile('index.html', { root: path.join(__dirname, '../build') });
61 | });
62 |
63 | // Global route handler
64 | app.use('*', (req, res) => {
65 | console.log('Page not found.');
66 | return res.status(404).send('Page not found.');
67 | });
68 |
69 | // Global error handler
70 | app.use(defaultErrorHandler);
71 | function defaultErrorHandler(err, req, res, next) {
72 | const defaultErr = {
73 | log: 'Error! at the Disco',
74 | status: 400,
75 | message: {
76 | err: "An error occurred somewhere where it isn't being handled",
77 | },
78 | };
79 | const errorObj = Object.assign(defaultErr, err);
80 | console.log(errorObj.log);
81 | return res.status(errorObj.status).send(JSON.stringify(errorObj.message));
82 | }
83 |
84 | module.exports = app.listen(PORT, () =>
85 | console.log(`App is listening on port ${PORT}`)
86 | );
87 |
--------------------------------------------------------------------------------
/client/demoData/networkTransmitFormatted.json:
--------------------------------------------------------------------------------
1 | {
2 | "networkTransmitData": {
3 | "timestamps": [
4 | "17:52",
5 | "17:57",
6 | "18:02",
7 | "18:07",
8 | "18:12",
9 | "18:17",
10 | "18:22",
11 | "18:27",
12 | "18:32",
13 | "18:37",
14 | "18:42",
15 | "18:47",
16 | "18:52",
17 | "18:57",
18 | "19:02",
19 | "19:07",
20 | "19:12",
21 | "19:17",
22 | "19:22",
23 | "19:27",
24 | "19:32",
25 | "19:37",
26 | "19:42",
27 | "19:47",
28 | "19:52",
29 | "19:57",
30 | "20:02",
31 | "20:07",
32 | "20:12",
33 | "20:17",
34 | "20:22",
35 | "20:27",
36 | "20:32",
37 | "20:37",
38 | "20:42",
39 | "20:47",
40 | "20:52",
41 | "20:57",
42 | "21:02",
43 | "21:07",
44 | "21:12",
45 | "21:17",
46 | "21:22",
47 | "21:27",
48 | "21:32",
49 | "21:37",
50 | "21:42",
51 | "21:47",
52 | "21:52"
53 | ],
54 | "seriesLabels": ["Cluster"],
55 | "seriesValues": [
56 | [
57 | "108047.37818671988",
58 | "107245.73685371155",
59 | "115793.2121276655",
60 | "107417.87263466745",
61 | "106885.49746078838",
62 | "107819.25092709798",
63 | "107500.40455804803",
64 | "105742.52568548956",
65 | "106094.6490355648",
66 | "105952.9800882636",
67 | "106021.33717589565",
68 | "108055.97356071987",
69 | "107324.71147641522",
70 | "104939.25937906258",
71 | "106672.7025092899",
72 | "106815.68797786515",
73 | "105591.91965435349",
74 | "107217.4807600847",
75 | "106441.27638208476",
76 | "283298.7828569581",
77 | "105449.50820170264",
78 | "106187.50164594207",
79 | "105299.54218415526",
80 | "106133.66994888916",
81 | "105563.59034610921",
82 | "105051.59433548707",
83 | "105517.20021135123",
84 | "155268.34741625754",
85 | "111418.85300662197",
86 | "262735.6354487903",
87 | "106789.99485058717",
88 | "123126.63090096643",
89 | "115045.53009920717",
90 | "114698.06225393618",
91 | "108580.04575136791",
92 | "110642.62421863398",
93 | "106355.11955381442",
94 | "104785.11794104397",
95 | "110237.29251806396",
96 | "115000.76219683315",
97 | "112675.5176313883",
98 | "107342.67801346486",
99 | "107589.0264483073",
100 | "104962.70096899717",
101 | "108172.17537240099",
102 | "109968.55106923045",
103 | "107879.54411274314",
104 | "110755.64498746092",
105 | "108127.34814925384"
106 | ]
107 | ]
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/client/demoData/clusterFreeMemory.json:
--------------------------------------------------------------------------------
1 | {
2 | "clusterFreeMemory": {
3 | "status": "success",
4 | "data": {
5 | "resultType": "matrix",
6 | "result": [
7 | {
8 | "metric": {},
9 | "values": [
10 | [1652291563.841, "28732518.336729288"],
11 | [1652291863.841, "24521262.881017044"],
12 | [1652292163.841, "34801220.98376262"],
13 | [1652292463.841, "36147488.107746184"],
14 | [1652292763.841, "37328267.19789913"],
15 | [1652293063.841, "35642676.622363836"],
16 | [1652293363.841, "20197415.79499869"],
17 | [1652293663.841, "25246611.159717944"],
18 | [1652293963.841, "30263291.55848983"],
19 | [1652294263.841, "32869001.324907307"],
20 | [1652294563.841, "26834946.57640039"],
21 | [1652294863.841, "34017708.59762251"],
22 | [1652295163.841, "30417933.8965487"],
23 | [1652295463.841, "38568429.235408254"],
24 | [1652295763.841, "29424999.51126674"],
25 | [1652296063.841, "35652417.73866486"],
26 | [1652296363.841, "32139538.03160796"],
27 | [1652296663.841, "25097563.78441781"],
28 | [1652296963.841, "41108270.286731064"],
29 | [1652297263.841, "38569076.034669906"],
30 | [1652297563.841, "21408239.890216246"],
31 | [1652297863.841, "42275849.78322687"],
32 | [1652298163.841, "32006736.35061108"],
33 | [1652298463.841, "27342988.566747077"],
34 | [1652298763.841, "24834742.6172766"],
35 | [1652299063.841, "35004300.30304763"],
36 | [1652299363.841, "31325654.700019717"],
37 | [1652299663.841, "27958905.614368975"],
38 | [1652299963.841, "34151699.54849255"],
39 | [1652300263.841, "29878308.689677678"],
40 | [1652300563.841, "40291809.63402227"],
41 | [1652300863.841, "37786650.26353454"],
42 | [1652301163.841, "38750383.8075348"],
43 | [1652301463.841, "46784968.02187943"],
44 | [1652301763.841, "40958563.96381239"],
45 | [1652302063.841, "42393852.00534156"],
46 | [1652302363.841, "37972578.485541746"],
47 | [1652302663.841, "29439152.207369093"],
48 | [1652302963.841, "26436434.97497619"],
49 | [1652303263.841, "55710204.26658526"],
50 | [1652303563.841, "43009685.38784952"],
51 | [1652303863.841, "53450032.54298615"],
52 | [1652304163.841, "42945617.4370615"],
53 | [1652304463.841, "46791077.49804035"],
54 | [1652304763.841, "61634645.5937919"],
55 | [1652305063.841, "46983567.086350314"],
56 | [1652305363.841, "61861330.082515344"],
57 | [1652305663.841, "35381744.04812537"],
58 | [1652305963.841, "44126085.03915976"]
59 | ]
60 | }
61 | ]
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/coverage/lcov-report/block-navigation.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var jumpToCode = (function init() {
3 | // Classes of code we would like to highlight in the file view
4 | var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5 |
6 | // Elements to highlight in the file listing view
7 | var fileListingElements = ['td.pct.low'];
8 |
9 | // We don't want to select elements that are direct descendants of another match
10 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11 |
12 | // Selecter that finds elements on the page to which we can jump
13 | var selector =
14 | fileListingElements.join(', ') +
15 | ', ' +
16 | notSelector +
17 | missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18 |
19 | // The NodeList of matching elements
20 | var missingCoverageElements = document.querySelectorAll(selector);
21 |
22 | var currentIndex;
23 |
24 | function toggleClass(index) {
25 | missingCoverageElements
26 | .item(currentIndex)
27 | .classList.remove('highlighted');
28 | missingCoverageElements.item(index).classList.add('highlighted');
29 | }
30 |
31 | function makeCurrent(index) {
32 | toggleClass(index);
33 | currentIndex = index;
34 | missingCoverageElements.item(index).scrollIntoView({
35 | behavior: 'smooth',
36 | block: 'center',
37 | inline: 'center'
38 | });
39 | }
40 |
41 | function goToPrevious() {
42 | var nextIndex = 0;
43 | if (typeof currentIndex !== 'number' || currentIndex === 0) {
44 | nextIndex = missingCoverageElements.length - 1;
45 | } else if (missingCoverageElements.length > 1) {
46 | nextIndex = currentIndex - 1;
47 | }
48 |
49 | makeCurrent(nextIndex);
50 | }
51 |
52 | function goToNext() {
53 | var nextIndex = 0;
54 |
55 | if (
56 | typeof currentIndex === 'number' &&
57 | currentIndex < missingCoverageElements.length - 1
58 | ) {
59 | nextIndex = currentIndex + 1;
60 | }
61 |
62 | makeCurrent(nextIndex);
63 | }
64 |
65 | return function jump(event) {
66 | if (
67 | document.getElementById('fileSearch') === document.activeElement &&
68 | document.activeElement != null
69 | ) {
70 | // if we're currently focused on the search input, we don't want to navigate
71 | return;
72 | }
73 |
74 | switch (event.which) {
75 | case 78: // n
76 | case 74: // j
77 | goToNext();
78 | break;
79 | case 66: // b
80 | case 75: // k
81 | case 80: // p
82 | goToPrevious();
83 | break;
84 | }
85 | };
86 | })();
87 | window.addEventListener('keydown', jumpToCode);
88 |
--------------------------------------------------------------------------------
/client/homepage/components/Statuses/NodesStatus.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import Box from '@mui/material/Box';
4 | import Card from '@mui/material/Card';
5 | import Popper from '@mui/material/Popper';
6 | import Paper from '@mui/material/Paper';
7 | import Typography from '@mui/material/Typography';
8 | import ComponentWrapper from '../../../utils/ComponentWrapper';
9 | import parseStatus from '../../../utils/parseStatus';
10 |
11 | const mapStateToProps = ({ nodes }) => {
12 | return nodes;
13 | };
14 |
15 | const NodesStatus = ({ items }) => {
16 | const nodesObj = parseStatus(items);
17 | // const [nodesObj, setNodesObj] = React.useState(parseStatus(items));
18 | const [anchorEl, setAnchorEl] = React.useState(null);
19 | const [open, setOpen] = React.useState(false);
20 | const [placement, setPlacement] = React.useState();
21 | const [popperText, setPopperText] = React.useState([]);
22 |
23 | // Handleclick for popper
24 | const handleClick = (event, key) => {
25 | console.log('key', key);
26 | let popperHTML = [];
27 | popperHTML.push(Name: {key}
);
28 | for (let condition of Object.keys(nodesObj[key])) {
29 | popperHTML.push(
30 |
31 | {condition}: {nodesObj[key][condition]}
32 |
33 | );
34 | setPopperText(popperHTML);
35 | }
36 | setAnchorEl(event.currentTarget);
37 | setOpen((previousOpen) => !previousOpen);
38 | };
39 |
40 | // Generate node boxes
41 | const NodeBoxes = [];
42 | for (let key of Object.keys(nodesObj)) {
43 | let boxStyle = {
44 | outline: 'none',
45 | backgroundColor: '#3dce2d',
46 | border: '1px solid #39ba2a',
47 | borderRadius: '3px',
48 | width: '30px',
49 | height: '30px',
50 | margin: '1px',
51 | };
52 |
53 | if (nodesObj[key]['Ready'] === 'False') {
54 | boxStyle['backgroundColor'] = '#ea2315';
55 | boxStyle['border'] = '1px solid #d82c20';
56 | } else if (nodesObj[key]['Ready'] === 'Unknown') {
57 | boxStyle['backgroundColor'] = 'yellow';
58 | }
59 |
60 | NodeBoxes.push(
61 |
62 |
75 |
76 | );
77 | }
78 |
79 | return (
80 |
81 |
82 |
83 | {popperText}
84 |
85 |
86 |
87 |
88 | {NodeBoxes}
89 |
90 |
91 | );
92 | };
93 |
94 | export default connect(mapStateToProps)(NodesStatus);
95 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | name: kube-state-metrics
10 | namespace: monitoring
11 | spec:
12 | replicas: 1
13 | selector:
14 | matchLabels:
15 | app.kubernetes.io/component: exporter
16 | app.kubernetes.io/name: kube-state-metrics
17 | app.kubernetes.io/part-of: kube-prometheus
18 | template:
19 | metadata:
20 | annotations:
21 | kubectl.kubernetes.io/default-container: kube-state-metrics
22 | labels:
23 | app.kubernetes.io/component: exporter
24 | app.kubernetes.io/name: kube-state-metrics
25 | app.kubernetes.io/part-of: kube-prometheus
26 | app.kubernetes.io/version: 2.3.0
27 | spec:
28 | containers:
29 | - args:
30 | - --host=127.0.0.1
31 | - --port=8081
32 | - --telemetry-host=127.0.0.1
33 | - --telemetry-port=8082
34 | image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.3.0
35 | name: kube-state-metrics
36 | resources:
37 | limits:
38 | cpu: 100m
39 | memory: 250Mi
40 | requests:
41 | cpu: 10m
42 | memory: 190Mi
43 | securityContext:
44 | runAsUser: 65534
45 | - args:
46 | - --logtostderr
47 | - --secure-listen-address=:8443
48 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
49 | - --upstream=http://127.0.0.1:8081/
50 | image: quay.io/brancz/kube-rbac-proxy:v0.11.0
51 | name: kube-rbac-proxy-main
52 | ports:
53 | - containerPort: 8443
54 | name: https-main
55 | resources:
56 | limits:
57 | cpu: 40m
58 | memory: 40Mi
59 | requests:
60 | cpu: 20m
61 | memory: 20Mi
62 | securityContext:
63 | runAsGroup: 65532
64 | runAsNonRoot: true
65 | runAsUser: 65532
66 | - args:
67 | - --logtostderr
68 | - --secure-listen-address=:9443
69 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
70 | - --upstream=http://127.0.0.1:8082/
71 | image: quay.io/brancz/kube-rbac-proxy:v0.11.0
72 | name: kube-rbac-proxy-self
73 | ports:
74 | - containerPort: 9443
75 | name: https-self
76 | resources:
77 | limits:
78 | cpu: 20m
79 | memory: 40Mi
80 | requests:
81 | cpu: 10m
82 | memory: 20Mi
83 | securityContext:
84 | runAsGroup: 65532
85 | runAsNonRoot: true
86 | runAsUser: 65532
87 | nodeSelector:
88 | kubernetes.io/os: linux
89 | serviceAccountName: kube-state-metrics
90 |
--------------------------------------------------------------------------------
/client/alertspage/ExpandableRow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Box,
4 | Typography,
5 | Chip,
6 | Table,
7 | TableHead,
8 | TableRow,
9 | TableCell,
10 | TableBody,
11 | } from '@mui/material';
12 |
13 | function ExpandableRow({ description, summary, alerts, open }) {
14 | const alertsRow = (alert) => {
15 | return (
16 |
17 |
18 |
19 | Active at: {alert.activeAt}
20 |
21 |
25 |
26 |
27 |
28 |
29 | Container
30 | Instance
31 | Job
32 | Namespace
33 | Statefulset
34 | Description
35 | Summary
36 |
37 |
38 |
39 |
40 |
41 | {alert.labels.container}
42 |
43 | {alert.labels.instance}
44 | {alert.labels.job}
45 | {alert.labels.namespace}
46 | {alert.labels.statefulset}
47 | {alert.annotations.description}
48 | {alert.annotations.summary}
49 |
50 |
51 |
52 |
53 | );
54 | };
55 | return (
56 |
57 |
58 | {/* */}
59 |
60 |
61 | Annotations
62 |
63 |
64 |
65 |
66 | Description
67 | {/* Runbook Url */}
68 | Summary
69 |
70 |
71 |
72 |
73 |
74 | {description}
75 |
76 | {/* {annotations?.runbook_url} */}
77 | {summary}
78 |
79 |
80 |
81 |
82 | {alerts?.length > 0 && (
83 |
84 |
85 | Alerts
86 |
87 | {alerts.map((alert) => alertsRow(alert))}
88 |
89 | )}
90 |
91 |
92 | );
93 | }
94 |
95 | export default ExpandableRow;
96 |
--------------------------------------------------------------------------------
/manifests/kubeStateMetrics-prometheusRule.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: PrometheusRule
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-state-metrics
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 2.3.0
9 | prometheus: k8s
10 | role: alert-rules
11 | name: kube-state-metrics-rules
12 | namespace: monitoring
13 | spec:
14 | groups:
15 | - name: kube-state-metrics
16 | rules:
17 | - alert: KubeStateMetricsListErrors
18 | annotations:
19 | description: kube-state-metrics is experiencing errors at an elevated rate
20 | in list operations. This is likely causing it to not be able to expose metrics
21 | about Kubernetes objects correctly or at all.
22 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/kube-state-metrics/kubestatemetricslisterrors
23 | summary: kube-state-metrics is experiencing errors in list operations.
24 | expr: |
25 | (sum(rate(kube_state_metrics_list_total{job="kube-state-metrics",result="error"}[5m]))
26 | /
27 | sum(rate(kube_state_metrics_list_total{job="kube-state-metrics"}[5m])))
28 | > 0.01
29 | for: 15m
30 | labels:
31 | severity: critical
32 | - alert: KubeStateMetricsWatchErrors
33 | annotations:
34 | description: kube-state-metrics is experiencing errors at an elevated rate
35 | in watch operations. This is likely causing it to not be able to expose
36 | metrics about Kubernetes objects correctly or at all.
37 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/kube-state-metrics/kubestatemetricswatcherrors
38 | summary: kube-state-metrics is experiencing errors in watch operations.
39 | expr: |
40 | (sum(rate(kube_state_metrics_watch_total{job="kube-state-metrics",result="error"}[5m]))
41 | /
42 | sum(rate(kube_state_metrics_watch_total{job="kube-state-metrics"}[5m])))
43 | > 0.01
44 | for: 15m
45 | labels:
46 | severity: critical
47 | - alert: KubeStateMetricsShardingMismatch
48 | annotations:
49 | description: kube-state-metrics pods are running with different --total-shards
50 | configuration, some Kubernetes objects may be exposed multiple times or
51 | not exposed at all.
52 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/kube-state-metrics/kubestatemetricsshardingmismatch
53 | summary: kube-state-metrics sharding is misconfigured.
54 | expr: |
55 | stdvar (kube_state_metrics_total_shards{job="kube-state-metrics"}) != 0
56 | for: 15m
57 | labels:
58 | severity: critical
59 | - alert: KubeStateMetricsShardsMissing
60 | annotations:
61 | description: kube-state-metrics shards are missing, some Kubernetes objects
62 | are not being exposed.
63 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/kube-state-metrics/kubestatemetricsshardsmissing
64 | summary: kube-state-metrics shards are missing.
65 | expr: |
66 | 2^max(kube_state_metrics_total_shards{job="kube-state-metrics"}) - 1
67 | -
68 | sum( 2 ^ max by (shard_ordinal) (kube_state_metrics_shard_ordinal{job="kube-state-metrics"}) )
69 | != 0
70 | for: 15m
71 | labels:
72 | severity: critical
73 |
--------------------------------------------------------------------------------
/manifests/blackboxExporter-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: blackbox-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 0.19.0
9 | name: blackbox-exporter
10 | namespace: monitoring
11 | spec:
12 | replicas: 1
13 | selector:
14 | matchLabels:
15 | app.kubernetes.io/component: exporter
16 | app.kubernetes.io/name: blackbox-exporter
17 | app.kubernetes.io/part-of: kube-prometheus
18 | template:
19 | metadata:
20 | annotations:
21 | kubectl.kubernetes.io/default-container: blackbox-exporter
22 | labels:
23 | app.kubernetes.io/component: exporter
24 | app.kubernetes.io/name: blackbox-exporter
25 | app.kubernetes.io/part-of: kube-prometheus
26 | app.kubernetes.io/version: 0.19.0
27 | spec:
28 | containers:
29 | - args:
30 | - --config.file=/etc/blackbox_exporter/config.yml
31 | - --web.listen-address=:19115
32 | image: quay.io/prometheus/blackbox-exporter:v0.19.0
33 | name: blackbox-exporter
34 | ports:
35 | - containerPort: 19115
36 | name: http
37 | resources:
38 | limits:
39 | cpu: 20m
40 | memory: 40Mi
41 | requests:
42 | cpu: 10m
43 | memory: 20Mi
44 | securityContext:
45 | runAsNonRoot: true
46 | runAsUser: 65534
47 | volumeMounts:
48 | - mountPath: /etc/blackbox_exporter/
49 | name: config
50 | readOnly: true
51 | - args:
52 | - --webhook-url=http://localhost:19115/-/reload
53 | - --volume-dir=/etc/blackbox_exporter/
54 | image: jimmidyson/configmap-reload:v0.5.0
55 | name: module-configmap-reloader
56 | resources:
57 | limits:
58 | cpu: 20m
59 | memory: 40Mi
60 | requests:
61 | cpu: 10m
62 | memory: 20Mi
63 | securityContext:
64 | runAsNonRoot: true
65 | runAsUser: 65534
66 | terminationMessagePath: /dev/termination-log
67 | terminationMessagePolicy: FallbackToLogsOnError
68 | volumeMounts:
69 | - mountPath: /etc/blackbox_exporter/
70 | name: config
71 | readOnly: true
72 | - args:
73 | - --logtostderr
74 | - --secure-listen-address=:9115
75 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
76 | - --upstream=http://127.0.0.1:19115/
77 | image: quay.io/brancz/kube-rbac-proxy:v0.11.0
78 | name: kube-rbac-proxy
79 | ports:
80 | - containerPort: 9115
81 | name: https
82 | resources:
83 | limits:
84 | cpu: 20m
85 | memory: 40Mi
86 | requests:
87 | cpu: 10m
88 | memory: 20Mi
89 | securityContext:
90 | runAsGroup: 65532
91 | runAsNonRoot: true
92 | runAsUser: 65532
93 | nodeSelector:
94 | kubernetes.io/os: linux
95 | serviceAccountName: blackbox-exporter
96 | volumes:
97 | - configMap:
98 | name: blackbox-exporter-configuration
99 | name: config
100 |
--------------------------------------------------------------------------------
/client/logspage/components/LogsTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import MaUTable from '@mui/material/Table';
4 | import { TableBody, AlertTitle, Grid, Paper } from '@mui/material';
5 | import TableCell from '@mui/material/TableCell';
6 | import TableHead from '@mui/material/TableHead';
7 | import TableRow from '@mui/material/TableRow';
8 | import { useTable, useFilters } from 'react-table';
9 | import {
10 | SelectColumnFilter,
11 | DefaultColumnFilter,
12 | } from '../../utils/table/helpers';
13 | import Alert from '../../utils/renderAlert';
14 |
15 | const HEADERS = [
16 | 'NAMESPACE',
17 | 'LAST SEEN',
18 | 'TYPE',
19 | 'REASON',
20 | 'OBJECT',
21 | 'MESSAGE',
22 | ];
23 |
24 | function Table({ columns, data }) {
25 | const defaultColumn = React.useMemo(
26 | () => ({
27 | // Let's set up our default Filter UI
28 | Filter: DefaultColumnFilter,
29 | }),
30 | []
31 | );
32 |
33 | // Use the state and functions returned from useTable to build your UI
34 | const { getTableProps, headerGroups, rows, prepareRow } = useTable(
35 | {
36 | columns,
37 | data,
38 | defaultColumn,
39 | },
40 | useFilters
41 | );
42 |
43 | // Render the UI for your table
44 | return (
45 |
46 |
47 | {headerGroups.map((headerGroup) => (
48 |
49 | {headerGroup.headers.map((column) => (
50 |
51 | {column.render('Header')}
52 | {column.canFilter ? column.render('Filter') : null}
53 |
54 | ))}
55 |
56 | ))}
57 |
58 |
59 | {rows.map((row, i) => {
60 | prepareRow(row);
61 | return (
62 |
63 | {row.cells.map((cell) => {
64 | return (
65 |
66 | {cell.render('Cell')}
67 |
68 | );
69 | })}
70 |
71 | );
72 | })}
73 |
74 |
75 | );
76 | }
77 |
78 | const formatColumns = () => {
79 | const columnsWithFilters = ['TYPE', 'REASON'];
80 | return HEADERS.map((el) => {
81 | const obj = {};
82 | if (columnsWithFilters.includes(el)) {
83 | obj.filter = 'includes';
84 | obj.Filter = SelectColumnFilter;
85 | } else {
86 | obj.canFilter = false;
87 | obj.disableFilters = true;
88 | }
89 | obj.Header = el.charAt(0).toUpperCase() + el.slice(1);
90 | obj.accessor = el.toLowerCase().replace(' ', '_');
91 | return obj;
92 | });
93 | };
94 |
95 | function LogsTable({ data, namespace }) {
96 | const columns = React.useMemo(
97 | () => formatColumns(),
98 |
99 | []
100 | );
101 |
102 | const message = () => {
103 | return (
104 | <>
105 | No logs found
106 | {namespace !== '' &&
107 | namespace !== 'All' &&
108 | ` in the ${namespace} namespace`}
109 | >
110 | );
111 | };
112 |
113 | return (
114 |
115 | {Alert(data.length, message(), 'info')}
116 |
117 |
118 |
119 |
120 | );
121 | }
122 |
123 | export default LogsTable;
124 |
--------------------------------------------------------------------------------
/client/homepage/components/Problematic/ProblematicPods.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import ComponentWrapper from '../../../utils/ComponentWrapper';
4 | import ProblematicItem from '../CriticalNodes/ProblematicItem';
5 | import { POD_STATUS } from '../../../utils/constants';
6 | import {
7 | List,
8 | ListItemButton,
9 | ListItemIcon,
10 | ListItemText,
11 | Collapse,
12 | Tooltip,
13 | } from '@mui/material';
14 |
15 | const ProblematicPods = ({ items, namespace }) => {
16 | const [filteredItems, setFilteredItems] = useState([]);
17 | const [openState, setOpenState] = useState({
18 | pending: false,
19 | running: false,
20 | succeeded: false,
21 | failed: false,
22 | unknown: false,
23 | });
24 |
25 | const handleClick = (key) => {
26 | setOpenState({ ...openState, [key]: !openState[key] });
27 | };
28 |
29 | // Filters by namespace
30 | useEffect(() => {
31 | if (namespace !== '' && namespace !== 'All') {
32 | setFilteredItems(
33 | items.filter((item) => item.metadata.namespace === namespace)
34 | );
35 | } else {
36 | setFilteredItems(items);
37 | }
38 | }, [namespace, items]);
39 |
40 | const phases = Object.keys(POD_STATUS);
41 |
42 | // Formats pod phases
43 | const parseItems = () => {
44 | return phases.map((p) => {
45 | let pods = filteredItems.filter((i) => i.status.phase === p);
46 | return {
47 | phase: p,
48 | pods: pods.map((pod) => {
49 | return { name: pod.metadata.name, conditions: pod.status.conditions };
50 | }),
51 | description: POD_STATUS[p],
52 | };
53 | });
54 | };
55 |
56 | const icons = [
57 | { icon: 'pending' },
58 | { icon: 'task_alt' },
59 | { icon: 'published_with_changes' },
60 | { icon: 'dangerous' },
61 | { icon: 'question_mark' },
62 | ];
63 | const renderList = () => {
64 | return parseItems().map((item, index) => {
65 | const phase = item.phase;
66 | const open = openState[phase.toLowerCase()];
67 | const length = item.pods.length;
68 | return (
69 |
70 |
71 | handleClick(phase.toLowerCase())}>
72 |
73 | {icons[index].icon}
74 |
75 |
76 | {open ? (
77 | expand_less
78 | ) : (
79 | expand_more
80 | )}
81 |
82 |
83 |
84 | {item.pods.map((p) => (
85 |
90 | ))}
91 |
92 |
93 | );
94 | });
95 | };
96 |
97 | return (
98 |
99 | {renderList()}
100 |
101 | );
102 | };
103 |
104 | const mapStateToProps = ({ pods, namespace }) => {
105 | return { ...pods, namespace: namespace.selectedNamespace };
106 | };
107 | export default connect(mapStateToProps)(ProblematicPods);
108 |
--------------------------------------------------------------------------------
/manifests/nodeExporter-daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: node-exporter
7 | app.kubernetes.io/part-of: kube-prometheus
8 | app.kubernetes.io/version: 1.3.1
9 | name: node-exporter
10 | namespace: monitoring
11 | spec:
12 | selector:
13 | matchLabels:
14 | app.kubernetes.io/component: exporter
15 | app.kubernetes.io/name: node-exporter
16 | app.kubernetes.io/part-of: kube-prometheus
17 | template:
18 | metadata:
19 | annotations:
20 | kubectl.kubernetes.io/default-container: node-exporter
21 | labels:
22 | app.kubernetes.io/component: exporter
23 | app.kubernetes.io/name: node-exporter
24 | app.kubernetes.io/part-of: kube-prometheus
25 | app.kubernetes.io/version: 1.3.1
26 | spec:
27 | containers:
28 | - args:
29 | - --web.listen-address=127.0.0.1:9100
30 | - --path.sysfs=/host/sys
31 | - --path.rootfs=/host/root
32 | - --no-collector.wifi
33 | - --no-collector.hwmon
34 | - --collector.filesystem.mount-points-exclude=^/(dev|proc|sys|run/k3s/containerd/.+|var/lib/docker/.+|var/lib/kubelet/pods/.+)($|/)
35 | - --collector.netclass.ignored-devices=^(veth.*|[a-f0-9]{15})$
36 | - --collector.netdev.device-exclude=^(veth.*|[a-f0-9]{15})$
37 | image: quay.io/prometheus/node-exporter:v1.3.1
38 | name: node-exporter
39 | resources:
40 | limits:
41 | cpu: 250m
42 | memory: 180Mi
43 | requests:
44 | cpu: 102m
45 | memory: 180Mi
46 | volumeMounts:
47 | - mountPath: /host/sys
48 | mountPropagation: HostToContainer
49 | name: sys
50 | readOnly: true
51 | - mountPath: /host/root
52 | mountPropagation: HostToContainer
53 | name: root
54 | readOnly: true
55 | - args:
56 | - --logtostderr
57 | - --secure-listen-address=[$(IP)]:9100
58 | - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
59 | - --upstream=http://127.0.0.1:9100/
60 | env:
61 | - name: IP
62 | valueFrom:
63 | fieldRef:
64 | fieldPath: status.podIP
65 | image: quay.io/brancz/kube-rbac-proxy:v0.11.0
66 | name: kube-rbac-proxy
67 | ports:
68 | - containerPort: 9100
69 | hostPort: 9100
70 | name: https
71 | resources:
72 | limits:
73 | cpu: 20m
74 | memory: 40Mi
75 | requests:
76 | cpu: 10m
77 | memory: 20Mi
78 | securityContext:
79 | runAsGroup: 65532
80 | runAsNonRoot: true
81 | runAsUser: 65532
82 | hostNetwork: true
83 | hostPID: true
84 | nodeSelector:
85 | kubernetes.io/os: linux
86 | securityContext:
87 | runAsNonRoot: true
88 | runAsUser: 65534
89 | serviceAccountName: node-exporter
90 | tolerations:
91 | - operator: Exists
92 | volumes:
93 | - hostPath:
94 | path: /sys
95 | name: sys
96 | - hostPath:
97 | path: /
98 | name: root
99 | updateStrategy:
100 | rollingUpdate:
101 | maxUnavailable: 10%
102 | type: RollingUpdate
103 |
--------------------------------------------------------------------------------
/__tests__/backendTests.js:
--------------------------------------------------------------------------------
1 | const request = require('supertest');
2 |
3 | const server = 'http://localhost:8080';
4 | const fs = require('fs');
5 | const path = require('path');
6 | const promURI = 'http://127.0.0.1:9090/api/v1/';
7 | // const serverFile = require('../server/server.js');
8 |
9 | describe('fetch metrics', () => {
10 | describe('/api/fetchMetrics', () => {
11 | describe('GET', () => {
12 | it('responds with 200 status and data as JSON content type', () => {
13 | request(server)
14 | .get('/api/fetchMetrics')
15 | .expect(200)
16 | .expect('Content-Type', /application\/json/);
17 | });
18 |
19 | it('client metrics are in body of response', () =>
20 | request(server)
21 | .get('/api/fetchMetrics')
22 | .then((response) => {
23 | expect(response.body);
24 | expect(response.body[0].values !== undefined).toBe(true);
25 | }));
26 | });
27 | });
28 | });
29 |
30 | describe('fetch alerts', () => {
31 | describe('/api/alerts', () => {
32 | describe('GET', () => {
33 | it('responds with 201 status and data as JSON content type', () => {
34 | request(server)
35 | .get('/api/alerts')
36 | .expect(201)
37 | .expect('Content-Type', /application\/json/);
38 | });
39 |
40 | it('alerts are in body of response', () =>
41 | request(server)
42 | .get('/api/alerts')
43 | .then((response) => {
44 | expect(response.body);
45 | expect(response.body.status === 'success').toBe(true);
46 | }));
47 | });
48 | });
49 | });
50 |
51 | let now = new Date();
52 | let nowCopy = new Date(now.getTime());
53 | nowCopy.setHours(nowCopy.getHours() - 24);
54 | let endDateTime = now.toISOString();
55 | let startDateTime = nowCopy.toISOString();
56 |
57 | let step = '30m';
58 | /*
59 | `/api/prometheus/homepage?startDateTime=${startDateTime}&endDateTime=${endDateTime}&step=${step}`
60 | */
61 | describe('fetch prom metrics for homepage', () => {
62 | describe(`/api/prometheus/homepage?startDateTime=${startDateTime}&endDateTime=${endDateTime}&step=${step}`, () => {
63 | describe('GET', () => {
64 | it('responds with 200 status and data as JSON content type', () => {
65 | request(server)
66 | .get(
67 | `/api/prometheus/homepage?startDateTime=${startDateTime}&endDateTime=${endDateTime}&step=${step}`
68 | )
69 | .expect(200)
70 | .expect('Content-Type', /application\/json/);
71 | });
72 |
73 | it('prometheus metrics are in body of response', () =>
74 | request(server)
75 | .get(
76 | `/api/prometheus/homepage?startDateTime=${startDateTime}&endDateTime=${endDateTime}&step=${step}`
77 | )
78 | .then((response) => {
79 | expect(response.body);
80 | expect(response.body.bytesReceivedPerNode !== undefined).toBe(true);
81 | expect(response.body.bytesTransmittedPerNode !== undefined).toBe(
82 | true
83 | );
84 | }));
85 | });
86 | });
87 | });
88 |
89 | describe('fetch namespace list', () => {
90 | describe('/api/namespaceList', () => {
91 | describe('GET', () => {
92 | it('responds with 201 status and data as JSON content type', () => {
93 | request(server)
94 | .get('/api/namespaceList')
95 | .expect(201)
96 | .expect('Content-Type', /application\/json/);
97 | });
98 |
99 | it('namespaces are in body of response', () =>
100 | request(server)
101 | .get('/api/namespaceList')
102 | .then((response) => {
103 | expect(response.body);
104 | expect(response.body.items[0].metadata.name !== undefined).toBe(
105 | true
106 | );
107 | }));
108 | });
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/client/homepage/components/Problematic/ProblematicNodes.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Paper, Typography, ListSubheader } from '@mui/material';
4 | import Box from '@mui/material/Box';
5 | import List from '@mui/material/List';
6 | import ListItemButton from '@mui/material/ListItemButton';
7 | import ListItemIcon from '@mui/material/ListItemIcon';
8 | import ListItemText from '@mui/material/ListItemText';
9 | import Collapse from '@mui/material/Collapse';
10 | import parseStatus from '../../../utils/parseStatus';
11 | import ComponentWrapper from '../../../utils/ComponentWrapper';
12 | import ProblematicItem from '../CriticalNodes/ProblematicItem';
13 |
14 | // Component to show problematic nodes
15 | const ProblematicNodes = ({ items, lastUpdated }) => {
16 | const [openState, setOpenState] = useState({
17 | pidPressure: false,
18 | memoryPressure: false,
19 | diskPressure: false,
20 | down: false,
21 | });
22 |
23 | const handleClick = (key) => {
24 | setOpenState({ [key]: !openState[key] });
25 | };
26 | const nodeConditions = parseStatus(items);
27 |
28 | // Generates ProblematicItem list for each condition
29 | const renderByCondition = (nodes, conditionType, condition) => {
30 | const list = Object.keys(nodes).filter(
31 | (key) => nodes[key][conditionType] === condition
32 | );
33 | return list.map((n) => {
34 | return ;
35 | });
36 | };
37 | const down = () => {
38 | return renderByCondition(nodeConditions, 'Ready', 'False');
39 | };
40 | const memoryPressure = () => {
41 | return renderByCondition(nodeConditions, 'MemoryPressure', 'True');
42 | };
43 | const diskPressure = () => {
44 | return renderByCondition(nodeConditions, 'DiskPressure', 'True');
45 | };
46 | const pidPressure = () => {
47 | return renderByCondition(nodeConditions, 'PIDPressure', 'True');
48 | };
49 |
50 | const renderList = (key, conditionType, renderComponent, icon) => {
51 | const open = openState[key];
52 | const length = renderComponent().length;
53 | return (
54 | <>
55 | handleClick(key)}>
56 |
57 | {icon}
58 |
59 |
60 | {open ? (
61 | expand_less
62 | ) : (
63 | expand_more
64 | )}
65 |
66 |
67 | {renderComponent()}
68 |
69 | >
70 | );
71 | };
72 |
73 | const render = () => {
74 | return (
75 | items.length && (
76 |
77 |
78 | {renderList('down', 'Down', down, 'dangerous')}
79 | {renderList('pidPressure', 'PID Pressure', pidPressure, 'speed')}
80 | {renderList(
81 | 'memoryPressure',
82 | 'Memory Pressure',
83 | memoryPressure,
84 | 'memory'
85 | )}
86 | {renderList('diskPressure', 'Disk Pressure', diskPressure, 'save')}
87 |
88 |
89 | )
90 | );
91 | };
92 |
93 | return render();
94 | };
95 |
96 | const mapStateToProps = ({ nodes }) => {
97 | return nodes;
98 | };
99 |
100 | export default connect(mapStateToProps)(ProblematicNodes);
101 |
--------------------------------------------------------------------------------
/client/components/CriticalNodes/ProblematicNodes.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Paper, Typography, ListSubheader } from '@mui/material';
4 | import Box from '@mui/material/Box';
5 | import List from '@mui/material/List';
6 | import ListItemButton from '@mui/material/ListItemButton';
7 | import ListItemIcon from '@mui/material/ListItemIcon';
8 | import ListItemText from '@mui/material/ListItemText';
9 | import Collapse from '@mui/material/Collapse';
10 | import parseStatus from '../../../utils/parseStatus';
11 | import ComponentWrapper from '../../../utils/ComponentWrapper';
12 | import ProblematicItem from './ProblematicItem';
13 |
14 | const ProblematicNodes = ({ items, lastUpdated }) => {
15 | const [openState, setOpenState] = useState({
16 | pidPressure: false,
17 | memoryPressure: false,
18 | diskPressure: false,
19 | down: false,
20 | });
21 |
22 | const handleClick = (key) => {
23 | setOpenState({ ...openState, [key]: !openState[key] });
24 | };
25 | const nodeConditions = parseStatus(items);
26 |
27 | const renderByCondition = (nodes, conditionType, condition) => {
28 | const list = Object.keys(nodes).filter(
29 | (key) => nodes[key][conditionType] === condition
30 | );
31 | return list.map((n) => {
32 | return ;
33 | });
34 | };
35 | const down = () => {
36 | return renderByCondition(nodeConditions, 'Ready', 'False');
37 | };
38 | const memoryPressure = () => {
39 | return renderByCondition(nodeConditions, 'MemoryPressure', 'True');
40 | };
41 | const diskPressure = () => {
42 | return renderByCondition(nodeConditions, 'DiskPressure', 'True');
43 | };
44 | const pidPressure = () => {
45 | return renderByCondition(nodeConditions, 'PIDPressure', 'True');
46 | };
47 |
48 | const renderList = (key, conditionType, renderComponent) => {
49 | const open = openState[key];
50 | const length = renderComponent().length;
51 | return (
52 | <>
53 | handleClick(key)}>
54 |
55 | dangerous
56 |
57 |
58 | {open ? (
59 | expand_less
60 | ) : (
61 | expand_more
62 | )}
63 |
64 |
65 | {renderComponent()}
66 |
67 | >
68 | );
69 | };
70 |
71 | const render = () => {
72 | return (
73 | items.length && (
74 |
75 |
87 |
88 | )
89 | );
90 | };
91 |
92 | return render();
93 | };
94 |
95 | const mapStateToProps = ({ nodes }) => {
96 | return nodes;
97 | };
98 |
99 | export default connect(mapStateToProps)(ProblematicNodes);
100 |
101 | // horizontal bar charts
102 |
--------------------------------------------------------------------------------
/manifests/kubePrometheus-prometheusRule.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: monitoring.coreos.com/v1
2 | kind: PrometheusRule
3 | metadata:
4 | labels:
5 | app.kubernetes.io/component: exporter
6 | app.kubernetes.io/name: kube-prometheus
7 | app.kubernetes.io/part-of: kube-prometheus
8 | prometheus: k8s
9 | role: alert-rules
10 | name: kube-prometheus-rules
11 | namespace: monitoring
12 | spec:
13 | groups:
14 | - name: general.rules
15 | rules:
16 | - alert: TargetDown
17 | annotations:
18 | description: '{{ printf "%.4g" $value }}% of the {{ $labels.job }}/{{ $labels.service
19 | }} targets in {{ $labels.namespace }} namespace are down.'
20 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/general/targetdown
21 | summary: One or more targets are unreachable.
22 | expr: 100 * (count(up == 0) BY (job, namespace, service) / count(up) BY (job,
23 | namespace, service)) > 10
24 | for: 10m
25 | labels:
26 | severity: warning
27 | - alert: Watchdog
28 | annotations:
29 | description: |
30 | This is an alert meant to ensure that the entire alerting pipeline is functional.
31 | This alert is always firing, therefore it should always be firing in Alertmanager
32 | and always fire against a receiver. There are integrations with various notification
33 | mechanisms that send a notification when this alert is not firing. For example the
34 | "DeadMansSnitch" integration in PagerDuty.
35 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/general/watchdog
36 | summary: An alert that should always be firing to certify that Alertmanager
37 | is working properly.
38 | expr: vector(1)
39 | labels:
40 | severity: none
41 | - name: node-network
42 | rules:
43 | - alert: NodeNetworkInterfaceFlapping
44 | annotations:
45 | description: Network interface "{{ $labels.device }}" changing its up status
46 | often on node-exporter {{ $labels.namespace }}/{{ $labels.pod }}
47 | runbook_url: https://runbooks.prometheus-operator.dev/runbooks/general/nodenetworkinterfaceflapping
48 | summary: Network interface is often changing its status
49 | expr: |
50 | changes(node_network_up{job="node-exporter",device!~"veth.+"}[2m]) > 2
51 | for: 2m
52 | labels:
53 | severity: warning
54 | - name: kube-prometheus-node-recording.rules
55 | rules:
56 | - expr: sum(rate(node_cpu_seconds_total{mode!="idle",mode!="iowait",mode!="steal"}[3m]))
57 | BY (instance)
58 | record: instance:node_cpu:rate:sum
59 | - expr: sum(rate(node_network_receive_bytes_total[3m])) BY (instance)
60 | record: instance:node_network_receive_bytes:rate:sum
61 | - expr: sum(rate(node_network_transmit_bytes_total[3m])) BY (instance)
62 | record: instance:node_network_transmit_bytes:rate:sum
63 | - expr: sum(rate(node_cpu_seconds_total{mode!="idle",mode!="iowait",mode!="steal"}[5m]))
64 | WITHOUT (cpu, mode) / ON(instance) GROUP_LEFT() count(sum(node_cpu_seconds_total)
65 | BY (instance, cpu)) BY (instance)
66 | record: instance:node_cpu:ratio
67 | - expr: sum(rate(node_cpu_seconds_total{mode!="idle",mode!="iowait",mode!="steal"}[5m]))
68 | record: cluster:node_cpu:sum_rate5m
69 | - expr: cluster:node_cpu_seconds_total:rate5m / count(sum(node_cpu_seconds_total)
70 | BY (instance, cpu))
71 | record: cluster:node_cpu:ratio
72 | - name: kube-prometheus-general.rules
73 | rules:
74 | - expr: count without(instance, pod, node) (up == 1)
75 | record: count:up1
76 | - expr: count without(instance, pod, node) (up == 0)
77 | record: count:up0
78 |
--------------------------------------------------------------------------------
/client/homepage/components/Charts/LineChartTemplate.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Legend,
11 | } from 'chart.js';
12 | import { Line } from 'react-chartjs-2';
13 | import mdColors from './MaterialColors';
14 | import { Button } from '@mui/material';
15 | import { CopyToClipboard } from 'react-copy-to-clipboard';
16 | ChartJS.register(
17 | CategoryScale,
18 | LinearScale,
19 | PointElement,
20 | LineElement,
21 | Title,
22 | Tooltip,
23 | Legend
24 | );
25 |
26 | const LineChart = ({ chartData, title, label, query }) => {
27 | // React hooks for "Copy Query to Clipboard" button and collapsing/expanding legend
28 | const [copiedText, setCopiedText] = useState();
29 | const [buttonClicked, setButtonClicked] = useState(false);
30 |
31 | const options = {
32 | indexAxis: 'x',
33 | maintainAspectRatio: false,
34 | responsive: true,
35 | pointRadius: 0,
36 | plugins: {
37 | legend: {
38 | display: buttonClicked,
39 | labels: {
40 | usePointStyle: true,
41 | pointStyle: 'rect',
42 | boxWidth: 6,
43 | boxHeight: 6,
44 | color: 'white',
45 | },
46 | },
47 | title: {
48 | display: true,
49 | text: title,
50 | font: {
51 | size: 25,
52 | },
53 | color: 'white',
54 | padding: {
55 | bottom: 15,
56 | },
57 | },
58 | datalabels: {
59 | // hide datalabels for all datasets
60 | display: false,
61 | },
62 | },
63 | scales: {
64 | xAxes: {
65 | display: true,
66 | ticks: {
67 | color: 'white',
68 | font: {
69 | size: 12,
70 | },
71 | },
72 | },
73 | yAxes: {
74 | display: true,
75 | ticks: {
76 | color: 'white',
77 | },
78 | },
79 | },
80 | };
81 |
82 | // if chart data is empty render "No data available"
83 | if (!chartData) return No data available in {title}
;
84 |
85 | // Format chart data for line chart with varying colors
86 | const objArr = [];
87 | if (!chartData.seriesLabels) console.log('title', title);
88 | const lineChartData = chartData.seriesLabels.forEach((el, index) => {
89 | const colors = mdColors;
90 | const rand = Math.floor(Math.random() * 3);
91 | objArr.push({
92 | data: chartData.seriesValues[index],
93 | label: chartData.seriesLabels[index],
94 | fill: true,
95 | borderColor: colors[(index * 3) % mdColors.length],
96 | backgroundColor: colors[(index * 3) % mdColors.length],
97 | pointHitRadius: 20,
98 | });
99 | });
100 |
101 | const data = {
102 | labels: chartData.timestamps,
103 | datasets: objArr,
104 | };
105 | let id = 1;
106 |
107 | // Collapse or expand legend
108 | const handleLegendClick = () => {
109 | setButtonClicked((prevCheck) => !prevCheck);
110 | };
111 |
112 | return (
113 |
114 |
115 | setCopiedText({ query })}>
116 |
123 |
124 |
132 |
133 | );
134 | };
135 |
136 | export default LineChart;
137 |
--------------------------------------------------------------------------------
/coverage/lcov-report/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Code coverage report for All files
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
All files
23 |
24 |
25 |
26 | Unknown%
27 | Statements
28 | 0/0
29 |
30 |
31 |
32 |
33 | Unknown%
34 | Branches
35 | 0/0
36 |
37 |
38 |
39 |
40 | Unknown%
41 | Functions
42 | 0/0
43 |
44 |
45 |
46 |
47 | Unknown%
48 | Lines
49 | 0/0
50 |
51 |
52 |
53 |
54 |
55 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
56 |
57 |
58 |
59 | Filter:
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | | File |
70 | |
71 | Statements |
72 | |
73 | Branches |
74 | |
75 | Functions |
76 | |
77 | Lines |
78 | |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
91 |
92 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------