openvibers/openvibe.host Commits
Host: Vultr plans are named as Vultr names them, bare metal is dedicated, disks add up, and a free tier is not a price (#91)
Changes
@@@ -48,6 +48,18 @@ function lightsail(x, id) {ipv4: ipv6Only ? 0 : 1, locations: typeof x.location === 'string' && x.location ? [x.location] : null,};}++// Vultr's plan types as its pricing page names them; a plan is named from its type, CPU vendor, vCPUs and memory++// ("High Performance AMD · 1 vCPU, 1 GB"), never by its API id alone.++const VULTR_TYPES = Object.freeze({ vc2: 'Cloud Compute', vhf: 'High Frequency', vhp: 'High Performance', voc: 'Optimized Cloud', vcg: 'Cloud GPU', vbm: 'Bare Metal', vdc: 'Dedicated Cloud' });++function vultrName(x, id) {++ const type = VULTR_TYPES[x.type];++ const vcpu = Number(x.vcpu_count), ram = Number(x.ram);++ if (!type || !Number.isFinite(vcpu) || !Number.isFinite(ram) || ram <= 0) return id;++ const vendor = /-amd$/.test(id) ? ' AMD' : /-intel$/.test(id) ? ' Intel' : '';++ const memory = ram >= 1024 ? `${Math.round((ram / 1024) * 10) / 10} GB` : `${ram} MB`;++ return `${type}${vendor} · ${vcpu} vCPU, ${memory}`;++}++function mapItem(item) {const provider = SOURCES[item && item.source_key];if (!provider) return null;@@@ -63,12 +75,15 @@ function mapItem(item) {const vultr = item.source_key === 'vultr-plans';const monthly = number(vultr ? x.monthly_cost : x.price_monthly, 'monthly price');if (monthly == null) throw new TypeError('missing monthly price');++ // A free tier (Vultr's "-free" plan answers $0 with conditions attached) is not a price to compare: held, not listed.++ if (monthly <= 0 || (vultr && /-free$/.test(id))) return null;++ const disks = vultr && x.disk_count != null ? number(x.disk_count, 'disk count') : 1;return {-- ...common, name: vultr ? id : ((typeof item.title === 'string' && item.title.trim()) || id),-- kind: vultr && x.type === 'vdc' ? 'dedicated' : 'cloud',++ ...common, name: vultr ? vultrName(x, id) : ((typeof item.title === 'string' && item.title.trim()) || id),++ kind: vultr && ['vdc', 'vbm'].includes(x.type) ? 'dedicated' : 'cloud',vcpu: number(vultr ? x.vcpu_count : x.vcpus, 'vcpu'),ram_mb: number(vultr ? x.ram : x.memory, 'ram'),-- disk_gb: vultr ? number(x.disk, 'disk') : (x.disk == null ? null : number(x.disk, 'disk') / 1024),++ disk_gb: vultr ? (x.disk == null ? null : number(x.disk, 'disk') * (disks || 1)) : (x.disk == null ? null : number(x.disk, 'disk') / 1024),disk_type: vultr ? (['vhf', 'vhp'].includes(x.type) ? 'nvme' : x.type === 'vc2' ? 'ssd' : null) : null,bandwidth_gb: number(vultr ? x.bandwidth : x.transfer, 'bandwidth'),price_monthly: monthly, price_hourly: number(vultr ? x.hourly_cost : x.price_hourly, 'hourly price'),@@@ -28,6 +28,18 @@ const item = (key, id, fields, time = at, title = id) => ({ id: `itm_${'0'.repeaassert.strictEqual(mapItem(item('vultr-plans', 'vdc-1', { monthly_cost: 50, type: 'vdc' })).kind, 'dedicated');assert.strictEqual(mapItem(item('vultr-plans', 'vhf-1', { monthly_cost: 10, type: 'vhf' })).disk_type, 'nvme');});++ await check('Vultr plans are named as Vultr names them, bare metal is dedicated, disks add up, and a free tier is not a price', async () => {++ const name = (id, f) => mapItem(item('vultr-plans', id, { monthly_cost: 6, ...f })).name;++ assert.strictEqual(name('vc2-1c-1gb', { type: 'vc2', vcpu_count: 1, ram: 1024 }), 'Cloud Compute · 1 vCPU, 1 GB');++ assert.strictEqual(name('vhp-2c-4gb-amd', { type: 'vhp', vcpu_count: 2, ram: 4096 }), 'High Performance AMD · 2 vCPU, 4 GB');++ assert.strictEqual(name('vhp-1c-1gb-intel', { type: 'vhp', vcpu_count: 1, ram: 1024 }), 'High Performance Intel · 1 vCPU, 1 GB');++ assert.strictEqual(name('vc2-1c-0.5gb', { type: 'vc2', vcpu_count: 1, ram: 512 }), 'Cloud Compute · 1 vCPU, 512 MB');++ assert.strictEqual(name('odd-plan', { type: 'xyz', vcpu_count: 1, ram: 1024 }), 'odd-plan', 'an unknown type keeps its id');++ const metal = mapItem(item('vultr-plans', 'vbm-4c-32gb', { type: 'vbm', vcpu_count: 4, ram: 32768, disk: 240, disk_count: 2, monthly_cost: 120 }));++ assert.deepStrictEqual([metal.kind, metal.disk_gb], ['dedicated', 480]);++ assert.strictEqual(mapItem(item('vultr-plans', 'vc2-1c-0.5gb-free', { type: 'vc2', vcpu_count: 1, ram: 512, monthly_cost: 0 })), null);++ assert.strictEqual(await catalog.applyItem(item('vultr-plans', 'vc2-1c-0.5gb-free', { type: 'vc2', vcpu_count: 1, ram: 512, monthly_cost: 0 })), 'hold', 'held, and the feed moves past it');++ });await check('an AWS Lightsail bundle maps from the price list text, with its monthly price from 744 hours', async () => {const ls = { usagetype: 'USE1-BundleUsage:0.5GB', vcpu: '2', memory: '0.5GB', storage: '20GB', transfer: '1TB', location: 'US East (N. Virginia)', region: 'us-east-1', price_hourly: '0.0067200000', price_unit: 'Hrs' };const p = mapItem(item('aws-lightsail-bundles', '8E4A4X47ZPXKB2KD', ls, at, ls.usagetype));
@@ -48,6 +48,18 @@ function lightsail(x, id) {
@@ -48,6 +48,18 @@ function lightsail(x, id) {
ipv4: ipv6Only ? 0 : 1, locations: typeof x.location === 'string' && x.location ? [x.location] : null,
ipv4: ipv6Only ? 0 : 1, locations: typeof x.location === 'string' && x.location ? [x.location] : null,
};
};
}
}
+// Vultr's plan types as its pricing page names them; a plan is named from its type, CPU vendor, vCPUs and memory
+// ("High Performance AMD · 1 vCPU, 1 GB"), never by its API id alone.
+const VULTR_TYPES = Object.freeze({ vc2: 'Cloud Compute', vhf: 'High Frequency', vhp: 'High Performance', voc: 'Optimized Cloud', vcg: 'Cloud GPU', vbm: 'Bare Metal', vdc: 'Dedicated Cloud' });
+function vultrName(x, id) {
+ const type = VULTR_TYPES[x.type];
+ const vcpu = Number(x.vcpu_count), ram = Number(x.ram);
+ if (!type || !Number.isFinite(vcpu) || !Number.isFinite(ram) || ram <= 0) return id;
+ const vendor = /-amd$/.test(id) ? ' AMD' : /-intel$/.test(id) ? ' Intel' : '';
+ const memory = ram >= 1024 ? `${Math.round((ram / 1024) * 10) / 10} GB` : `${ram} MB`;
+ return `${type}${vendor} · ${vcpu} vCPU, ${memory}`;
+}
+
function mapItem(item) {
function mapItem(item) {
const provider = SOURCES[item && item.source_key];
const provider = SOURCES[item && item.source_key];
if (!provider) return null;
if (!provider) return null;
@@ -63,12 +75,15 @@ function mapItem(item) {
@@ -63,12 +75,15 @@ function mapItem(item) {
const vultr = item.source_key === 'vultr-plans';
const vultr = item.source_key === 'vultr-plans';
const monthly = number(vultr ? x.monthly_cost : x.price_monthly, 'monthly price');
const monthly = number(vultr ? x.monthly_cost : x.price_monthly, 'monthly price');
if (monthly == null) throw new TypeError('missing monthly price');
if (monthly == null) throw new TypeError('missing monthly price');
+ // A free tier (Vultr's "-free" plan answers $0 with conditions attached) is not a price to compare: held, not listed.
+ if (monthly <= 0 || (vultr && /-free$/.test(id))) return null;
+ const disks = vultr && x.disk_count != null ? number(x.disk_count, 'disk count') : 1;
return {
return {
- ...common, name: vultr ? id : ((typeof item.title === 'string' && item.title.trim()) || id),
- kind: vultr && x.type === 'vdc' ? 'dedicated' : 'cloud',
+ ...common, name: vultr ? vultrName(x, id) : ((typeof item.title === 'string' && item.title.trim()) || id),
+ kind: vultr && ['vdc', 'vbm'].includes(x.type) ? 'dedicated' : 'cloud',
vcpu: number(vultr ? x.vcpu_count : x.vcpus, 'vcpu'),
vcpu: number(vultr ? x.vcpu_count : x.vcpus, 'vcpu'),
ram_mb: number(vultr ? x.ram : x.memory, 'ram'),
ram_mb: number(vultr ? x.ram : x.memory, 'ram'),
- disk_gb: vultr ? number(x.disk, 'disk') : (x.disk == null ? null : number(x.disk, 'disk') / 1024),
+ disk_gb: vultr ? (x.disk == null ? null : number(x.disk, 'disk') * (disks || 1)) : (x.disk == null ? null : number(x.disk, 'disk') / 1024),
disk_type: vultr ? (['vhf', 'vhp'].includes(x.type) ? 'nvme' : x.type === 'vc2' ? 'ssd' : null) : null,
disk_type: vultr ? (['vhf', 'vhp'].includes(x.type) ? 'nvme' : x.type === 'vc2' ? 'ssd' : null) : null,
bandwidth_gb: number(vultr ? x.bandwidth : x.transfer, 'bandwidth'),
bandwidth_gb: number(vultr ? x.bandwidth : x.transfer, 'bandwidth'),
price_monthly: monthly, price_hourly: number(vultr ? x.hourly_cost : x.price_hourly, 'hourly price'),
price_monthly: monthly, price_hourly: number(vultr ? x.hourly_cost : x.price_hourly, 'hourly price'),
@@ -28,6 +28,18 @@ const item = (key, id, fields, time = at, title = id) => ({ id: `itm_${'0'.repea
@@ -28,6 +28,18 @@ const item = (key, id, fields, time = at, title = id) => ({ id: `itm_${'0'.repea
assert.strictEqual(mapItem(item('vultr-plans', 'vdc-1', { monthly_cost: 50, type: 'vdc' })).kind, 'dedicated');
assert.strictEqual(mapItem(item('vultr-plans', 'vdc-1', { monthly_cost: 50, type: 'vdc' })).kind, 'dedicated');
assert.strictEqual(mapItem(item('vultr-plans', 'vhf-1', { monthly_cost: 10, type: 'vhf' })).disk_type, 'nvme');
assert.strictEqual(mapItem(item('vultr-plans', 'vhf-1', { monthly_cost: 10, type: 'vhf' })).disk_type, 'nvme');
});
});
+ await check('Vultr plans are named as Vultr names them, bare metal is dedicated, disks add up, and a free tier is not a price', async () => {
+ const name = (id, f) => mapItem(item('vultr-plans', id, { monthly_cost: 6, ...f })).name;
+ assert.strictEqual(name('vc2-1c-1gb', { type: 'vc2', vcpu_count: 1, ram: 1024 }), 'Cloud Compute · 1 vCPU, 1 GB');
+ assert.strictEqual(name('vhp-2c-4gb-amd', { type: 'vhp', vcpu_count: 2, ram: 4096 }), 'High Performance AMD · 2 vCPU, 4 GB');
+ assert.strictEqual(name('vhp-1c-1gb-intel', { type: 'vhp', vcpu_count: 1, ram: 1024 }), 'High Performance Intel · 1 vCPU, 1 GB');
+ assert.strictEqual(name('vc2-1c-0.5gb', { type: 'vc2', vcpu_count: 1, ram: 512 }), 'Cloud Compute · 1 vCPU, 512 MB');
+ assert.strictEqual(name('odd-plan', { type: 'xyz', vcpu_count: 1, ram: 1024 }), 'odd-plan', 'an unknown type keeps its id');
+ const metal = mapItem(item('vultr-plans', 'vbm-4c-32gb', { type: 'vbm', vcpu_count: 4, ram: 32768, disk: 240, disk_count: 2, monthly_cost: 120 }));
+ assert.deepStrictEqual([metal.kind, metal.disk_gb], ['dedicated', 480]);
+ assert.strictEqual(mapItem(item('vultr-plans', 'vc2-1c-0.5gb-free', { type: 'vc2', vcpu_count: 1, ram: 512, monthly_cost: 0 })), null);
+ assert.strictEqual(await catalog.applyItem(item('vultr-plans', 'vc2-1c-0.5gb-free', { type: 'vc2', vcpu_count: 1, ram: 512, monthly_cost: 0 })), 'hold', 'held, and the feed moves past it');
+ });
await check('an AWS Lightsail bundle maps from the price list text, with its monthly price from 744 hours', async () => {
await check('an AWS Lightsail bundle maps from the price list text, with its monthly price from 744 hours', async () => {
const ls = { usagetype: 'USE1-BundleUsage:0.5GB', vcpu: '2', memory: '0.5GB', storage: '20GB', transfer: '1TB', location: 'US East (N. Virginia)', region: 'us-east-1', price_hourly: '0.0067200000', price_unit: 'Hrs' };
const ls = { usagetype: 'USE1-BundleUsage:0.5GB', vcpu: '2', memory: '0.5GB', storage: '20GB', transfer: '1TB', location: 'US East (N. Virginia)', region: 'us-east-1', price_hourly: '0.0067200000', price_unit: 'Hrs' };
const p = mapItem(item('aws-lightsail-bundles', '8E4A4X47ZPXKB2KD', ls, at, ls.usagetype));
const p = mapItem(item('aws-lightsail-bundles', '8E4A4X47ZPXKB2KD', ls, at, ls.usagetype));