From 0c9db5b63420bab25e970c75dfd9169b9c7db139 Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Tue, 19 Dec 2023 10:49:05 -0500 Subject: [PATCH] 19675: Add CWL size formatter and display MiB max ram/disk request alongside human readable units Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- .../workbench2/src/common/formatters.test.ts | 52 ++++++++++++++++++- services/workbench2/src/common/formatters.ts | 15 ++++-- .../instance-types-panel.tsx | 8 +-- 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/services/workbench2/src/common/formatters.test.ts b/services/workbench2/src/common/formatters.test.ts index 7f9ffa0c35..cde1a4f9d2 100644 --- a/services/workbench2/src/common/formatters.test.ts +++ b/services/workbench2/src/common/formatters.test.ts @@ -2,7 +2,57 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { formatUploadSpeed, formatCost } from "./formatters"; +import { formatFileSize, formatUploadSpeed, formatCost, formatCWLResourceSize } from "./formatters"; + +describe('formatFileSize', () => { + it('should pick the largest unit', () => { + const base = 1024; + const testCases = [ + {input: 0, output: '0 B'}, + {input: 1, output: '1 B'}, + {input: 1023, output: '1023 B'}, + {input: base, output: '1.0 KiB'}, + {input: 1.1 * base, output: '1.1 KiB'}, + {input: 1.5 * base, output: '1.5 KiB'}, + {input: base ** 2, output: '1.0 MiB'}, + {input: 1.5 * (base ** 2), output: '1.5 MiB'}, + {input: base ** 3, output: '1.0 GiB'}, + {input: base ** 4, output: '1.0 TiB'}, + ]; + + for (const { input, output } of testCases) { + expect(formatFileSize(input)).toBe(output); + } + }); + + it('should handle accidental empty string or undefined input', () => { + expect(formatFileSize('')).toBe('-'); + expect(formatFileSize(undefined)).toBe('-'); + }); + + it('should handle accidental non-empty string input', () => { + expect(formatFileSize('foo')).toBe('0 B'); + }); +}); + +describe('formatCWLResourceSize', () => { + it('should format bytes as MiB', () => { + const base = 1024 ** 2; + + const testCases = [ + {input: 0, output: '0 MiB'}, + {input: 1, output: '0 MiB'}, + {input: base - 1, output: '1 MiB'}, + {input: 2 * base, output: '2 MiB'}, + {input: 1024 * base, output: '1024 MiB'}, + {input: 10000 * base, output: '10000 MiB'}, + ]; + + for (const { input, output } of testCases) { + expect(formatCWLResourceSize(input)).toBe(output); + } + }); +}); describe('formatUploadSpeed', () => { it('should show speed less than 1MB/s', () => { diff --git a/services/workbench2/src/common/formatters.ts b/services/workbench2/src/common/formatters.ts index 3366af0d56..e44a21e026 100644 --- a/services/workbench2/src/common/formatters.ts +++ b/services/workbench2/src/common/formatters.ts @@ -41,6 +41,10 @@ export const formatFileSize = (size?: number | string) => { return '0 B'; }; +export const formatCWLResourceSize = (size: number) => { + return `${(size / CWL_SIZE.base).toFixed(0)} ${CWL_SIZE.unit}`; +}; + export const formatTime = (time: number, seconds?: boolean) => { const minutes = Math.floor((time / (1000 * 60)) % 60).toFixed(0); const hours = Math.floor(time / (1000 * 60 * 60)).toFixed(0); @@ -78,15 +82,15 @@ export function formatUploadSpeed( const FILE_SIZES = [ { - base: 1099511627776, + base: 1024 ** 4, unit: 'TiB', }, { - base: 1073741824, + base: 1024 ** 3, unit: 'GiB', }, { - base: 1048576, + base: 1024 ** 2, unit: 'MiB', }, { @@ -99,6 +103,11 @@ const FILE_SIZES = [ }, ]; +const CWL_SIZE = { + base: 1024 ** 2, + unit: 'MiB', +}; + export const formatPropertyValue = ( pv: PropertyValue, vocabulary?: Vocabulary diff --git a/services/workbench2/src/views/instance-types-panel/instance-types-panel.tsx b/services/workbench2/src/views/instance-types-panel/instance-types-panel.tsx index a18f5d2368..006a119797 100644 --- a/services/workbench2/src/views/instance-types-panel/instance-types-panel.tsx +++ b/services/workbench2/src/views/instance-types-panel/instance-types-panel.tsx @@ -10,7 +10,7 @@ import { RootState } from 'store/store'; import { connect } from 'react-redux'; import { ClusterConfigJSON } from 'common/config'; import { NotFoundView } from 'views/not-found-panel/not-found-panel'; -import { formatCost, formatFileSize } from 'common/formatters'; +import { formatCWLResourceSize, formatCost, formatFileSize } from 'common/formatters'; type CssRules = 'root' | 'instanceType'; @@ -43,6 +43,8 @@ export const InstanceTypesPanel = withStyles(styles)(connect(mapStateToProps)( {Object.keys(instances).length > 0 ? Object.keys(instances).map((instanceKey) => { const instanceType = instances[instanceKey]; + const diskRequest = instanceType.IncludedScratch; + const ramRequest = instanceType.RAM - config.Containers.ReserveExtraRAM; return @@ -63,10 +65,10 @@ export const InstanceTypesPanel = withStyles(styles)(connect(mapStateToProps)( Preemptible: {instanceType.Preemptible.toString()} - Max disk request: {formatFileSize(instanceType.IncludedScratch)} + Max disk request: {formatCWLResourceSize(diskRequest)} ({formatFileSize(diskRequest)}) - Max ram request: {formatFileSize(instanceType.RAM - config.Containers.ReserveExtraRAM)} + Max RAM request: {formatCWLResourceSize(ramRequest)} ({formatFileSize(ramRequest)}) {instanceType.CUDA && instanceType.CUDA.DeviceCount > 0 ? <> -- 2.30.2