From 314a05b08231e767d4e8e72b9bccc6bf3c06ab07 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Mon, 15 Feb 2016 10:03:08 -0800 Subject: [PATCH] node-triton#95 Add test case for 'triton img create', changelog update --- CHANGES.md | 2 +- test/integration/cli-image-create.test.js | 210 ++++++++++++++++++++++ test/integration/helpers.js | 6 +- 3 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 test/integration/cli-image-create.test.js diff --git a/CHANGES.md b/CHANGES.md index a49fb16..c1f61cd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ## 4.5.2 (not yet released) -(nothing yet) +- #95 Fix breakage of `triton image create` in version 4.5.0. (By Kris Shannon.) ## 4.5.1 diff --git a/test/integration/cli-image-create.test.js b/test/integration/cli-image-create.test.js new file mode 100644 index 0000000..e549290 --- /dev/null +++ b/test/integration/cli-image-create.test.js @@ -0,0 +1,210 @@ +/* + * 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 2016, Joyent, Inc. + */ + +/* + * Test image commands. + */ + +var format = require('util').format; +var os = require('os'); +var test = require('tape'); +var vasync = require('vasync'); + +var common = require('../../lib/common'); +var h = require('./helpers'); + + +// --- globals + +var ORIGIN_ALIAS = format('nodetritontest-images-%s-origin', os.hostname()); +var IMAGE_DATA = { + name: format('nodetritontest-images-%s', os.hostname()), + version: '1.0.0' +}; +var DERIVED_ALIAS = format('nodetritontest-images-%s-derived', os.hostname()); + +var testOpts = { + skip: !h.CONFIG.allowWriteActions +}; + + +// --- Tests + +test('triton image ...', testOpts, function (tt) { + var imgNameVer = IMAGE_DATA.name + '@' + IMAGE_DATA.version; + var originInst; + var img; + + 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 origin inst ' + ORIGIN_ALIAS, function (t) { + h.triton(['inst', 'get', '-j', ORIGIN_ALIAS], + function (err, stdout, stderr) { + if (err) { + if (err.code === 3) { // `triton` code for ResourceNotFound + t.ok(true, 'no pre-existing inst ' + ORIGIN_ALIAS); + t.end(); + } else { + t.ifErr(err, err); + t.end(); + } + } else { + var instToRm = JSON.parse(stdout); + h.safeTriton(t, ['inst', 'rm', '-w', instToRm.id], function () { + t.ok(true, 'deleted inst ' + instToRm.id); + t.end(); + }); + } + }); + }); + + // TODO: `triton rm -f` would be helpful for this + tt.test(' setup: rm existing derived inst ' + DERIVED_ALIAS, function (t) { + h.triton(['inst', 'get', '-j', DERIVED_ALIAS], + function (err, stdout, stderr) { + if (err) { + if (err.code === 3) { // `triton` code for ResourceNotFound + t.ok(true, 'no pre-existing inst ' + DERIVED_ALIAS); + t.end(); + } else { + t.ifErr(err, err); + t.end(); + } + } else { + var instToRm = JSON.parse(stdout); + h.safeTriton(t, ['inst', 'rm', '-w', instToRm.id], function () { + t.ok(true, 'deleted inst ' + instToRm.id); + t.end(); + }); + } + }); + }); + + tt.test(' setup: rm existing img ' + imgNameVer, function (t) { + h.triton(['img', 'get', '-j', imgNameVer], + function (err, stdout, stderr) { + if (err) { + if (err.code === 3) { // `triton` code for ResourceNotFound + t.ok(true, 'no pre-existing img ' + imgNameVer); + t.end(); + } else { + t.ifErr(err, err); + t.end(); + } + } else { + var imgToRm = JSON.parse(stdout); + h.safeTriton(t, ['image', 'rm', '-f', imgToRm.id], function () { + t.ok(true, 'deleted img ' + imgToRm.id); + t.end(); + }); + } + }); + }); + + var originImgNameOrId; + tt.test(' setup: find origin image', function (t) { + h.getTestImg(t, function (err, imgId) { + t.ifError(err, 'getTestImg' + (err ? ': ' + err : '')); + originImgNameOrId = imgId; + t.end(); + }); + }); + + var pkgId; + tt.test(' setup: find test package', function (t) { + h.getTestPkg(t, function (err, pkgId_) { + t.ifError(err, 'getTestPkg' + (err ? ': ' + err : '')); + pkgId = pkgId_; + t.end(); + }); + }); + + var markerFile = '/nodetritontest-was-here.txt'; + tt.test(' setup: triton create ... -n ' + ORIGIN_ALIAS, function (t) { + var argv = ['create', '-wj', '-n', ORIGIN_ALIAS, + '-m', 'user-script=touch ' + markerFile, + originImgNameOrId, pkgId]; + h.safeTriton(t, argv, function (err, stdout) { + var lines = h.jsonStreamParse(stdout); + originInst = lines[1]; + t.ok(originInst.id, 'originInst.id: ' + originInst.id); + t.equal(lines[1].state, 'running', 'originInst is running'); + t.end(); + }); + }); + + // TODO: I'd like to use this 'triton ssh INST touch $markerFile' to + // tweak the image. However, that current hangs when run via + // tape (don't know why yet). Instead we'll use a user-script to + // change the origin as our image change. + // + //tt.test(' setup: add marker to origin', function (t) { + // var argv = ['ssh', originInst.id, + // '-o', 'StrictHostKeyChecking=no', + // '-o', 'UserKnownHostsFile=/dev/null', + // 'touch', markerFile]; + // h.safeTriton(t, argv, function (err, stdout) { + // t.ifError(err, 'adding origin marker file, err=' + err); + // t.end(); + // }); + //}); + + tt.test(' triton image create ...', function (t) { + var argv = ['image', 'create', '-j', '-w', '-t', 'foo=bar', + originInst.id, IMAGE_DATA.name, IMAGE_DATA.version]; + h.safeTriton(t, argv, function (err, stdout) { + var lines = h.jsonStreamParse(stdout); + img = lines[1]; + t.ok(img, 'created image, id=' + img.id); + t.equal(img.name, IMAGE_DATA.name, 'img.name'); + t.equal(img.version, IMAGE_DATA.version, 'img.version'); + t.equal(img.public, false, 'img.public is false'); + t.equal(img.state, 'active', 'img.state is active'); + t.equal(img.origin, originInst.image, 'img.origin'); + t.end(); + }); + }); + + var derivedInst; + tt.test(' triton create ... -n ' + DERIVED_ALIAS, function (t) { + var argv = ['create', '-wj', '-n', DERIVED_ALIAS, img.id, pkgId]; + h.safeTriton(t, argv, function (err, stdout) { + var lines = h.jsonStreamParse(stdout); + derivedInst = lines[1]; + t.ok(derivedInst.id, 'derivedInst.id: ' + derivedInst.id); + t.equal(lines[1].state, 'running', 'derivedInst is running'); + t.end(); + }); + }); + + // TODO: Once have `triton ssh ...` working in test suite without hangs, + // then want to check that the created VM has the markerFile. + + // Remove instances. 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', originInst.id, derivedInst.id], + function () { + t.end(); + }); + }); + + tt.test(' cleanup: triton image rm', function (t) { + h.safeTriton(t, ['image', 'rm', '-f', img.id], function () { + t.end(); + }); + }); +}); diff --git a/test/integration/helpers.js b/test/integration/helpers.js index 5ae775a..fa6f8d4 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -141,10 +141,10 @@ function safeTriton(t, opts, cb) { assert.optionalBool(opts.json, 'opts.json'); assert.func(cb, 'cb'); - t.comment(f('running: triton %s', opts.args.join(' '))); + // t.comment(f('running: triton %s', opts.args.join(' '))); triton(opts.args, function (err, stdout, stderr) { - t.error(err, 'no error running child process'); - t.equal(stderr, '', 'no stderr produced'); + t.error(err, f('ran "triton %s", err=%s', opts.args.join(' '), err)); + t.equal(stderr, '', 'empty stderr'); if (opts.json) { try { stdout = JSON.parse(stdout);