Skip to content

Commit 49084d9

Browse files
committed
fix(pci-kubernetes): calculate multiple node price
ref: #TAPC-3341 Signed-off-by: Pierre-Philippe <[email protected]>
1 parent 3bfb7e5 commit 49084d9

File tree

7 files changed

+144
-61
lines changed

7 files changed

+144
-61
lines changed

packages/manager/apps/pci-kubernetes/src/helpers/index.spec.ts

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import {
1212
isBase64,
1313
parseCommaSeparated,
1414
generateUniqueName,
15+
getPrice,
1516
} from '@/helpers/index';
16-
import { NodePool } from '@/api/data/kubernetes';
17+
import { NodePoolPrice } from '@/api/data/kubernetes';
18+
import { AutoscalingState } from '@/components/Autoscaling.component';
19+
import { KubeFlavor } from '@/components/flavor-selector/FlavorSelector.component';
1720

1821
describe('helper', () => {
1922
it('compares two objects based on a key', () => {
@@ -159,7 +162,7 @@ describe('parseCommaSeparated', () => {
159162
});
160163

161164
it('returns an empty array for undefined input', () => {
162-
expect(parseCommaSeparated(undefined)).toEqual([]);
165+
expect(parseCommaSeparated(undefined!)).toEqual([]);
163166
});
164167
});
165168

@@ -200,9 +203,77 @@ describe('generateUniqueName', () => {
200203
(baseName, existingNodePools, expectedResult) => {
201204
const result = generateUniqueName(
202205
baseName,
203-
existingNodePools as NodePool[],
206+
existingNodePools as NodePoolPrice[],
204207
);
205208
expect(result).toBe(expectedResult);
206209
},
207210
);
208211
});
212+
213+
describe('getPrice', () => {
214+
const testCases = [
215+
{
216+
description: 'flavor et scaling with monthly',
217+
flavor: {
218+
pricingsHourly: { price: 0.1 },
219+
pricingsMonthly: { price: 50 },
220+
},
221+
scaling: {
222+
quantity: { desired: 2 },
223+
},
224+
expected: {
225+
hour: 0.2,
226+
month: 100,
227+
},
228+
},
229+
{
230+
description: 'flavor et scaling without monthly',
231+
flavor: {
232+
pricingsHourly: { price: 0.01 },
233+
pricingsMonthly: null,
234+
},
235+
scaling: {
236+
quantity: { desired: 3 },
237+
},
238+
expected: {
239+
hour: 0.03,
240+
month: null,
241+
},
242+
},
243+
{
244+
description: 'scaling null',
245+
flavor: {
246+
pricingsHourly: { price: 0.1 },
247+
pricingsMonthly: { price: 50 },
248+
},
249+
scaling: null,
250+
expected: {
251+
hour: 0,
252+
month: 0,
253+
},
254+
},
255+
{
256+
description: 'flavor null',
257+
flavor: null,
258+
scaling: {
259+
quantity: { desired: 2 },
260+
},
261+
expected: {
262+
hour: 0,
263+
month: 0,
264+
},
265+
},
266+
];
267+
268+
(testCases as {
269+
description: string;
270+
flavor: KubeFlavor | null;
271+
scaling: AutoscalingState | null;
272+
expected: { hour: number; month: number };
273+
}[]).forEach(({ description, flavor, scaling, expected }) => {
274+
it(`should return correct pricing when ${description}`, () => {
275+
const result = getPrice(flavor!, scaling);
276+
expect(result).toEqual(expected);
277+
});
278+
});
279+
});

packages/manager/apps/pci-kubernetes/src/helpers/index.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { ZodObject, ZodRawShape } from 'zod';
44
import { ClassValue, clsx } from 'clsx';
55
import { twMerge } from 'tailwind-merge';
66
import { DeploymentMode, SigningAlgorithms, TOidcProvider } from '@/types';
7-
import { NodePool } from '@/api/data/kubernetes';
7+
import { NodePool, NodePoolPrice } from '@/api/data/kubernetes';
8+
9+
import { KubeFlavor } from '@/components/flavor-selector/FlavorSelector.component';
10+
import { AutoscalingState } from '@/components/Autoscaling.component';
811

912
export const REFETCH_INTERVAL_DURATION = 15_000;
1013
export const QUOTA_ERROR_URL =
@@ -191,7 +194,7 @@ export const getValidOptionalKeys = (oidcProvider: TOidcProvider) =>
191194
*/
192195
export function generateUniqueName(
193196
baseName: string,
194-
existingNodePools: NodePool[],
197+
existingNodePools: NodePoolPrice[],
195198
) {
196199
let newName = baseName;
197200
let copyNumber = 1;
@@ -216,3 +219,18 @@ export const isMultiDeploymentZones = (type: DeploymentMode) =>
216219
type === DeploymentMode.MULTI_ZONES;
217220
export const isLocalDeploymentZone = (type: DeploymentMode) =>
218221
type === DeploymentMode.LOCAL_ZONE;
222+
223+
export const getPrice = (
224+
flavor: KubeFlavor | null,
225+
scaling: AutoscalingState | null,
226+
) => {
227+
if (flavor && scaling) {
228+
return {
229+
hour: (flavor.pricingsHourly?.price ?? 0) * scaling.quantity.desired,
230+
month: flavor.pricingsMonthly
231+
? flavor.pricingsMonthly.price * scaling.quantity.desired
232+
: null,
233+
};
234+
}
235+
return { hour: 0, month: 0 };
236+
};

packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/New.page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import NodePoolAntiAffinity from '@/pages/new/steps/node-pool/NodePoolAntiAffini
3939
import { FlavorSelector } from '@/components/flavor-selector/FlavorSelector.component';
4040
import NodePoolSize from '@/pages/new/steps/node-pool/NodePoolSize.component';
4141
import DeploymentZone from '@/pages/new/steps/node-pool/DeploymentZone.component';
42-
import { isMultiDeploymentZones } from '@/helpers';
42+
import { getPrice, isMultiDeploymentZones } from '@/helpers';
4343
import { useRegionInformations } from '@/api/hooks/useRegionInformations';
4444

4545
export default function NewPage(): JSX.Element {
@@ -434,8 +434,8 @@ export default function NewPage(): JSX.Element {
434434
order={4}
435435
>
436436
<BillingStep
437-
price={billingState.price}
438-
monthlyPrice={billingState.monthlyPrice}
437+
price={getPrice(store.flavor, store.autoScaling).hour}
438+
monthlyPrice={getPrice(store.flavor, store.autoScaling).month}
439439
monthlyBilling={billingState.monthlyBilling}
440440
warn={billingState.warn}
441441
/>

packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/store.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createRef, MutableRefObject } from 'react';
44
import { StepsEnum } from '@/pages/detail/nodepools/new/steps.enum';
55
import { AutoscalingState } from '@/components/Autoscaling.component';
66
import { NAME_INPUT_CONSTRAINTS } from '@/constants';
7+
import { KubeFlavor } from '@/components/flavor-selector/FlavorSelector.component';
78

89
type TStep = {
910
isOpen: boolean;
@@ -18,7 +19,7 @@ export type TFormStore = {
1819
isTouched: boolean;
1920
hasError: boolean;
2021
};
21-
flavor: TFlavor;
22+
flavor?: KubeFlavor;
2223
selectedAvailibilityZone: string;
2324
autoScaling: AutoscalingState;
2425
antiAffinity: boolean;
@@ -120,8 +121,8 @@ export const useNewPoolStore = create<TFormStore>()((set, get) => ({
120121
}
121122
},
122123

123-
flavor: (val: TFlavor) => set({ flavor: val }),
124-
autoScaling: (val: AutoscalingState) => set({ autoScaling: val }),
124+
flavor: (val?: KubeFlavor) => set({ flavor: val }),
125+
autoScaling: (val: AutoscalingState | null) => set({ autoScaling: val }),
125126
antiAffinity: (val: boolean) => set({ antiAffinity: val }),
126127
isMonthlyBilling: (val: boolean) => set({ isMonthlyBilling: val }),
127128
},

packages/manager/apps/pci-kubernetes/src/pages/new/steps/ClusterNameStep.component.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,19 @@ export function ClusterNameStep({
6060
>
6161
{t('kubernetes_add_name')}
6262
</OsdsText>
63-
<OsdsInput
64-
placeholder={t('kubernetes_add_name_placeholder')}
65-
value={name}
66-
color={ODS_THEME_COLOR_INTENT.primary}
67-
type={ODS_INPUT_TYPE.text}
68-
onOdsValueChange={(e) => {
69-
setName(e.detail.value ?? '');
70-
}}
71-
onOdsInputBlur={() => setIsTouched(true)}
72-
error={hasError}
73-
/>
63+
<div className="w-fit">
64+
<OsdsInput
65+
placeholder={t('kubernetes_add_name_placeholder')}
66+
value={name}
67+
color={ODS_THEME_COLOR_INTENT.primary}
68+
type={ODS_INPUT_TYPE.text}
69+
onOdsValueChange={(e) => {
70+
setName(e.detail.value ?? '');
71+
}}
72+
onOdsInputBlur={() => setIsTouched(true)}
73+
error={hasError}
74+
/>
75+
</div>
7476
</OsdsFormField>
7577
<OsdsButton
7678
className="mt-8 w-fit"

packages/manager/apps/pci-kubernetes/src/pages/new/steps/NodePoolStep.component.tsx

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,18 @@ import NodePoolType from './node-pool/NodePoolType.component';
3030
import NodePoolSize from './node-pool/NodePoolSize.component';
3131
import NodePoolAntiAffinity from './node-pool/NodePoolAntiAffinity.component';
3232

33-
import { NodePool, NodePoolPrice } from '@/api/data/kubernetes';
33+
import { NodePoolPrice } from '@/api/data/kubernetes';
3434
import {
3535
generateUniqueName,
3636
isMonoDeploymentZone,
3737
isMultiDeploymentZones,
38+
getPrice,
3839
} from '@/helpers';
3940
import { useRegionInformations } from '@/api/hooks/useRegionInformations';
4041
import DeploymentZone from './node-pool/DeploymentZone.component';
4142
import { KubeFlavor } from '@/components/flavor-selector/FlavorSelector.component';
4243
import use3AZPlanAvailable from '@/hooks/use3azPlanAvaible';
4344

44-
const getPrice = (flavor: KubeFlavor, scaling: AutoscalingState | null) => {
45-
if (flavor && scaling) {
46-
return {
47-
hour: flavor.pricingsHourly.price * scaling.quantity.desired,
48-
month: flavor.pricingsMonthly
49-
? flavor.pricingsMonthly.price * scaling.quantity.desired
50-
: null,
51-
};
52-
}
53-
return { hour: 0, month: 0 };
54-
};
55-
5645
export type NodePoolState = {
5746
antiAffinity: boolean;
5847
name: string;
@@ -172,9 +161,9 @@ const NodePoolStep = ({
172161
}, [nodePoolEnabled]);
173162

174163
const setNewNodePool = useCallback(() => {
175-
if (nodes || nodePoolState.scaling || flavor) {
164+
if (nodes && nodePoolState.scaling && flavor) {
176165
const newNodePool: NodePoolPrice = {
177-
name: generateUniqueName(nodePoolState.name, nodes as NodePool[]),
166+
name: generateUniqueName(nodePoolState.name, nodes),
178167
antiAffinity: nodePoolState.antiAffinity,
179168
autoscale: nodePoolState.scaling.isAutoscale,
180169
...(isMultiDeploymentZones(regionInformations?.type) && {
@@ -233,19 +222,7 @@ const NodePoolStep = ({
233222
: 'invisible w-0 h-0 overflow-hidden'
234223
}
235224
>
236-
<div className="mb-8">
237-
<NodePoolName
238-
onTouched={(isTouched: boolean) =>
239-
setNodePoolState((state) => ({ ...state, isTouched }))
240-
}
241-
hasError={hasError}
242-
onNameChange={(name: string) =>
243-
setNodePoolState((state) => ({ ...state, name }))
244-
}
245-
name={nodePoolState.name}
246-
/>
247-
</div>
248-
<div className="mb-8">
225+
<div className="mb-8 mt-4">
249226
<NodePoolType
250227
projectId={projectId as string}
251228
region={stepper.form.region.name}
@@ -303,6 +280,18 @@ const NodePoolStep = ({
303280
}
304281
/>
305282
</div>
283+
<div className="mb-8">
284+
<NodePoolName
285+
onTouched={(isTouched: boolean) =>
286+
setNodePoolState((state) => ({ ...state, isTouched }))
287+
}
288+
hasError={hasError}
289+
onNameChange={(name: string) =>
290+
setNodePoolState((state) => ({ ...state, name }))
291+
}
292+
name={nodePoolState.name}
293+
/>
294+
</div>
306295
</div>
307296

308297
{!stepper.node.step.isLocked && nodePoolEnabled && (

packages/manager/apps/pci-kubernetes/src/pages/new/steps/node-pool/NodePoolName.component.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,19 @@ const NodePoolName = ({
5858
>
5959
{t('kubernetes_add_name')}
6060
</OsdsText>
61-
<OsdsInput
62-
placeholder={t('kube_add_node_pool_name_label')}
63-
value={name}
64-
color={ODS_THEME_COLOR_INTENT.primary}
65-
type={ODS_INPUT_TYPE.text}
66-
onOdsValueChange={(e) => {
67-
onNameChange(e.detail.value);
68-
}}
69-
onOdsInputBlur={() => onTouched(true)}
70-
error={hasError}
71-
/>
61+
<div className="w-fit">
62+
<OsdsInput
63+
placeholder={t('kube_add_node_pool_name_label')}
64+
value={name}
65+
color={ODS_THEME_COLOR_INTENT.primary}
66+
type={ODS_INPUT_TYPE.text}
67+
onOdsValueChange={(e) => {
68+
onNameChange(e.detail.value ?? '');
69+
}}
70+
onOdsInputBlur={() => onTouched(true)}
71+
error={hasError}
72+
/>
73+
</div>
7274
</OsdsFormField>
7375
</>
7476
);

0 commit comments

Comments
 (0)