diff --git a/CHANGES.md b/CHANGES.md index 609b4df..92e075a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,13 @@ Known issues: ## not yet released +## 5.8.0 + +- [TRITON-124] add node-triton support for bhyve. This adds a `triton instance + create --brand=bhyve ...` option that can be used for zvol images that support + it. Note that bhyve support is alpha in TritonDC -- most datacenters won't yet + support this option. + ## 5.7.0 - [TRITON-116] node-triton image sharing. Adds `triton image share` and diff --git a/lib/do_fwrule/do_instances.js b/lib/do_fwrule/do_instances.js index 53bc6bf..b549f16 100644 --- a/lib/do_fwrule/do_instances.js +++ b/lib/do_fwrule/do_instances.js @@ -5,7 +5,7 @@ */ /* - * Copyright 2016 Joyent, Inc. + * Copyright 2018 Joyent, Inc. * * `triton fwrule instances ...` */ @@ -111,6 +111,7 @@ function do_instances(subcmd, opts, args, cb) { common.uuidToShortId(inst.image); inst.shortid = inst.id.split('-', 1)[0]; var flags = []; + if (inst.brand === 'bhyve') flags.push('B'); if (inst.docker) flags.push('D'); if (inst.firewall_enabled) flags.push('F'); if (inst.brand === 'kvm') flags.push('K'); @@ -159,6 +160,7 @@ do_instances.help = [ 'for convenience):', ' shortid* A short ID prefix.', ' flags* Single letter flags summarizing some fields:', + ' "B" the brand is "bhyve"', ' "D" docker instance', ' "F" firewall is enabled', ' "K" the brand is "kvm"', diff --git a/lib/do_instance/do_create.js b/lib/do_instance/do_create.js index 47b7e18..886bebb 100644 --- a/lib/do_instance/do_create.js +++ b/lib/do_instance/do_create.js @@ -5,7 +5,7 @@ */ /* - * Copyright 2017 Joyent, Inc. + * Copyright 2018 Joyent, Inc. * * `triton instance create ...` */ @@ -376,6 +376,9 @@ function do_create(subcmd, opts, args, cb) { function (net) { return net.id; }) }; + if (opts.brand) { + createOpts.brand = opts.brand; + } if (ctx.volMounts) { createOpts.volumes = ctx.volMounts; } @@ -492,6 +495,13 @@ do_create.options = [ { group: 'Create options' }, + { + names: ['brand'], + helpArg: 'BRAND', + type: 'string', + help: 'Override the default brand for this instance. Most users will ' + + 'not need this option.' + }, { names: ['name', 'n'], helpArg: 'NAME', diff --git a/lib/do_instance/do_list.js b/lib/do_instance/do_list.js index 2619a3a..3892efc 100644 --- a/lib/do_instance/do_list.js +++ b/lib/do_instance/do_list.js @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. * * `triton instance list ...` */ @@ -150,6 +150,7 @@ function do_list(subcmd, opts, args, callback) { common.uuidToShortId(inst.image); inst.shortid = inst.id.split('-', 1)[0]; var flags = []; + if (inst.brand === 'bhyve') flags.push('B'); if (inst.docker) flags.push('D'); if (inst.firewall_enabled) flags.push('F'); if (inst.brand === 'kvm') flags.push('K'); @@ -208,6 +209,7 @@ do_list.help = [ 'for convenience):', ' shortid* A short ID prefix.', ' flags* Single letter flags summarizing some fields:', + ' "B" the brand is "bhyve"', ' "D" docker instance', ' "F" firewall is enabled', ' "K" the brand is "kvm"', diff --git a/lib/do_instance/do_snapshot/do_create.js b/lib/do_instance/do_snapshot/do_create.js index de7b5cf..62df2f6 100644 --- a/lib/do_instance/do_snapshot/do_create.js +++ b/lib/do_instance/do_snapshot/do_create.js @@ -5,7 +5,7 @@ */ /* - * Copyright 2016 Joyent, Inc. + * Copyright 2018 Joyent, Inc. * * `triton snapshot create ...` */ @@ -133,7 +133,7 @@ do_create.help = [ '{{usage}}', '', '{{options}}', - 'Snapshot do not work for instances of type "kvm".' + 'Snapshots do not work for instances of type "bhyve" or "kvm".' ].join('\n'); do_create.completionArgtypes = ['tritoninstance', 'none']; diff --git a/package.json b/package.json index 03cc0eb..3516090 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "triton", "description": "Joyent Triton CLI and client (https://www.joyent.com/triton)", - "version": "5.7.0", + "version": "5.8.0", "author": "Joyent (joyent.com)", "homepage": "https://github.com/joyent/node-triton", "dependencies": { diff --git a/test/config.json.sample b/test/config.json.sample index ec64816..a9326b8 100644 --- a/test/config.json.sample +++ b/test/config.json.sample @@ -26,6 +26,11 @@ // to true. "skipAffinityTests": false, + // Optional. Set to 'true' to skip testing of bhyve things. Some DCs might + // not support bhyve (no packages or images available, and/or no CNs with + // bhyve compatible hardware). + "skipBhyveTests": false, + // Optional. Set to 'true' to skip testing of KVM things. Some DCs might // not support KVM (no KVM packages or images available). "skipKvmTests": false, @@ -36,6 +41,12 @@ "resizePackage": "", "image": "" + // The params used for test *bhyve* provisions. By default the tests use: + // the smallest RAM package with "kvm" in the name, the latest + // ubuntu-certified image. + "bhyvePackage": "", + "bhyveImage": "", + // The params used for test *KVM* provisions. By default the tests use: // the smallest RAM package with "kvm" in the name, the latest // ubuntu-certified image. diff --git a/test/integration/cli-instance-create-bhyve.test.js b/test/integration/cli-instance-create-bhyve.test.js new file mode 100644 index 0000000..a437b04 --- /dev/null +++ b/test/integration/cli-instance-create-bhyve.test.js @@ -0,0 +1,92 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Copyright 2018, Joyent, Inc. + */ + +/* + * Test creating a bhyve VM. + */ + +var os = require('os'); + +var format = require('util').format; +var test = require('tape'); + +var h = require('./helpers'); + + +// --- globals + +var INST_ALIAS = 'nodetritontest-instance-create-bhyve-' + + os.hostname(); + +var testOpts = { + skip: !h.CONFIG.allowWriteActions || h.CONFIG.skipBhyveTests +}; + + +// --- Tests + +test('triton image ...', testOpts, function (tt) { + var imgId; + var inst; + var pkgId; + + tt.comment('Test config:'); + Object.keys(h.CONFIG).forEach(function (key) { + var value = h.CONFIG[key]; + tt.comment(format('- %s: %j', key, value)); + }); + + // TODO: `triton rm -f` would be helpful for this + tt.test(' setup: rm existing inst ' + INST_ALIAS, function (t) { + h.deleteTestInst(t, INST_ALIAS, function onDel() { + t.end(); + }); + }); + + tt.test(' setup: find image', function (t) { + h.getTestBhyveImg(t, function (err, _imgId) { + t.ifError(err, 'getTestImg' + (err ? ': ' + err : '')); + imgId = _imgId; + t.end(); + }); + }); + + tt.test(' setup: find test package', function (t) { + h.getTestBhyvePkg(t, function (err, _pkgId) { + t.ifError(err, 'getTestPkg' + (err ? ': ' + err : '')); + pkgId = _pkgId; + t.end(); + }); + }); + + tt.test(' setup: triton create ... -n ' + INST_ALIAS, function (t) { + var argv = ['create', '-wj', '--brand=bhyve', '-n', INST_ALIAS, + imgId, pkgId]; + h.safeTriton(t, argv, function (err, stdout) { + var lines = h.jsonStreamParse(stdout); + inst = lines[1]; + t.ok(inst.id, 'inst.id: ' + inst.id); + t.equal(lines[1].state, 'running', 'inst is running'); + t.end(); + }); + }); + + // TODO: Once have `triton ssh ...` working in test suite without hangs, + // then want to check that the created VM works. + + // Remove instance. Add a test timeout, because '-w' on delete doesn't + // have a way to know if the attempt failed or if it is just taking a + // really long time. + tt.test(' cleanup: triton rm', {timeout: 10 * 60 * 1000}, function (t) { + h.safeTriton(t, ['rm', '-w', inst.id], function () { + t.end(); + }); + }); +}); diff --git a/test/integration/helpers.js b/test/integration/helpers.js index e091dda..4a2f205 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -5,7 +5,7 @@ */ /* - * Copyright 2017 Joyent, Inc. + * Copyright 2018 Joyent, Inc. */ /* @@ -211,6 +211,46 @@ function getTestImg(t, cb) { } +/* + * Find and return an image that can be used for test *bhyve* provisions. + * + * @param {Tape} t - tape test object + * @param {Function} cb - `function (err, imgId)` + * where `imgId` is an image identifier (an image name, shortid, or id). + */ +function getTestBhyveImg(t, cb) { + if (CONFIG.bhyveImage) { + assert.string(CONFIG.bhyvePackage, 'CONFIG.bhyvePackage'); + t.ok(CONFIG.bhyveImage, 'bhyveImage from config: ' + CONFIG.bhyveImage); + cb(null, CONFIG.bhyveImage); + return; + } + + var candidateImageNames = { + 'ubuntu-certified-16.04': true + }; + safeTriton(t, ['img', 'ls', '-j'], function (err, stdout) { + var imgId; + var imgs = jsonStreamParse(stdout); + // Newest images first. + tabula.sortArrayOfObjects(imgs, ['-published_at']); + var imgRepr; + for (var i = 0; i < imgs.length; i++) { + var img = imgs[i]; + if (candidateImageNames[img.name]) { + imgId = img.id; + imgRepr = f('%s@%s', img.name, img.version); + break; + } + } + + t.ok(imgId, + f('latest bhyve image (using subset of supported names): %s (%s)', + imgId, imgRepr)); + cb(err, imgId); + }); +} + /* * Find and return an image that can be used for test *KVM* provisions. * @@ -281,6 +321,38 @@ function getTestPkg(t, cb) { }); } +/* + * Find and return an package that can be used for *bhyve* test provisions. + * + * @param {Tape} t - tape test object + * @param {Function} cb - `function (err, pkgId)` + * where `pkgId` is an package identifier (a name, shortid, or id). + */ +function getTestBhyvePkg(t, cb) { + if (CONFIG.bhyvePackage) { + assert.string(CONFIG.bhyvePackage, 'CONFIG.bhyvePackage'); + t.ok(CONFIG.bhyvePackage, 'bhyvePackage from config: ' + + CONFIG.bhyvePackage); + cb(null, CONFIG.bhyvePackage); + return; + } + + // bhyve uses the same packages as kvm + safeTriton(t, ['pkg', 'ls', '-j'], function (err, stdout) { + var pkgs = jsonStreamParse(stdout); + // Filter on those with 'kvm' in the name. + pkgs = pkgs.filter(function (pkg) { + return pkg.name.indexOf('kvm') !== -1; + }); + // Smallest RAM first. + tabula.sortArrayOfObjects(pkgs, ['memory']); + var pkgId = pkgs[0].id; + t.ok(pkgId, f('smallest (RAM) available kvm package: %s (%s)', + pkgId, pkgs[0].name)); + cb(null, pkgId); + }); +} + /* * Find and return an package that can be used for *KVM* test provisions. * @@ -511,8 +583,10 @@ module.exports = { deleteTestImg: deleteTestImg, getTestImg: getTestImg, + getTestBhyveImg: getTestBhyveImg, getTestKvmImg: getTestKvmImg, getTestPkg: getTestPkg, + getTestBhyvePkg: getTestBhyvePkg, getTestKvmPkg: getTestKvmPkg, getResizeTestPkg: getResizeTestPkg,