node-triton#98 triton inst get ID
fails obtusely on a destroyed instance
This commit is contained in:
parent
1d0bafcc5e
commit
88c52d1610
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## 4.6.0 (not yet released)
|
## 4.6.0 (not yet released)
|
||||||
|
|
||||||
|
- #98 `triton inst get ID` for a deleted instance will now emit the instance
|
||||||
|
object and error less obtusely. This adds a new `InstanceDeleted` error code
|
||||||
|
from `TritonApi`.
|
||||||
- PUBAPI-1233 firewalls: `triton fwrule ...`
|
- PUBAPI-1233 firewalls: `triton fwrule ...`
|
||||||
- PUBAPI-1234 instance snapshots: `triton inst snapshot ...`
|
- PUBAPI-1234 instance snapshots: `triton inst snapshot ...`
|
||||||
- #52 Fix 'triton ssh ...' stdout/stderr to fully flush with node >= 4.x.
|
- #52 Fix 'triton ssh ...' stdout/stderr to fully flush with node >= 4.x.
|
||||||
|
@ -235,8 +235,8 @@ function CLI() {
|
|||||||
' 0 Successful completion.',
|
' 0 Successful completion.',
|
||||||
' 1 An error occurred.',
|
' 1 An error occurred.',
|
||||||
' 2 Usage error.',
|
' 2 Usage error.',
|
||||||
' 3 "ResourceNotFound" error. Returned when an instance, image,',
|
' 3 "ResourceNotFound" error (when an instance, image, etc. with',
|
||||||
' package, etc. with the given name or id is not found.'
|
' the given name or id is not found) or "InstanceDeleted" error.'
|
||||||
/* END JSSTYLED */
|
/* END JSSTYLED */
|
||||||
].join('\n')
|
].join('\n')
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Joyent, Inc.
|
* Copyright 2016 Joyent, Inc.
|
||||||
*
|
*
|
||||||
* `triton instance get ...`
|
* `triton instance get ...`
|
||||||
*/
|
*/
|
||||||
@ -20,16 +20,14 @@ function do_get(subcmd, opts, args, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.top.tritonapi.getInstance(args[0], function (err, inst) {
|
this.top.tritonapi.getInstance(args[0], function (err, inst) {
|
||||||
if (err) {
|
if (inst) {
|
||||||
return cb(err);
|
if (opts.json) {
|
||||||
|
console.log(JSON.stringify(inst));
|
||||||
|
} else {
|
||||||
|
console.log(JSON.stringify(inst, null, 4));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cb(err);
|
||||||
if (opts.json) {
|
|
||||||
console.log(JSON.stringify(inst));
|
|
||||||
} else {
|
|
||||||
console.log(JSON.stringify(inst, null, 4));
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +52,10 @@ do_get.help = (
|
|||||||
+ '\n'
|
+ '\n'
|
||||||
+ '{{options}}'
|
+ '{{options}}'
|
||||||
+ '\n'
|
+ '\n'
|
||||||
+ 'Note: Currently this dumps prettified JSON by default. That might change\n'
|
+ 'A *deleted* instance may still respond with the instance object. In that\n'
|
||||||
|
+ 'case a the instance will be print *and* an error will be raised.\n'
|
||||||
|
+ '\n'
|
||||||
|
+ 'Currently this dumps prettified JSON by default. That might change\n'
|
||||||
+ 'in the future. Use "-j" to explicitly get JSON output.\n'
|
+ 'in the future. Use "-j" to explicitly get JSON output.\n'
|
||||||
/* END JSSTYLED */
|
/* END JSSTYLED */
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Joyent, Inc.
|
* Copyright 2016 Joyent, Inc.
|
||||||
*
|
*
|
||||||
* Error classes that the joyent CLI may produce.
|
* Error classes that the joyent CLI may produce.
|
||||||
*/
|
*/
|
||||||
@ -233,6 +233,24 @@ function ResourceNotFoundError(cause, msg) {
|
|||||||
util.inherits(ResourceNotFoundError, _TritonBaseWError);
|
util.inherits(ResourceNotFoundError, _TritonBaseWError);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance was deleted.
|
||||||
|
*/
|
||||||
|
function InstanceDeletedError(cause, msg) {
|
||||||
|
if (msg === undefined) {
|
||||||
|
msg = cause;
|
||||||
|
cause = undefined;
|
||||||
|
}
|
||||||
|
_TritonBaseWError.call(this, {
|
||||||
|
cause: cause,
|
||||||
|
message: msg,
|
||||||
|
code: 'InstanceDeleted',
|
||||||
|
exitStatus: 3
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(InstanceDeletedError, _TritonBaseWError);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiple errors in a group.
|
* Multiple errors in a group.
|
||||||
*/
|
*/
|
||||||
@ -266,6 +284,7 @@ module.exports = {
|
|||||||
SelfSignedCertError: SelfSignedCertError,
|
SelfSignedCertError: SelfSignedCertError,
|
||||||
TimeoutError: TimeoutError,
|
TimeoutError: TimeoutError,
|
||||||
ResourceNotFoundError: ResourceNotFoundError,
|
ResourceNotFoundError: ResourceNotFoundError,
|
||||||
|
InstanceDeletedError: InstanceDeletedError,
|
||||||
MultiError: MultiError
|
MultiError: MultiError
|
||||||
};
|
};
|
||||||
// vim: set softtabstop=4 shiftwidth=4:
|
// vim: set softtabstop=4 shiftwidth=4:
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Joyent, Inc.
|
* Copyright 2016 Joyent, Inc.
|
||||||
*
|
*
|
||||||
* Core TritonApi client driver class.
|
* Core TritonApi client driver class.
|
||||||
*/
|
*/
|
||||||
@ -602,17 +602,18 @@ TritonApi.prototype.getNetwork = function getNetwork(name, cb) {
|
|||||||
/**
|
/**
|
||||||
* Get an instance.
|
* Get an instance.
|
||||||
*
|
*
|
||||||
* Alternative call signature: `getInstance(id, callback)`.
|
* Alternative call signature: `getInstance(id, cb)`.
|
||||||
*
|
*
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* - {UUID} id: The instance ID, name, or short ID. Required.
|
* - {UUID} id: The instance ID, name, or short ID. Required.
|
||||||
* - {Array} fields: Optional. An array of instance field names that are
|
* - {Array} fields: Optional. An array of instance field names that are
|
||||||
* wanted by the caller. This *can* allow the implementation to avoid
|
* wanted by the caller. This *can* allow the implementation to avoid
|
||||||
* extra API calls. E.g. `['id', 'name']`.
|
* extra API calls. E.g. `['id', 'name']`.
|
||||||
* @param {Function} callback `function (err, inst, res)`
|
* @param {Function} cb `function (err, inst, res)`
|
||||||
* On success, `res` is the response object from a `GetMachine`, if one
|
* Note that deleted instances will result in `err` being a
|
||||||
* was made (possibly not if the instance was retrieved from `ListMachines`
|
* `InstanceDeletedError` and `inst` being defined. On success, `res` is
|
||||||
* calls).
|
* the response object from a `GetMachine`, if one was made (possibly not
|
||||||
|
* if the instance was retrieved from `ListMachines` calls).
|
||||||
*/
|
*/
|
||||||
TritonApi.prototype.getInstance = function getInstance(opts, cb) {
|
TritonApi.prototype.getInstance = function getInstance(opts, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -624,6 +625,24 @@ TritonApi.prototype.getInstance = function getInstance(opts, cb) {
|
|||||||
assert.optionalArrayOfString(opts.fields, 'opts.fields');
|
assert.optionalArrayOfString(opts.fields, 'opts.fields');
|
||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some wrapping/massaging of some CloudAPI GetMachine errors.
|
||||||
|
*/
|
||||||
|
var errFromGetMachineErr = function (err) {
|
||||||
|
if (!err) {
|
||||||
|
// jsl:pass
|
||||||
|
} else if (err.restCode === 'ResourceNotFound') {
|
||||||
|
// The CloudApi 404 error message sucks: "VM not found".
|
||||||
|
err = new errors.ResourceNotFoundError(err,
|
||||||
|
format('instance with id %s was not found', opts.id));
|
||||||
|
} else if (err.statusCode === 410) {
|
||||||
|
// GetMachine returns '410 Gone' for deleted machines.
|
||||||
|
err = new errors.InstanceDeletedError(err,
|
||||||
|
format('instance %s was deleted', opts.id));
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
var res;
|
var res;
|
||||||
var shortId;
|
var shortId;
|
||||||
var inst;
|
var inst;
|
||||||
@ -646,11 +665,7 @@ TritonApi.prototype.getInstance = function getInstance(opts, cb) {
|
|||||||
self.cloudapi.getMachine(uuid, function (err, inst_, res_) {
|
self.cloudapi.getMachine(uuid, function (err, inst_, res_) {
|
||||||
res = res_;
|
res = res_;
|
||||||
inst = inst_;
|
inst = inst_;
|
||||||
if (err && err.restCode === 'ResourceNotFound') {
|
err = errFromGetMachineErr(err);
|
||||||
// The CloudApi 404 error message sucks: "VM not found".
|
|
||||||
err = new errors.ResourceNotFoundError(err,
|
|
||||||
format('instance with id %s was not found', opts.id));
|
|
||||||
}
|
|
||||||
next(err);
|
next(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -739,19 +754,13 @@ TritonApi.prototype.getInstance = function getInstance(opts, cb) {
|
|||||||
self.cloudapi.getMachine(uuid, function (err, inst_, res_) {
|
self.cloudapi.getMachine(uuid, function (err, inst_, res_) {
|
||||||
res = res_;
|
res = res_;
|
||||||
inst = inst_;
|
inst = inst_;
|
||||||
if (err && err.restCode === 'ResourceNotFound') {
|
err = errFromGetMachineErr(err);
|
||||||
// The CloudApi 404 error message sucks: "VM not found".
|
|
||||||
err = new errors.ResourceNotFoundError(err,
|
|
||||||
format('instance with id %s was not found', opts.id));
|
|
||||||
}
|
|
||||||
next(err);
|
next(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
]}, function (err) {
|
]}, function (err) {
|
||||||
if (err) {
|
if (err || inst) {
|
||||||
cb(err);
|
cb(err, inst, res);
|
||||||
} else if (inst) {
|
|
||||||
cb(null, inst, res);
|
|
||||||
} else {
|
} else {
|
||||||
cb(new errors.ResourceNotFoundError(format(
|
cb(new errors.ResourceNotFoundError(format(
|
||||||
'no instance with name or short id "%s" was found', opts.id)));
|
'no instance with name or short id "%s" was found', opts.id)));
|
||||||
|
@ -140,6 +140,21 @@ test('triton manage workflow', opts, function (tt) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Test the '410 Gone' handling from CloudAPI GetMachine.
|
||||||
|
tt.test(' triton inst get (deleted)', function (t) {
|
||||||
|
h.triton(['inst', 'get', instance.id], function (err, stdout, stderr) {
|
||||||
|
t.ok(err, 'got err: ' + err);
|
||||||
|
t.equal(err.code, 3, 'exit status of 3');
|
||||||
|
var errCodeRe = /InstanceDeleted/;
|
||||||
|
t.ok(errCodeRe.exec(stderr),
|
||||||
|
f('stderr matched %s: %j', errCodeRe, stderr));
|
||||||
|
t.ok(stdout, 'still got stdout');
|
||||||
|
var inst = JSON.parse(stdout);
|
||||||
|
t.equal(inst.state, 'deleted', 'instance state is "deleted"');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: would be nice to have a `triton ssh cat /var/log/boot.log` to
|
// TODO: would be nice to have a `triton ssh cat /var/log/boot.log` to
|
||||||
// verify the user-script worked.
|
// verify the user-script worked.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user