Skip to content

Commit

Permalink
Merge pull request ottomatica#23 from gjabell/kvm
Browse files Browse the repository at this point in the history
Add KVM support
  • Loading branch information
chrisparnin authored Jun 2, 2019
2 parents efd934d + cafa4cd commit 03140b0
Show file tree
Hide file tree
Showing 13 changed files with 367 additions and 79 deletions.
9 changes: 6 additions & 3 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const { slimdir, scriptdir } = env.vars();
const docker = new Docker();

async function build(context) {
let { provider, buildPath, outputPath, dockerOpts } = context;
let { provider, buildPath, outputDir, dockerOpts } = context;

let exportDir = path.join(slimdir, 'slim-vm');
await fs.emptyDir(exportDir);
Expand All @@ -32,11 +32,14 @@ async function build(context) {
case 'hyperkit':
throw new Error('Hyperkit is not yet supported');
case 'kvm':
throw new Error('KVM is not yet supported');
case 'virtualbox':
default:
info('copying initrd and vmlinuz');
await fs.copy(path.join(slimdir, 'vmlinuz'), path.join(outputDir, 'vmlinuz'));
await fs.copy(path.join(slimdir, 'initrd'), path.join(outputDir, 'initrd'));
info('creating microkernel');
await buildIso(outputPath);
await buildIso(path.join(outputDir, 'slim.iso'));
break;
}

ok('success!');
Expand Down
16 changes: 13 additions & 3 deletions lib/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { error } = require('../logger');

const build = require('../build');
const env = require('../env');
const providers = require('../providers');

const { pubkey } = env.vars();

Expand All @@ -21,17 +22,23 @@ exports.builder = yargs => {
},
provider: {
alias: 'p',
choices: ['virtualbox', 'hyperkit', 'kvm'],
choices: Object.keys(providers),
default: 'virtualbox',
description: 'the vm provider to use',
type: 'string'
}
});
};

const compatMap = {
'kvm': ['kvm', 'virtualbox'],
'virtualbox': ['kvm', 'virtualbox'],
'hyperkit': ['hyperkit']
}

exports.handler = async argv => {
const { path, cache, provider } = argv;
let { buildPath, infoPath, outputPath } = await env.makeContext(path);
let { buildPath, infoPath, outputDir } = await env.makeContext(path);

let info = await yaml.safeLoad(fs.readFileSync(infoPath));
let pkgs = '';
Expand All @@ -40,10 +47,13 @@ exports.handler = async argv => {
if (info.base_directory) buildPath = p.join(buildPath, info.base_directory);
if (info.base_args) pkgs = info.base_args.PKGS;

info.providers = compatMap[provider];
await fs.writeFile(p.join(outputDir, 'info.yml'), await yaml.safeDump(info));

let context = {
provider,
buildPath,
outputPath,
outputDir,
dockerOpts: {
nocache: !cache,
buildargs: {
Expand Down
38 changes: 26 additions & 12 deletions lib/commands/delete.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
const fs = require('fs-extra');
const path = require('path');

const { error } = require('../logger');
const { error, ok } = require('../logger');

const env = require('../env');
const images = require('../images');
const micro = require('../micro');
const providers = require('../providers');

const { registry } = env.vars();

exports.command = 'delete <vm|image> <name>';
exports.desc = 'Delete a microkernel image or vm';

exports.builder = () => {};
exports.builder = yargs => {
yargs.options({
provider: {
alias: 'p',
choices: ['virtualbox', 'hyperkit', 'kvm'],
default: 'virtualbox',
description: 'the vm provider to use',
type: 'string'
}
});
};

exports.handler = async argv => {
const { vm, image, name } = argv;
// both vm and image have the same value
const { vm: command, name, provider } = argv;

if (vm === 'vm') {
if (name) await micro.delete(name);
} else if (image === 'image') {
if (await images.exists(name, registry)) {
await fs.remove(path.resolve(registry, name));
} else {
error(`${name} image not found`);
try {
switch (command) {
case 'vm':
await providers[provider].delete(name);
break;
case 'image':
await fs.remove(path.resolve(registry, name));
break;
}
ok(`${name} deleted`);
} catch (e) {
error(e);
}
};
35 changes: 27 additions & 8 deletions lib/commands/images.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
const env = require('../env');
const images = require('../images');
const { info } = require('../logger');

const { registry } = env.vars();
const images = require('../images');

exports.command = 'images';
exports.desc = 'List available images';

exports.builder = () => {};

exports.handler = async () => {
let table = await images.list(registry);
let transformed = table.reduce((table, {image, ...x}) => {
table[image] = x;
return table;
}, {});
let table = await images.list();

if (table.length === 0) {
info('No images');
return;
}

let transformed = table
.map(i => ({
image: i.image,
size: sizeToHumanSize(i.size),
description: i.description,
providers: i.providers.join(', ')
}))
.reduce((table, {image, ...x}) => {
table[image] = x;
return table;
}, {}
);

console.table(transformed);
};

function sizeToHumanSize(size) {
if( size == 0 ) return 0;
var i = Math.floor( Math.log(size) / Math.log(1024) );
return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
31 changes: 16 additions & 15 deletions lib/commands/run.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
const { error } = require('../logger');

const env = require('../env');
const images = require('../images');
const micro = require('../micro');

const { registry } = env.vars();
const providers = require('../providers');

exports.command = 'run <name> <image>';
exports.desc = 'Provision a new micro kernel';
Expand Down Expand Up @@ -32,18 +29,22 @@ exports.builder = yargs => {
};

exports.handler = async argv => {
const { image, cpus, memory, name } = argv;
const { image, cpus, memory, name, provider } = argv;

if (await images.exists(image, registry)) {
let info = await images.info(image, registry).catch(e => error(e));
if (!await images.exists(image, provider)) {
error(`${image} image not found`);
return;
}

await micro.create(name, registry, {
attach_iso: image,
cpus: cpus || info.cpus,
mem: memory || info.memory
}).catch(e => error(e));
} else {
error(`${image} image not found.`);
process.exit(1);
let info = await images.info(image).catch(e => error(e));
if (info.providers.indexOf(provider) == -1) {
error(`Please rebuild ${image} for ${provider}`);
return;
}

await providers[provider].create(name, {
image: image,
cpus: cpus || info.cpus,
mem: memory || info.memory
}).catch(e => error(e));
};
42 changes: 42 additions & 0 deletions lib/commands/vms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const { info } = require('../logger');

const providers = require('../providers');

exports.command = 'vms';
exports.desc = 'List virtual machines';

exports.builder = yargs => {
yargs.options({
provider: {
alias: 'p',
choices: Object.keys(providers),
description: 'the vm provider to use',
type: 'string'
}
});
};

exports.handler = async argv => {
let { provider } = argv;

let vms = [];
if (provider) {
vms.push(...await providers[provider].list());
} else {
for (const provider of Object.keys(providers)) {
vms.push(...(await providers[provider].list()).map(v => ({...v, provider: provider})));
}
}

if (vms.length === 0) {
info('No virtual machines');
return;
}

let transformed = vms.reduce((table, {id, ...x}) => {
table[id] = x;
return table;
}, {});

console.table(transformed);
}
6 changes: 2 additions & 4 deletions lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,18 @@ class Env {
let buildPath = path.resolve(p || path.join(scriptdir, 'images', 'alpine3.8-runc-ansible'));
let infoPath = path.join(buildPath, 'info.yml');
let name = path.basename(buildPath);
let outputPath = path.join(registry, name, 'slim.iso');
let outputDir = path.dirname(outputPath);
let outputDir = path.join(registry, name);

await Promise.all([
expectPath(infoPath, `Expected config does not exist in ${infoPath}`),
expectPath(buildPath, `Build path ${buildPath} does not exist`),
fs.ensureDir(outputDir),
fs.copyFile(infoPath, path.join(outputDir, 'info.yml'))
]);

return {
buildPath,
infoPath,
outputPath
outputDir
};
}

Expand Down
53 changes: 26 additions & 27 deletions lib/images.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,41 @@ const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');

const env = require('./env');
const providers = require('./providers');

const { registry } = env.vars();

class Images {
constructor() {}

async list(registery)
async list()
{
let sizeToHumanSize = function (size) {
if( size == 0 ) return 0;
var i = Math.floor( Math.log(size) / Math.log(1024) );
return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
};

let table = [];
for( let dir of fs.readdirSync(registery) )
{
let iso = path.join(registery, dir, 'slim.iso');
let img = {};
img.image = dir;
img.size = sizeToHumanSize(fs.statSync(iso).size)

let info = await yaml.safeLoad(fs.readFileSync(path.join(registery, dir, 'info.yml')));
img.description = info.description;
table.push(img);
}
return table;
return await Promise.all(fs.readdirSync(registry).map(async name => {
let info = await this.info(name, registry);

let total = 0;
for (const p of info.providers) {
let size = await providers[p].size(name);
total += size;
}

return {
image: name,
size: total,
description: info.description,
providers: info.providers
};
}));
}

async exists(name, registery) {
const images = (await this.list(registery)).map(image => image.image);
return images.includes(name);
async exists(name, provider) {
return providers[provider].exists(name);
}

async info(name, registery) {
return await yaml.safeLoad(fs.readFileSync(path.join(registery, name, 'info.yml')));
async info(name) {
return await yaml.safeLoad(fs.readFileSync(path.join(registry, name, 'info.yml')));
}
}



module.exports = new Images();
10 changes: 10 additions & 0 deletions lib/providers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// TODO add hyperkit
const kvm = require('./providers/kvm');
const vbox = require('./providers/virtualbox');

const providers = {
'kvm': kvm,
'virtualbox': vbox
};

module.exports = providers;
Loading

0 comments on commit 03140b0

Please sign in to comment.