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)

OP OpenVibers committed 30 days ago · 7fb404f73f · 2 files, +30 −3

Plan T20 (Host: hosting compared): Vultr's plans are about to be listed. Vultr's public plans API answers without a key: 138 plans, checked from the production host. Its mapping was not ready for them.

**Changes**
- **Names.** A plan is named as Vultr's pricing page names it, from the type, CPU vendor, vCPUs and memory, for example "High Performance AMD · 2 vCPU, 4 GB". Before, the raw API id was shown (`vhp-2c-4gb-amd`). An unknown type keeps its id.
- **A free tier is not a price.** Vultr's `vc2-1c-0.5gb-free` answers $0 with conditions attached, and it would have topped "Lowest monthly prices". A $0 plan, or a `-free` one, is held: not listed, and the feed moves past it. Any other provider's $0 is held too.
- **Bare metal** (`vbm`) is `dedicated`, like `vdc`.
- **Disks add up:** `disk × disk_count`.

**Tests**
- `catalog-ingest` covers names, vendors, bare metal, disk totals and the held free tier.
- The full suite passes.

After deploy, the `vultr-plans` source (seeded, never enabled) is enabled in Sources.

Changes

ddiff --git a/server/catalog/mapping.js b/server/catalog/mapping.js
iindex 34d9c51..649bb8a 100644
---- a/server/catalog/mapping.js
++++ b/server/catalog/mapping.js
@@@ -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'),
ddiff --git a/test/catalog-ingest.test.js b/test/catalog-ingest.test.js
iindex d392102..9961d1e 100644
---- a/test/catalog-ingest.test.js
++++ b/test/catalog-ingest.test.js
@@@ -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', '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));