Skip to content

Commit

Permalink
Merge pull request #271 from kubero-dev/feature/add-uptime-to-stats-view
Browse files Browse the repository at this point in the history
Feature / Add uptime to stats view
  • Loading branch information
mms-gianni authored Jan 22, 2024
2 parents 1ac9db1 + cb4830e commit 971bde8
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 10 deletions.
30 changes: 24 additions & 6 deletions client/src/components/apps/appstats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,18 @@
<v-col cols="6" class="text-right"><v-progress-linear :value="metric.memory.percentage" color="#8560A9" class="float-left" ></v-progress-linear></v-col>
</v-row>
</div>
<div class="px-5" v-if="metricsDisplay == 'table'">
<div class="px-3" v-if="metricsDisplay == 'table'">
<v-row>
<v-col cols="8" class="pb-0 text-left text-caption font-weight-light">Pod</v-col>
<v-col cols="2" class="pb-0 text-left text-caption font-weight-light">CPU</v-col>
<v-col cols="2" class="pb-0 text-right text-caption font-weight-light">Memory</v-col>
<v-col cols="1" class="pb-0 text-left text-caption font-weight-light">CPU</v-col>
<v-col cols="1" class="pb-0 text-right text-caption font-weight-light">Memory</v-col>
<v-col cols="2" class="pb-0 text-right text-caption font-weight-light">Uptime</v-col>
</v-row>
<v-row v-for="metric in metrics" :key="metric.name" id="metrics">
<v-col cols="8" class="py-0 text-left">{{metric.name}}</v-col>
<v-col cols="2" class="py-0 text-left">{{metric.cpu.usage}}{{metric.cpu.unit}}</v-col>
<v-col cols="2" class="py-0 text-right">{{metric.memory.usage}}{{metric.memory.unit}}</v-col>
<v-col cols="1" class="py-0 text-left">{{metric.cpu.usage}}{{metric.cpu.unit}}</v-col>
<v-col cols="1" class="py-0 text-right">{{metric.memory.usage}}{{metric.memory.unit}}</v-col>
<v-col cols="2" class="py-0 text-right">{{metric.uptime.formatted}}</v-col>
</v-row>
</div>
<div class="mb-5 mt-10">
Expand Down Expand Up @@ -392,6 +394,10 @@ type appData = {
type Metric = {
name: string,
uptime: {
formatted: string,
ms: number,
},
cpu: {
percentage: number,
usage: number,
Expand Down Expand Up @@ -432,19 +438,30 @@ export default defineComponent({
metrics: [] as Metric[],
metricsDisplay: "bars",
metricsInterval: 0 as any, // can't find the right type for this
uptimes: {} as any,
}
},
components: {
Addons,
},
mounted() {
this.loadMetrics();
this.loadUptimes();
this.metricsInterval = setInterval(this.loadMetrics, 40000);
},
unmounted() {
clearInterval(this.metricsInterval);
},
methods: {
loadUptimes() {
axios.get(`/api/uptimes/${this.pipeline}/${this.phase}`)
.then(response => {
this.uptimes = response.data;
this.loadMetrics();
})
.catch(error => {
console.log(error);
});
},
loadMetrics() {
axios.get(`/api/metrics/${this.pipeline}/${this.phase}/${this.app}`)
Expand All @@ -459,6 +476,7 @@ export default defineComponent({
){
this.metricsDisplay = "table";
}
response.data[i].uptime = this.uptimes[response.data[i].name];
}
this.metrics = response.data;
})
Expand Down
9 changes: 7 additions & 2 deletions server/src/kubero.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ export class Kubero {
for (const phase of pipeline.phases) {

if (phase.enabled == true) {
debug.log("Loading Namespace: "+pipeline.name+"-"+phase.name);
debug.log("🔁 Loading Namespace: "+pipeline.name+"-"+phase.name);
this.listAppsInNamespace(pipeline.name, phase.name)
.then(appsList => {
if (appsList) {
for (const app of appsList.items) {
debug.log("Loading App: "+app.spec.name);
debug.log("🔁 Loading App: "+app.spec.name);
this.appStateList.push(app.spec);
}
}
Expand Down Expand Up @@ -1185,6 +1185,11 @@ export class Kubero {
return this.kubectl.getEvents(namespace);
}

public getPodUptime(pipelineName: string, phaseName: string) {
const namespace = pipelineName+'-'+phaseName;
return this.kubectl.getPodUptimes(namespace);
}

public getPodMetrics(pipelineName: string, phaseName: string, appName: string) {
const namespace = pipelineName+'-'+phaseName;
return this.kubectl.getPodMetrics(namespace, appName);
Expand Down
75 changes: 74 additions & 1 deletion server/src/modules/kubectl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
BatchV1Api,
NetworkingV1Api,
} from '@kubernetes/client-node'
import { IPipeline, IKubectlPipeline, IKubectlPipelineList, IKubectlAppList, IKuberoConfig} from '../types';
import { IPipeline, IKubectlPipeline, IKubectlPipelineList, IKubectlAppList, IKuberoConfig, Uptime} from '../types';
import { App, KubectlApp } from './application';
import { KubectlPipeline } from './pipeline';
import { IAddon, IAddonMinimal } from './addons';
Expand Down Expand Up @@ -555,6 +555,79 @@ export class Kubectl {
return metrics.items;
}

private getPodUptimeMS(pod: V1Pod): number {
const startTime = pod.status?.startTime;
if (startTime) {
const start = new Date(startTime);
const now = new Date();
const uptime = now.getTime() - start.getTime();
return uptime;
}
return -1;
}

public async getPodUptimes(namespace: string): Promise<Object> {
const pods = await this.coreV1Api.listNamespacedPod(namespace);
const ret = Object();
for (let i = 0; i < pods.body.items.length; i++) {
const pod = pods.body.items[i];
const uptime = this.getPodUptimeMS(pod);
if (pod.metadata && pod.metadata.name) {
ret[pod.metadata.name] = {
ms: uptime,
formatted: this.formatUptime(uptime)
}
}
}
return ret;
}

private formatUptime(uptime: number): string {
// 0-120s show seconds
// 2-10m show minutes and seconds
// 10-120m show minutes
// 2-48h show hours and minutes
// 2-30d show days and hours
// >30d show date

if (uptime < 0) {
return '';
}
if (uptime < 120000) {
const seconds = Math.floor(uptime / 1000);
return seconds + "s";
}
if (uptime < 600000) {
const minutes = Math.floor(uptime / (1000 * 60));
const seconds = Math.floor((uptime - (minutes * 1000 * 60)) / 1000);
if (seconds > 0) {
return minutes + "m" + seconds + "s";
}
return minutes + "m";
}
if (uptime < 7200000) {
const minutes = Math.floor(uptime / (1000 * 60));
return minutes + "m";
}
if (uptime < 172800000) {
const hours = Math.floor(uptime / (1000 * 60 * 60));
const minutes = Math.floor((uptime - (hours * 1000 * 60 * 60)) / (1000 * 60));
if (minutes > 0) {
return hours + "h" + minutes + "m";
}
return hours + "h";
}
//if (uptime < 2592000000) {
const days = Math.floor(uptime / (1000 * 60 * 60 * 24));
const hours = Math.floor((uptime - (days * 1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
if (hours > 0) {
return days + "d" + hours + "h";
}
return days + "d";
//}

}

public async getStorageglasses(): Promise<Object[]> {
let ret = [];
try {
Expand Down
17 changes: 16 additions & 1 deletion server/src/routes/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ auth.init();
export const authMiddleware = auth.getAuthMiddleware();

import debug from 'debug';
//import rateLimit from 'express-rate-limit';
debug('app:routes')

Router.get('/logs/:pipeline/:phase/:app', authMiddleware, async function (req: Request, res: Response) {
Expand Down Expand Up @@ -51,7 +52,13 @@ Router.get('/events', authMiddleware, async function (req: Request, res: Respons
const events = await req.app.locals.kubero.getEvents(namespace);
res.send(events);
});

/*
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 10, // limit each IP to 10 requests per windowMs
});
*/
//Router.get('/audit', authMiddleware, limiter, async function (req: Request, res: Response) {
Router.get('/audit', authMiddleware, async function (req: Request, res: Response) {
// #swagger.tags = ['UI']
// #swagger.summary = 'Get the Kubero audit log'
Expand All @@ -78,6 +85,14 @@ Router.get('/audit', authMiddleware, async function (req: Request, res: Response

});

Router.get('/uptimes/:pipeline/:phase/', authMiddleware, async function (req: Request, res: Response) {

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
const uptimes = await req.app.locals.kubero.getPodUptime(
req.params.pipeline,
req.params.phase
);
res.send(uptimes);
});

Router.get('/metrics/:pipeline/:phase/:app', authMiddleware, async function (req: Request, res: Response) {
// #swagger.tags = ['UI']
// #swagger.summary = 'Get metrics for a specific app'
Expand Down
8 changes: 8 additions & 0 deletions server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,12 @@ export interface IMessage {
pipelineName: string,
phaseName: string,
data?: any
}

export interface Uptime {
days: number,
hours: number,
minutes: number,
seconds: number,
milliseconds: number
}

0 comments on commit 971bde8

Please sign in to comment.