fix(gpu-service): address PR review on sliced scaling and theme logo
- Scale sliced CPU in millicores and RAM in MiB for precise, k8s-valid fractional allocations; show rounded decimals in the disabled inputs - Disable the by-ratio option when the type has no sliced capacity - Use the dark logo map under the dark theme in template cards
This commit is contained in:
@@ -282,30 +282,36 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
|
|||||||
|
|
||||||
const fallbackCpu = resources.cpu;
|
const fallbackCpu = resources.cpu;
|
||||||
|
|
||||||
// Sliced mode: scale a single card's unit resources by the chosen
|
|
||||||
// percentage (floored) — both CPU and RAM. Whole/CPU mode: multiply the
|
|
||||||
// unit by the count.
|
|
||||||
const percentage = _.toNumber(
|
const percentage = _.toNumber(
|
||||||
resources.acceleratorSlicedMemoryPercentage
|
resources.acceleratorSlicedMemoryPercentage
|
||||||
);
|
);
|
||||||
const sliced = isGPUType && percentage > 0;
|
const sliced = isGPUType && percentage > 0;
|
||||||
const wholeFactor = isGPUType ? accelerator : cpuCount;
|
const wholeFactor = isGPUType ? accelerator : cpuCount;
|
||||||
|
|
||||||
const scale = (num: number, unit: string) =>
|
// Sliced mode: scale a single card's unit resources by the chosen
|
||||||
sliced
|
// percentage. Scale CPU in millicores and RAM in MiB so fractional
|
||||||
? `${Math.max(1, _.floor((num * percentage) / 100))}${unit}`
|
// slices stay precise and k8s-valid (integers) — e.g. 10% of a 4-core /
|
||||||
: `${wholeFactor * num}${unit}`;
|
// 16Gi card → 400m / 1638Mi, not a rounded-up 1 core / 1Gi.
|
||||||
|
if (sliced && unitResourcesParsed) {
|
||||||
|
const cpuCores = unitResourcesParsed.cpu?.cores ?? 0;
|
||||||
|
const ramValue = unitResourcesParsed.ram?.value ?? 0;
|
||||||
|
return {
|
||||||
|
cpu: `${Math.max(1, _.floor((cpuCores * 1000 * percentage) / 100))}m`,
|
||||||
|
ram: `${Math.max(1, _.floor((ramValue * 1024 * percentage) / 100))}Mi`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whole / CPU mode: multiply the unit by the count.
|
||||||
return {
|
return {
|
||||||
cpu: cpuNum
|
cpu: cpuNum
|
||||||
? scale(cpuNum, unitResourcesParsed?.cpu?.unit || '')
|
? `${wholeFactor * cpuNum}${unitResourcesParsed?.cpu?.unit || ''}`
|
||||||
: // Don't stringify an unset value — `${undefined}` becomes the
|
: // Don't stringify an unset value — `${undefined}` becomes the
|
||||||
// literal "undefined", which fails k8s quantity validation.
|
// literal "undefined", which fails k8s quantity validation.
|
||||||
fallbackCpu
|
fallbackCpu
|
||||||
? `${fallbackCpu}`
|
? `${fallbackCpu}`
|
||||||
: undefined,
|
: undefined,
|
||||||
ram: ramNum
|
ram: ramNum
|
||||||
? scale(ramNum, unitResourcesParsed?.ram?.unit || '')
|
? `${wholeFactor * ramNum}${unitResourcesParsed?.ram?.unit || ''}`
|
||||||
: resources.ram
|
: resources.ram
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -329,13 +335,16 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
|
|||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
spec: {
|
spec: {
|
||||||
resources: {
|
resources: {
|
||||||
|
// Display the precise (rounded) fractional values — the inputs are
|
||||||
|
// disabled, so decimals are fine and match the submitted
|
||||||
|
// millicore / MiB allocation better than a floored integer.
|
||||||
cpu:
|
cpu:
|
||||||
cpuCores != null && percentage > 0
|
cpuCores != null && percentage > 0
|
||||||
? Math.max(1, _.floor((cpuCores * percentage) / 100))
|
? _.round((cpuCores * percentage) / 100, 2)
|
||||||
: null,
|
: null,
|
||||||
ram:
|
ram:
|
||||||
ramValue != null && percentage > 0
|
ramValue != null && percentage > 0
|
||||||
? Math.max(1, _.floor((ramValue * percentage) / 100))
|
? _.round((ramValue * percentage) / 100, 2)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ const InstanceTypeFormItem: React.FC<InstanceTypeFormItemProps> = ({
|
|||||||
onSliceMemoryPercentageChange?.(value);
|
onSliceMemoryPercentageChange?.(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Max selectable ratio in sliced mode: status.onceMaxRequest.acceleratorSliced
|
||||||
|
// (a percentage). Ticks above it stay visible but disabled.
|
||||||
|
const slicedMaxPercentage =
|
||||||
|
_.toNumber(
|
||||||
|
selectedInstanceType?.status?.onceMaxRequest?.acceleratorSliced
|
||||||
|
) || 0;
|
||||||
|
|
||||||
const modeSegmented = showModeSwitch ? (
|
const modeSegmented = showModeSwitch ? (
|
||||||
<Segmented
|
<Segmented
|
||||||
size="small"
|
size="small"
|
||||||
@@ -149,7 +156,9 @@ const InstanceTypeFormItem: React.FC<InstanceTypeFormItemProps> = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({ id: 'gpuservice.instance.mode.sliced' }),
|
label: intl.formatMessage({ id: 'gpuservice.instance.mode.sliced' }),
|
||||||
value: 'sliced'
|
value: 'sliced',
|
||||||
|
// No sliced capacity → keep the option visible but unselectable.
|
||||||
|
disabled: slicedMaxPercentage <= 0
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -157,13 +166,6 @@ const InstanceTypeFormItem: React.FC<InstanceTypeFormItemProps> = ({
|
|||||||
|
|
||||||
const isSliced = showModeSwitch && sliceMode === 'sliced';
|
const isSliced = showModeSwitch && sliceMode === 'sliced';
|
||||||
|
|
||||||
// Max selectable ratio in sliced mode: status.onceMaxRequest.acceleratorSliced
|
|
||||||
// (a percentage). Ticks above it stay visible but disabled.
|
|
||||||
const slicedMaxPercentage =
|
|
||||||
_.toNumber(
|
|
||||||
selectedInstanceType?.status?.onceMaxRequest?.acceleratorSliced
|
|
||||||
) || 0;
|
|
||||||
|
|
||||||
// When the max ratio is below 10%, switch the ticks to a finer 1..10 scale
|
// When the max ratio is below 10%, switch the ticks to a finer 1..10 scale
|
||||||
// so small slices are still selectable; otherwise use the 10..100 scale.
|
// so small slices are still selectable; otherwise use the 10..100 scale.
|
||||||
const sliceTicks =
|
const sliceTicks =
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const matchImageLogo = (
|
|||||||
isDark: boolean
|
isDark: boolean
|
||||||
): { logo: string; type: string } | null => {
|
): { logo: string; type: string } | null => {
|
||||||
if (!image) return null;
|
if (!image) return null;
|
||||||
const logoMap = imageLogoLightMap;
|
const logoMap = isDark ? imageLogoDarkMap : imageLogoLightMap;
|
||||||
const lower = image.toLowerCase();
|
const lower = image.toLowerCase();
|
||||||
let matched: keyof typeof logoMap | null = null;
|
let matched: keyof typeof logoMap | null = null;
|
||||||
let earliest = Infinity;
|
let earliest = Infinity;
|
||||||
|
|||||||
Reference in New Issue
Block a user