Skip to content

Commit 49f099d

Browse files
committed
Honor Linode volume price budgets
1 parent 3543a22 commit 49f099d

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

packages/cloud/linode/src/index.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,24 @@ describe('Linode cloud adapter', () => {
151151
expect(second.length).toBeLessThanOrEqual(32);
152152
});
153153

154+
it('does not create block storage when maxHourlyPrice is below the volume rate', async () => {
155+
const fetchMock = vi.fn();
156+
vi.stubGlobal('fetch', fetchMock);
157+
158+
await expect(adapter.provision({
159+
secret: (key: string) => key === 'LINODE_API_TOKEN' ? 'token' : undefined,
160+
log: vi.fn(),
161+
dryRun: false,
162+
}, {
163+
kind: 'block-storage',
164+
storage: 20,
165+
region: 'us-east',
166+
maxHourlyPrice: 0.001,
167+
}, {})).rejects.toThrow('exceeds maxHourlyPrice');
168+
169+
expect(fetchMock).not.toHaveBeenCalled();
170+
});
171+
154172
it('requires a login mechanism before non-dry-run image provisioning', async () => {
155173
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
156174
ok: true,

packages/cloud/linode/src/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export default defineCloud<Config>({
103103
if (spec.kind === 'block-storage') {
104104
const monthly = (spec.storage ?? 10) * VOLUME_MONTHLY_PER_GB;
105105
return {
106-
hourly: monthly / 730,
106+
hourly: volumeHourlyRate(spec.storage ?? 10),
107107
monthly,
108108
currency: 'USD',
109109
provider: 'linode',
@@ -136,6 +136,10 @@ export default defineCloud<Config>({
136136
if (ctx.dryRun) return { ...stubInstance('dry-run', 'provisioning', spec.kind), region };
137137

138138
if (spec.kind === 'block-storage') {
139+
const hourly = volumeHourlyRate(spec.storage ?? 10);
140+
if (spec.maxHourlyPrice !== undefined && hourly > spec.maxHourlyPrice) {
141+
throw new Error(`linode: block-storage hourly price $${hourly} exceeds maxHourlyPrice $${spec.maxHourlyPrice}`);
142+
}
139143
ctx.log(`linode provision - volume region=${region} size=${spec.storage ?? 10}GB`);
140144
const volume = await linodeRequest<LinodeVolume>(ctx, 'POST', '/volumes', {
141145
label,
@@ -304,13 +308,17 @@ function volumeToInstance(volume: LinodeVolume): Instance {
304308
kind: 'block-storage',
305309
status: statusMap[volume.status] ?? 'provisioning',
306310
createdAt: volume.created,
307-
hourlyRate: (volume.size * VOLUME_MONTHLY_PER_GB) / 730,
311+
hourlyRate: volumeHourlyRate(volume.size),
308312
currency: 'USD',
309313
region: volume.region,
310314
tags: volume.tags,
311315
};
312316
}
313317

318+
function volumeHourlyRate(sizeGb: number): number {
319+
return (sizeGb * VOLUME_MONTHLY_PER_GB) / 730;
320+
}
321+
314322
function hasHardwareConstraints(spec: InstanceSpec): boolean {
315323
return !!(spec.cpu || spec.memory || spec.storage || spec.gpu?.count);
316324
}

0 commit comments

Comments
 (0)