From b1fc66022e00824588eb03438d52c1aaa4803869 Mon Sep 17 00:00:00 2001 From: nublarsec Date: Fri, 13 May 2022 13:47:15 -0700 Subject: [PATCH 1/8] Bring over formatDuration function from Lens --- src/utils/formatDuration.ts | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/utils/formatDuration.ts diff --git a/src/utils/formatDuration.ts b/src/utils/formatDuration.ts new file mode 100644 index 0000000..d600e33 --- /dev/null +++ b/src/utils/formatDuration.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + + import moment from "moment"; + + /** + * This function formats durations in a more human readable form. + * @param timeValue the duration in milliseconds to format + */ + export default function formatDuration(timeValue: number, compact = true) { + const duration = moment.duration(timeValue, "milliseconds"); + const seconds = Math.floor(duration.asSeconds()); + const separator = compact ? "": " "; + + if (seconds < 0) { + return "0s"; + } else if (seconds < 60*2 ) { + return `${seconds}s`; + } + + const minutes = Math.floor(duration.asMinutes()); + + if (minutes < 10) { + const seconds = duration.seconds(); + + return getMeaningfulValues([minutes, seconds], ["m", "s"], separator); + } else if (minutes < 60 * 3) { + if (!compact) { + return getMeaningfulValues([minutes, duration.seconds()], ["m", "s"]); + } + + return `${minutes}m`; + } + + const hours = Math.floor(duration.asHours()); + + if(hours < 8) { + const minutes = duration.minutes(); + + return getMeaningfulValues([hours, minutes], ["h", "m"], separator); + } else if (hours < 48) { + if (compact) { + return `${hours}h`; + } else { + return getMeaningfulValues([hours, duration.minutes()], ["h", "m"]); + } + } + + const days = Math.floor(duration.asDays()); + + if (days < 8) { + const hours = duration.hours(); + + if (compact) { + return getMeaningfulValues([days, hours], ["d", "h"], separator); + } else { + return getMeaningfulValues([days, hours, duration.minutes()], ["d", "h", "m"]); + } + } + const years = Math.floor(duration.asYears()); + + if (years < 2) { + if (compact) { + return `${days}d`; + } else { + return getMeaningfulValues([days, duration.hours(), duration.minutes()], ["d", "h", "m"]); + } + } else if (years < 8) { + const days = duration.days(); + + if (compact) { + return getMeaningfulValues([years, days], ["y", "d"], separator); + } + } + + if (compact) { + return `${years}y`; + } + + return getMeaningfulValues([years, duration.days(), duration.hours(), duration.minutes()], ["y", "d", "h", "m"]); + } + + function getMeaningfulValues(values: number[], suffixes: string[], separator = " ") { + return values + .map((a, i): [number, string] => [a, suffixes[i]]) + .filter(([dur]) => dur > 0) + .map(([dur, suf]) => dur + suf) + .join(separator); + } \ No newline at end of file From 215a54840cc7ea0965571097feead893d76f950b Mon Sep 17 00:00:00 2001 From: nublarsec Date: Fri, 13 May 2022 13:47:25 -0700 Subject: [PATCH 2/8] Add moment packages --- package-lock.json | 38 ++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 40 insertions(+) diff --git a/package-lock.json b/package-lock.json index f0b16c2..d2b97aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "css-loader": "^5.0.0", "mobx": "^6.3.0", "mobx-react": "^7.2.0", + "moment": "^2.29.3", + "moment-timezone": "^0.5.34", "sass": "^1.29.0", "sass-loader": "^10.0.0", "style-loader": "^2.0.0", @@ -2561,6 +2563,27 @@ } } }, + "node_modules/moment": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", + "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.34", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", + "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", + "dev": true, + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5807,6 +5830,21 @@ "dev": true, "requires": {} }, + "moment": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", + "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", + "dev": true + }, + "moment-timezone": { + "version": "0.5.34", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", + "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", + "dev": true, + "requires": { + "moment": ">= 2.9.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index fe2e9ce..e2c251d 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,8 @@ "css-loader": "^5.0.0", "mobx": "^6.3.0", "mobx-react": "^7.2.0", + "moment": "^2.29.3", + "moment-timezone": "^0.5.34", "sass": "^1.29.0", "sass-loader": "^10.0.0", "style-loader": "^2.0.0", From eaca5c8ba0e6732bfdb0b77b1396bafd6f5b8e97 Mon Sep 17 00:00:00 2001 From: nublarsec Date: Fri, 13 May 2022 13:47:43 -0700 Subject: [PATCH 3/8] Add Age column to Vuln report with sorting --- src/vulnerabilityreports/page.scss | 9 ++++++--- src/vulnerabilityreports/page.tsx | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/vulnerabilityreports/page.scss b/src/vulnerabilityreports/page.scss index 04f2e04..771b4a0 100644 --- a/src/vulnerabilityreports/page.scss +++ b/src/vulnerabilityreports/page.scss @@ -6,18 +6,21 @@ align-items: center; text-align: left; &.name { - flex-grow: 0.25; + flex-grow: 0.225; } &.namespace { - flex-grow: 0.1; + flex-grow: 0.075; } &.repository { - flex-grow: 0.25; + flex-grow: 0.225; } &.summary { flex-grow: 0.3; } &.scanner { + flex-grow: 0.075; + } + &.age { flex-grow: 0.1; } } diff --git a/src/vulnerabilityreports/page.tsx b/src/vulnerabilityreports/page.tsx index 92f9ef0..5ec9bc8 100644 --- a/src/vulnerabilityreports/page.tsx +++ b/src/vulnerabilityreports/page.tsx @@ -4,6 +4,7 @@ import React from "react"; import {ClusterVulnerabilityReport, VulnerabilityReport} from "./types"; import {clusterStore, store} from "./store"; import {Scanner} from "../starboard/types"; +import formatDuration from "../utils/formatDuration"; const { Component: { @@ -16,6 +17,7 @@ enum sortBy { name = "name", namespace = "namespace", summary = "summary", + age = "age", } export class ClusterVulnerabilityReportPage extends React.Component<{ extension: Renderer.LensExtension }> { @@ -80,6 +82,7 @@ export class VulnerabilityReportPage extends React.Component<{ extension: Render report.report.summary.lowCount, report.report.summary.unknownCount, ], + [sortBy.age]: (report: VulnerabilityReport) => report.metadata.creationTimestamp, }} searchFilters={[ (report: VulnerabilityReport) => report.getSearchFields() @@ -91,6 +94,7 @@ export class VulnerabilityReportPage extends React.Component<{ extension: Render {title: "Image", className: "repository"}, {title: "Summary", className: "summary", sortBy: sortBy.summary}, {title: "Scanner", className: "scanner"}, + {title: "Age", className: "age", sortBy: sortBy.age}, ]} renderTableContents={(report: VulnerabilityReport) => [ renderName(report.getName()), @@ -104,6 +108,7 @@ export class VulnerabilityReportPage extends React.Component<{ extension: Render renderSeverity("UNKNOWN", report.report.summary.unknownCount), ], renderScanner(report.report.scanner), + renderAge(report.metadata.creationTimestamp), ]} /> ) @@ -138,3 +143,13 @@ function renderSeverity(severity: string, count: number) { ) } } + +function renderAge(timestamp: string) { + const creationTimestamp = new Date(timestamp).getTime(); + return ( + } /> + tooltip={timestamp} /> + ) +} From 7503866bec0e42638e3e91811c4d947a27d7c607 Mon Sep 17 00:00:00 2001 From: nublarsec Date: Thu, 19 May 2022 20:48:31 -0700 Subject: [PATCH 4/8] Drop local timezone timestamp, no access --- src/vulnerabilityreports/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vulnerabilityreports/page.tsx b/src/vulnerabilityreports/page.tsx index 5ec9bc8..1544bd8 100644 --- a/src/vulnerabilityreports/page.tsx +++ b/src/vulnerabilityreports/page.tsx @@ -149,7 +149,6 @@ function renderAge(timestamp: string) { return ( } /> tooltip={timestamp} /> ) } From 56b895577f203868e09b2aef9fe08d506c154c34 Mon Sep 17 00:00:00 2001 From: nublarsec Date: Thu, 19 May 2022 21:12:55 -0700 Subject: [PATCH 5/8] Add Age to ConfigAuditReports page --- src/configauditreports/page.scss | 7 +++++-- src/configauditreports/page.tsx | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/configauditreports/page.scss b/src/configauditreports/page.scss index aa3fbf3..20ddda6 100644 --- a/src/configauditreports/page.scss +++ b/src/configauditreports/page.scss @@ -9,13 +9,16 @@ flex-grow: 0.45; } &.namespace { - flex-grow: 0.1; + flex-grow: 0.075; } &.summary { flex-grow: 0.3; } &.scanner { - flex-grow: 0.15; + flex-grow: 0.125; + } + &.age { + flex-grow: 0.05; } } } diff --git a/src/configauditreports/page.tsx b/src/configauditreports/page.tsx index 1c82f3c..bda92bd 100644 --- a/src/configauditreports/page.tsx +++ b/src/configauditreports/page.tsx @@ -3,6 +3,7 @@ import {Renderer} from "@k8slens/extensions"; import React from "react"; import {clusterStore, store} from "./store"; import {ClusterConfigAuditReport, ConfigAuditReport} from "./types"; +import formatDuration from "../utils/formatDuration"; const { Component: { @@ -15,7 +16,8 @@ enum sortBy { name = "name", namespace = "namespace", summary = "summary", - scanner = "scanner" + scanner = "scanner", + age = "age", } export class ClusterConfigAuditReportPage extends React.Component<{ extension: Renderer.LensExtension }> { @@ -76,6 +78,7 @@ export class ConfigAuditReportPage extends React.Component<{ extension: Renderer report.report.summary.lowCount ], [sortBy.scanner]: (report: ConfigAuditReport) => report.report.scanner.name + " " + report.report.scanner.version, + [sortBy.age]: (report: ConfigAuditReport) => report.metadata.creationTimestamp, }} searchFilters={[ (report: ConfigAuditReport) => report.getSearchFields() @@ -86,6 +89,7 @@ export class ConfigAuditReportPage extends React.Component<{ extension: Renderer {title: "Namespace", className: "namespace", sortBy: sortBy.namespace}, {title: "Summary", className: "summary", sortBy: sortBy.summary}, {title: "Scanner", className: "scanner", sortBy: sortBy.scanner}, + {title: "Age", className: "age", sortBy: sortBy.age}, ]} renderTableContents={(report: ConfigAuditReport) => [ renderName(report.getName()), @@ -97,6 +101,7 @@ export class ConfigAuditReportPage extends React.Component<{ extension: Renderer renderSeverity("LOW", report.report.summary.lowCount), ], report.report.scanner.name + " " + report.report.scanner.version, + renderAge(report.metadata.creationTimestamp), ]} /> ) @@ -119,4 +124,13 @@ function renderSeverity(severity: string, count: number) { ) } +} + +function renderAge(timestamp: string) { + const creationTimestamp = new Date(timestamp).getTime(); + return ( + + ) } \ No newline at end of file From 839a29283780befa5df92e9d9a02099d5d32ecea Mon Sep 17 00:00:00 2001 From: nublarsec Date: Thu, 19 May 2022 21:13:07 -0700 Subject: [PATCH 6/8] Tweak flex widths for Vuln Reports --- src/vulnerabilityreports/page.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vulnerabilityreports/page.scss b/src/vulnerabilityreports/page.scss index 771b4a0..97753ff 100644 --- a/src/vulnerabilityreports/page.scss +++ b/src/vulnerabilityreports/page.scss @@ -6,13 +6,13 @@ align-items: center; text-align: left; &.name { - flex-grow: 0.225; + flex-grow: 0.25; } &.namespace { flex-grow: 0.075; } &.repository { - flex-grow: 0.225; + flex-grow: 0.25; } &.summary { flex-grow: 0.3; @@ -21,7 +21,7 @@ flex-grow: 0.075; } &.age { - flex-grow: 0.1; + flex-grow: 0.05; } } } From 9cd430cb72d3b508643065ddb1f0c5ac563db68a Mon Sep 17 00:00:00 2001 From: nublarsec Date: Thu, 19 May 2022 21:20:21 -0700 Subject: [PATCH 7/8] Add Age to CISKubeBenchReports page --- src/ciskubebenchreports/page.scss | 5 ++++- src/ciskubebenchreports/page.tsx | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/ciskubebenchreports/page.scss b/src/ciskubebenchreports/page.scss index f322cd0..e23272a 100644 --- a/src/ciskubebenchreports/page.scss +++ b/src/ciskubebenchreports/page.scss @@ -9,11 +9,14 @@ flex-grow: 0.45; } &.summary { - flex-grow: 0.4; + flex-grow: 0.35; } &.scanner { flex-grow: 0.15; } + &.age { + flex-grow: 0.05; + } } } diff --git a/src/ciskubebenchreports/page.tsx b/src/ciskubebenchreports/page.tsx index 708dd05..e83c0ad 100644 --- a/src/ciskubebenchreports/page.tsx +++ b/src/ciskubebenchreports/page.tsx @@ -3,6 +3,7 @@ import {Renderer} from "@k8slens/extensions"; import React from "react"; import {store} from "./store"; import {CISKubeBenchReport} from "./types"; +import formatDuration from "../utils/formatDuration"; const { Component: { @@ -14,6 +15,7 @@ const { enum sortBy { name = "name", summary = "summary", + age = "age", } export class CISKubeBenchReportsPage extends React.Component<{ extension: Renderer.LensExtension }> { @@ -30,7 +32,8 @@ export class CISKubeBenchReportsPage extends React.Component<{ extension: Render report.report.summary.infoCount, report.report.summary.passCount, report.report.summary.warnCount, - ] + ], + [sortBy.age]: (report: CISKubeBenchReport) => report.metadata.creationTimestamp, }} searchFilters={[ (report: CISKubeBenchReport) => report.getSearchFields() @@ -40,6 +43,7 @@ export class CISKubeBenchReportsPage extends React.Component<{ extension: Render {title: "Name", className: "name", sortBy: sortBy.name}, {title: "Summary", className: "summary", sortBy: sortBy.summary}, {title: "Scanner", className: "scanner"}, + {title: "Age", className: "age", sortBy: sortBy.age}, ]} renderTableContents={(report: CISKubeBenchReport) => [ report.getName(), @@ -50,6 +54,7 @@ export class CISKubeBenchReportsPage extends React.Component<{ extension: Render renderResult("pass", report.report.summary.passCount), ], report.report.scanner.name + " " + report.report.scanner.version, + renderAge(report.metadata.creationTimestamp), ]} /> ) @@ -66,4 +71,13 @@ function renderResult(result: string, count: number) { ) } +} + +function renderAge(timestamp: string) { + const creationTimestamp = new Date(timestamp).getTime(); + return ( + + ) } \ No newline at end of file From bbaa51e4a71c1170a5b4f36d259b4cb82d354a65 Mon Sep 17 00:00:00 2001 From: nublarsec Date: Thu, 19 May 2022 21:22:54 -0700 Subject: [PATCH 8/8] Add Age to Cluster* versions of pages --- src/configauditreports/page.tsx | 3 +++ src/vulnerabilityreports/page.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/configauditreports/page.tsx b/src/configauditreports/page.tsx index bda92bd..cce80f0 100644 --- a/src/configauditreports/page.tsx +++ b/src/configauditreports/page.tsx @@ -36,6 +36,7 @@ export class ClusterConfigAuditReportPage extends React.Component<{ extension: R report.report.summary.lowCount ], [sortBy.scanner]: (report: ClusterConfigAuditReport) => report.report.scanner.name + " " + report.report.scanner.version, + [sortBy.age]: (report: ConfigAuditReport) => report.metadata.creationTimestamp, }} searchFilters={[ (report: ClusterConfigAuditReport) => report.getSearchFields() @@ -45,6 +46,7 @@ export class ClusterConfigAuditReportPage extends React.Component<{ extension: R {title: "Name", sortBy: sortBy.name}, {title: "Summary", className: "summary", sortBy: sortBy.summary}, {title: "Scanner", sortBy: sortBy.scanner}, + {title: "Age", className: "age", sortBy: sortBy.age}, ]} renderTableContents={(report: ClusterConfigAuditReport) => [ renderName(report.getName()), @@ -55,6 +57,7 @@ export class ClusterConfigAuditReportPage extends React.Component<{ extension: R renderSeverity("LOW", report.report.summary.lowCount), ], report.report.scanner.name + " " + report.report.scanner.version, + renderAge(report.metadata.creationTimestamp), ]} /> ) diff --git a/src/vulnerabilityreports/page.tsx b/src/vulnerabilityreports/page.tsx index 1544bd8..f1bc9a3 100644 --- a/src/vulnerabilityreports/page.tsx +++ b/src/vulnerabilityreports/page.tsx @@ -36,6 +36,7 @@ export class ClusterVulnerabilityReportPage extends React.Component<{ extension: report.report.summary.lowCount, report.report.summary.unknownCount, ], + [sortBy.age]: (report: ClusterVulnerabilityReport) => report.metadata.creationTimestamp, }} searchFilters={[ (report: ClusterVulnerabilityReport) => report.getSearchFields() @@ -46,6 +47,7 @@ export class ClusterVulnerabilityReportPage extends React.Component<{ extension: {title: "Image", className: "repository"}, {title: "Summary", className: "summary", sortBy: sortBy.summary}, {title: "Scanner", className: "scanner"}, + {title: "Age", className: "age", sortBy: sortBy.age}, ]} renderTableContents={(report: ClusterVulnerabilityReport) => [ renderName(report.getName()), @@ -58,6 +60,7 @@ export class ClusterVulnerabilityReportPage extends React.Component<{ extension: renderSeverity("UNKNOWN", report.report.summary.unknownCount), ], renderScanner(report.report.scanner), + renderAge(report.metadata.creationTimestamp), ]} /> )