joyent/node-triton#54 'triton rbac role-tags' and refactoring to make adding other *-role-tags easy
This commit is contained in:
parent
4e45e4061f
commit
cd611dafde
@ -22,6 +22,8 @@
|
|||||||
configuration. This command is still in development.
|
configuration. This command is still in development.
|
||||||
- `triton rbac instance-role-tags ...` to list and manage role tags
|
- `triton rbac instance-role-tags ...` to list and manage role tags
|
||||||
on an instance.
|
on an instance.
|
||||||
|
- `triton rbac role-tags ...` lower-level command for managing role
|
||||||
|
tags directly on cloudapi RBAC resource *urls*.
|
||||||
|
|
||||||
|
|
||||||
## 2.1.4
|
## 2.1.4
|
||||||
|
@ -103,7 +103,9 @@ SaferJsonClient.prototype.parse = function parse(req, callback) {
|
|||||||
|
|
||||||
// Content-Length check
|
// Content-Length check
|
||||||
var contentLength = Number(res.headers['content-length']);
|
var contentLength = Number(res.headers['content-length']);
|
||||||
if (!isNaN(contentLength) && len !== contentLength) {
|
if (req.method !== 'HEAD' &&
|
||||||
|
!isNaN(contentLength) && len !== contentLength)
|
||||||
|
{
|
||||||
resErr = new errors.InvalidContentError(util.format(
|
resErr = new errors.InvalidContentError(util.format(
|
||||||
'Incomplete content: Content-Length:%s but got %s bytes',
|
'Incomplete content: Content-Length:%s but got %s bytes',
|
||||||
contentLength, len));
|
contentLength, len));
|
||||||
|
@ -1170,6 +1170,83 @@ CloudApi.prototype.deletePolicy = function deletePolicy(opts, cb) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---- RBAC role tag support functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <http://apidocs.joyent.com/cloudapi/#role-tags>
|
||||||
|
* Get RBAC role-tags on a resource. Technically there isn't a separate
|
||||||
|
* specific API endpoint for this -- it is the Get$Resource endpoint instead.
|
||||||
|
*
|
||||||
|
* @param {Object} opts:
|
||||||
|
* - {String} resource (required) The resource URL. E.g.
|
||||||
|
* '/:account/machines/:uuid' to tag a particular machine instance.
|
||||||
|
* @param {Function} cb of the form `function (err, roleTags, resource, res)`
|
||||||
|
* @throws {AssertionError, TypeError} on invalid inputs
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.getRoleTags = function getRoleTags(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.string(opts.resource, 'opts.resource');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
// Validate `resource`.
|
||||||
|
// TODO: share this validation with `setRoleTags`.
|
||||||
|
var resourceRe = new RegExp('^/[^/]{2,}/[^/]+');
|
||||||
|
if (! resourceRe.test(opts.resource)) {
|
||||||
|
throw new TypeError(format('invalid resource "%s": must match ' +
|
||||||
|
'"/:account/:type..."', opts.resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
var validResources = [
|
||||||
|
'machines',
|
||||||
|
'packages',
|
||||||
|
'images',
|
||||||
|
'fwrules',
|
||||||
|
'networks',
|
||||||
|
// TODO: validate/test role tags on these rbac resources
|
||||||
|
'users',
|
||||||
|
'roles',
|
||||||
|
'policies',
|
||||||
|
// TODO: validate, test
|
||||||
|
'keys',
|
||||||
|
'datacenters',
|
||||||
|
'analytics',
|
||||||
|
'instrumentations'
|
||||||
|
];
|
||||||
|
var parts = opts.resource.split('/');
|
||||||
|
if (validResources.indexOf(parts[2]) === -1) {
|
||||||
|
throw new TypeError(format('invalid resource "%s": resource type ' +
|
||||||
|
'must be one of: %s', opts.resource, validResources.join(', ')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function roleTagsFromRes(res) {
|
||||||
|
return (
|
||||||
|
(res.headers['role-tag'] || '')
|
||||||
|
/* JSSTYLED */
|
||||||
|
.split(/\s*,\s*/)
|
||||||
|
.filter(function (r) { return r.trim(); })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._request({
|
||||||
|
/*
|
||||||
|
* We use GET instead of HEAD to also be able to return the
|
||||||
|
* resource JSON. Technically we *could* drop support for that from
|
||||||
|
* this API, but `tritonapi.js` is using it.
|
||||||
|
*/
|
||||||
|
method: 'GET',
|
||||||
|
path: opts.resource
|
||||||
|
}, function (err, req, res, body) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, null, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roleTags = roleTagsFromRes(res);
|
||||||
|
cb(err, roleTags, body, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <http://apidocs.joyent.com/cloudapi/#SetRoleTags>
|
* <http://apidocs.joyent.com/cloudapi/#SetRoleTags>
|
||||||
* Set RBAC role-tags on a resource.
|
* Set RBAC role-tags on a resource.
|
||||||
@ -1191,7 +1268,6 @@ CloudApi.prototype.setRoleTags = function setRoleTags(opts, cb) {
|
|||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
// Validate `resource`.
|
// Validate `resource`.
|
||||||
// XXX Do we need to massage '/my' to '/:account' as old cloudapi.js does?
|
|
||||||
var resourceRe = new RegExp('^/[^/]{2,}/[^/]+');
|
var resourceRe = new RegExp('^/[^/]{2,}/[^/]+');
|
||||||
if (! resourceRe.test(opts.resource)) {
|
if (! resourceRe.test(opts.resource)) {
|
||||||
throw new TypeError(format('invalid resource "%s": must match ' +
|
throw new TypeError(format('invalid resource "%s": must match ' +
|
||||||
|
@ -333,7 +333,7 @@ function normShortId(s) {
|
|||||||
* and DC URL. This is currently used to create a filesystem-safe name
|
* and DC URL. This is currently used to create a filesystem-safe name
|
||||||
* to use for caching
|
* to use for caching
|
||||||
*/
|
*/
|
||||||
function slug(o) {
|
function profileSlug(o) {
|
||||||
assert.object(o, 'o');
|
assert.object(o, 'o');
|
||||||
assert.string(o.account, 'o.account');
|
assert.string(o.account, 'o.account');
|
||||||
assert.string(o.url, 'o.url');
|
assert.string(o.url, 'o.url');
|
||||||
@ -344,6 +344,17 @@ function slug(o) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a filename-safe slug for the given string.
|
||||||
|
*/
|
||||||
|
function filenameSlug(str) {
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/ +/g, '-')
|
||||||
|
.replace(/[^-\w]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* take some basic information and return node-cmdln options suitable for
|
* take some basic information and return node-cmdln options suitable for
|
||||||
* tabula
|
* tabula
|
||||||
@ -723,7 +734,8 @@ module.exports = {
|
|||||||
capitalize: capitalize,
|
capitalize: capitalize,
|
||||||
normShortId: normShortId,
|
normShortId: normShortId,
|
||||||
uuidToShortId: uuidToShortId,
|
uuidToShortId: uuidToShortId,
|
||||||
slug: slug,
|
profileSlug: profileSlug,
|
||||||
|
filenameSlug: filenameSlug,
|
||||||
getCliTableOptions: getCliTableOptions,
|
getCliTableOptions: getCliTableOptions,
|
||||||
promptYesNo: promptYesNo,
|
promptYesNo: promptYesNo,
|
||||||
promptEnter: promptEnter,
|
promptEnter: promptEnter,
|
||||||
|
@ -27,90 +27,6 @@ var errors = require('../errors');
|
|||||||
|
|
||||||
// ---- internal support stuff
|
// ---- internal support stuff
|
||||||
|
|
||||||
function _listRoleTags(opts, cb) {
|
|
||||||
assert.object(opts.cli, 'opts.cli');
|
|
||||||
assert.string(opts.resourceId, 'opts.resourceId');
|
|
||||||
assert.optionalBool(opts.json, 'opts.json');
|
|
||||||
assert.func(cb, 'cb');
|
|
||||||
var cli = opts.cli;
|
|
||||||
|
|
||||||
cli.tritonapi.getInstanceRoleTags(opts.resourceId,
|
|
||||||
function (err, roleTags) {
|
|
||||||
if (err) {
|
|
||||||
cb(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.json) {
|
|
||||||
console.log(JSON.stringify(roleTags));
|
|
||||||
} else {
|
|
||||||
roleTags.forEach(function (r) {
|
|
||||||
console.log(r);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _addRoleTags(opts, cb) {
|
|
||||||
assert.object(opts.cli, 'opts.cli');
|
|
||||||
assert.string(opts.resourceId, 'opts.resourceId');
|
|
||||||
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
|
||||||
assert.func(cb, 'cb');
|
|
||||||
var cli = opts.cli;
|
|
||||||
var log = cli.log;
|
|
||||||
|
|
||||||
vasync.pipeline({arg: {}, funcs: [
|
|
||||||
function getCurrRoleTags(ctx, next) {
|
|
||||||
cli.tritonapi.getInstanceRoleTags(opts.resourceId,
|
|
||||||
function (err, roleTags, inst) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctx.roleTags = roleTags;
|
|
||||||
ctx.inst = inst;
|
|
||||||
log.trace({inst: inst, roleTags: roleTags}, 'curr role tags');
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
function addRoleTags(ctx, next) {
|
|
||||||
var adding = [];
|
|
||||||
for (var i = 0; i < opts.roleTags.length; i++) {
|
|
||||||
var r = opts.roleTags[i];
|
|
||||||
if (ctx.roleTags.indexOf(r) === -1) {
|
|
||||||
ctx.roleTags.push(r);
|
|
||||||
adding.push(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (adding.length === 0) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
console.log('Adding %d role tag%s (%s) to instance "%s"',
|
|
||||||
adding.length, adding.length === 1 ? '' : 's',
|
|
||||||
adding.join(', '), ctx.inst.name);
|
|
||||||
}
|
|
||||||
cli.tritonapi.cloudapi.setRoleTags({
|
|
||||||
resource: _resourceUrlFromId(
|
|
||||||
cli.tritonapi.profile.account, ctx.inst.id),
|
|
||||||
roleTags: ctx.roleTags
|
|
||||||
}, next);
|
|
||||||
}
|
|
||||||
]}, function (err) {
|
|
||||||
cb(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: resource URL should be in tritonapi.js,
|
|
||||||
// E.g. perhaps `TritonApi.setInstanceRoleTags`?
|
|
||||||
function _resourceUrlFromId(account, id) {
|
|
||||||
return format('/%s/machines/%s', account, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function _reprFromRoleTags(roleTags) {
|
function _reprFromRoleTags(roleTags) {
|
||||||
assert.arrayOfString(roleTags, 'roleTags');
|
assert.arrayOfString(roleTags, 'roleTags');
|
||||||
|
|
||||||
@ -146,14 +62,100 @@ function _roleTagsFromRepr(repr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function _listRoleTags(opts, cb) {
|
||||||
|
assert.object(opts.cli, 'opts.cli');
|
||||||
|
assert.string(opts.resourceType, 'opts.resourceType');
|
||||||
|
assert.string(opts.resourceId, 'opts.resourceId');
|
||||||
|
assert.optionalBool(opts.json, 'opts.json');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
var cli = opts.cli;
|
||||||
|
|
||||||
|
cli.tritonapi.getRoleTags({
|
||||||
|
resourceType: opts.resourceType,
|
||||||
|
id: opts.resourceId
|
||||||
|
}, function (err, roleTags) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
console.log(JSON.stringify(roleTags));
|
||||||
|
} else {
|
||||||
|
roleTags.forEach(function (r) {
|
||||||
|
console.log(r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _addRoleTags(opts, cb) {
|
||||||
|
assert.object(opts.cli, 'opts.cli');
|
||||||
|
assert.string(opts.resourceType, 'opts.resourceType');
|
||||||
|
assert.string(opts.resourceId, 'opts.resourceId');
|
||||||
|
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
var cli = opts.cli;
|
||||||
|
var log = cli.log;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
|
function getCurrRoleTags(ctx, next) {
|
||||||
|
cli.tritonapi.getRoleTags({
|
||||||
|
resourceType: opts.resourceType,
|
||||||
|
id: opts.resourceId
|
||||||
|
}, function (err, roleTags, resource) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.roleTags = roleTags;
|
||||||
|
ctx.resource = resource;
|
||||||
|
log.trace({resource: resource, roleTags: roleTags},
|
||||||
|
'curr role tags');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
function addRoleTags(ctx, next) {
|
||||||
|
var adding = [];
|
||||||
|
for (var i = 0; i < opts.roleTags.length; i++) {
|
||||||
|
var r = opts.roleTags[i];
|
||||||
|
if (ctx.roleTags.indexOf(r) === -1) {
|
||||||
|
ctx.roleTags.push(r);
|
||||||
|
adding.push(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (adding.length === 0) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log('Adding %d role tag%s (%s) to %s "%s"',
|
||||||
|
adding.length, adding.length === 1 ? '' : 's',
|
||||||
|
adding.join(', '), opts.resourceType,
|
||||||
|
opts.resourceId);
|
||||||
|
}
|
||||||
|
cli.tritonapi.setRoleTags({
|
||||||
|
resourceType: opts.resourceType,
|
||||||
|
id: (opts.resourceType === 'resource'
|
||||||
|
? opts.resourceId : ctx.resource.id),
|
||||||
|
roleTags: ctx.roleTags
|
||||||
|
}, next);
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function _editRoleTags(opts, cb) {
|
function _editRoleTags(opts, cb) {
|
||||||
assert.object(opts.cli, 'opts.cli');
|
assert.object(opts.cli, 'opts.cli');
|
||||||
|
assert.string(opts.resourceType, 'opts.resourceType');
|
||||||
assert.string(opts.resourceId, 'opts.resourceId');
|
assert.string(opts.resourceId, 'opts.resourceId');
|
||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
var cli = opts.cli;
|
var cli = opts.cli;
|
||||||
|
|
||||||
var account = cli.tritonapi.profile.account;
|
|
||||||
var id;
|
|
||||||
var roleTags;
|
var roleTags;
|
||||||
var filename;
|
var filename;
|
||||||
var origText;
|
var origText;
|
||||||
@ -197,8 +199,9 @@ function _editRoleTags(opts, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save changes.
|
// Save changes.
|
||||||
cli.tritonapi.cloudapi.setRoleTags({
|
cli.tritonapi.setRoleTags({
|
||||||
resource: _resourceUrlFromId(account, id),
|
resourceType: opts.resourceType,
|
||||||
|
id: opts.resourceId,
|
||||||
roleTags: edited
|
roleTags: edited
|
||||||
}, function (setErr) {
|
}, function (setErr) {
|
||||||
if (setErr) {
|
if (setErr) {
|
||||||
@ -215,18 +218,20 @@ function _editRoleTags(opts, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cli.tritonapi.getInstanceRoleTags(opts.resourceId,
|
cli.tritonapi.getRoleTags({
|
||||||
function (err, roleTags_, inst) {
|
resourceType: opts.resourceType,
|
||||||
|
id: opts.resourceId
|
||||||
|
}, function (err, roleTags_) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id = inst.id;
|
|
||||||
roleTags = roleTags_;
|
roleTags = roleTags_;
|
||||||
filename = format('%s-inst-%s-roleTags.txt',
|
filename = format('%s-%s-%s-roleTags.txt',
|
||||||
cli.tritonapi.profile.account,
|
cli.tritonapi.profile.account,
|
||||||
opts.resourceId);
|
opts.resourceType,
|
||||||
|
common.filenameSlug(opts.resourceId));
|
||||||
origText = _reprFromRoleTags(roleTags);
|
origText = _reprFromRoleTags(roleTags);
|
||||||
editAttempt(origText);
|
editAttempt(origText);
|
||||||
});
|
});
|
||||||
@ -235,6 +240,7 @@ function _editRoleTags(opts, cb) {
|
|||||||
|
|
||||||
function _setRoleTags(opts, cb) {
|
function _setRoleTags(opts, cb) {
|
||||||
assert.object(opts.cli, 'opts.cli');
|
assert.object(opts.cli, 'opts.cli');
|
||||||
|
assert.string(opts.resourceType, 'opts.resourceType');
|
||||||
assert.string(opts.resourceId, 'opts.resourceId');
|
assert.string(opts.resourceId, 'opts.resourceId');
|
||||||
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
||||||
assert.optionalBool(opts.yes, 'opts.yes');
|
assert.optionalBool(opts.yes, 'opts.yes');
|
||||||
@ -242,25 +248,12 @@ function _setRoleTags(opts, cb) {
|
|||||||
var cli = opts.cli;
|
var cli = opts.cli;
|
||||||
|
|
||||||
vasync.pipeline({arg: {}, funcs: [
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
// TODO: consider shorter path if the instance UUID is given
|
|
||||||
// (but what if the instance has a UUID for an *alias*)?
|
|
||||||
function getResource(ctx, next) {
|
|
||||||
cli.tritonapi.getInstance(opts.resourceId, function (err, inst) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctx.inst = inst;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
function confirm(ctx, next) {
|
function confirm(ctx, next) {
|
||||||
if (opts.yes) {
|
if (opts.yes) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
var msg = format('Set role tags on instance "%s"? [y/n] ',
|
var msg = format('Set role tags on %s "%s"? [y/n] ',
|
||||||
ctx.inst.name);
|
opts.resourceType, opts.resourceId);
|
||||||
common.promptYesNo({msg: msg}, function (answer) {
|
common.promptYesNo({msg: msg}, function (answer) {
|
||||||
if (answer !== 'y') {
|
if (answer !== 'y') {
|
||||||
console.error('Aborting');
|
console.error('Aborting');
|
||||||
@ -272,10 +265,11 @@ function _setRoleTags(opts, cb) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
function setThem(ctx, next) {
|
function setThem(ctx, next) {
|
||||||
console.log('Setting role tags on instance "%s"', ctx.inst.name);
|
console.log('Setting role tags on %s "%s"',
|
||||||
cli.tritonapi.cloudapi.setRoleTags({
|
opts.resourceType, opts.resourceId);
|
||||||
resource: _resourceUrlFromId(
|
cli.tritonapi.setRoleTags({
|
||||||
cli.tritonapi.profile.account, ctx.inst.id),
|
resourceType: opts.resourceType,
|
||||||
|
id: opts.resourceId,
|
||||||
roleTags: opts.roleTags
|
roleTags: opts.roleTags
|
||||||
}, next);
|
}, next);
|
||||||
}
|
}
|
||||||
@ -288,6 +282,7 @@ function _setRoleTags(opts, cb) {
|
|||||||
|
|
||||||
function _deleteRoleTags(opts, cb) {
|
function _deleteRoleTags(opts, cb) {
|
||||||
assert.object(opts.cli, 'opts.cli');
|
assert.object(opts.cli, 'opts.cli');
|
||||||
|
assert.string(opts.resourceType, 'opts.resourceType');
|
||||||
assert.string(opts.resourceId, 'opts.resourceId');
|
assert.string(opts.resourceId, 'opts.resourceId');
|
||||||
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
@ -296,15 +291,18 @@ function _deleteRoleTags(opts, cb) {
|
|||||||
|
|
||||||
vasync.pipeline({arg: {}, funcs: [
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
function getCurrRoleTags(ctx, next) {
|
function getCurrRoleTags(ctx, next) {
|
||||||
cli.tritonapi.getInstanceRoleTags(opts.resourceId,
|
cli.tritonapi.getRoleTags({
|
||||||
function (err, roleTags, inst) {
|
resourceType: opts.resourceType,
|
||||||
|
id: opts.resourceId
|
||||||
|
}, function (err, roleTags, resource) {
|
||||||
if (err) {
|
if (err) {
|
||||||
next(err);
|
next(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.roleTags = roleTags;
|
ctx.roleTags = roleTags;
|
||||||
ctx.inst = inst;
|
ctx.resource = resource;
|
||||||
log.trace({inst: inst, roleTags: roleTags}, 'curr role tags');
|
log.trace({resource: resource, roleTags: roleTags},
|
||||||
|
'curr role tags');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -328,9 +326,10 @@ function _deleteRoleTags(opts, cb) {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
var msg = format(
|
var msg = format(
|
||||||
'Delete %d role tag%s (%s) from instance "%s"? [y/n] ',
|
'Delete %d role tag%s (%s) from %s "%s"? [y/n] ',
|
||||||
ctx.toDelete.length, ctx.toDelete.length === 1 ? '' : 's',
|
ctx.toDelete.length, ctx.toDelete.length === 1 ? '' : 's',
|
||||||
ctx.toDelete.join(', '), ctx.inst.name);
|
ctx.toDelete.join(', '),
|
||||||
|
opts.resourceType, opts.resourceId);
|
||||||
common.promptYesNo({msg: msg}, function (answer) {
|
common.promptYesNo({msg: msg}, function (answer) {
|
||||||
if (answer !== 'y') {
|
if (answer !== 'y') {
|
||||||
console.error('Aborting');
|
console.error('Aborting');
|
||||||
@ -346,12 +345,14 @@ function _deleteRoleTags(opts, cb) {
|
|||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('Deleting %d role tag%s (%s) from instance "%s"',
|
console.log('Deleting %d role tag%s (%s) from %s "%s"',
|
||||||
ctx.toDelete.length, ctx.toDelete.length === 1 ? '' : 's',
|
ctx.toDelete.length, ctx.toDelete.length === 1 ? '' : 's',
|
||||||
ctx.toDelete.join(', '), ctx.inst.name);
|
ctx.toDelete.join(', '),
|
||||||
cli.tritonapi.cloudapi.setRoleTags({
|
opts.resourceType, opts.resourceId);
|
||||||
resource: _resourceUrlFromId(
|
cli.tritonapi.setRoleTags({
|
||||||
cli.tritonapi.profile.account, ctx.inst.id),
|
resourceType: opts.resourceType,
|
||||||
|
id: (opts.resourceType === 'resource'
|
||||||
|
? opts.resourceId : ctx.resource.id),
|
||||||
roleTags: ctx.roleTagsToKeep
|
roleTags: ctx.roleTagsToKeep
|
||||||
}, next);
|
}, next);
|
||||||
}
|
}
|
||||||
@ -363,30 +364,18 @@ function _deleteRoleTags(opts, cb) {
|
|||||||
|
|
||||||
function _deleteAllRoleTags(opts, cb) {
|
function _deleteAllRoleTags(opts, cb) {
|
||||||
assert.object(opts.cli, 'opts.cli');
|
assert.object(opts.cli, 'opts.cli');
|
||||||
|
assert.string(opts.resourceType, 'opts.resourceType');
|
||||||
assert.string(opts.resourceId, 'opts.resourceId');
|
assert.string(opts.resourceId, 'opts.resourceId');
|
||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
var cli = opts.cli;
|
var cli = opts.cli;
|
||||||
|
|
||||||
vasync.pipeline({arg: {}, funcs: [
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
// TODO: consider shorter path if the instance UUID is given
|
|
||||||
// (but what if the instance has a UUID for an *alias*)?
|
|
||||||
function getResource(ctx, next) {
|
|
||||||
cli.tritonapi.getInstance(opts.resourceId, function (err, inst) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctx.inst = inst;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
function confirm(ctx, next) {
|
function confirm(ctx, next) {
|
||||||
if (opts.yes) {
|
if (opts.yes) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
var msg = format('Delete all role tags from instance "%s"? [y/n] ',
|
var msg = format('Delete all role tags from %s "%s"? [y/n] ',
|
||||||
ctx.inst.name);
|
opts.resourceType, opts.resourceId);
|
||||||
common.promptYesNo({msg: msg}, function (answer) {
|
common.promptYesNo({msg: msg}, function (answer) {
|
||||||
if (answer !== 'y') {
|
if (answer !== 'y') {
|
||||||
console.error('Aborting');
|
console.error('Aborting');
|
||||||
@ -398,11 +387,11 @@ function _deleteAllRoleTags(opts, cb) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
function deleteAllRoleTags(ctx, next) {
|
function deleteAllRoleTags(ctx, next) {
|
||||||
console.log('Deleting all role tags from instance "%s"',
|
console.log('Deleting all role tags from %s "%s"',
|
||||||
ctx.inst.name);
|
opts.resourceType, opts.resourceId);
|
||||||
cli.tritonapi.cloudapi.setRoleTags({
|
cli.tritonapi.setRoleTags({
|
||||||
resource: _resourceUrlFromId(
|
resourceType: opts.resourceType,
|
||||||
cli.tritonapi.profile.account, ctx.inst.id),
|
id: opts.resourceId,
|
||||||
roleTags: []
|
roleTags: []
|
||||||
}, next);
|
}, next);
|
||||||
}
|
}
|
||||||
@ -426,9 +415,84 @@ function _roleTagsFromArrayOfString(arr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---- `triton rbac instance-role-tags`
|
// ---- `triton rbac role-tags RESOURCE-URL`
|
||||||
|
|
||||||
function do_instance_role_tags(subcmd, opts, args, cb) {
|
|
||||||
|
//do_role_tags.options = [
|
||||||
|
// {
|
||||||
|
// names: ['help', 'h'],
|
||||||
|
// type: 'bool',
|
||||||
|
// help: 'Show this help.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['json', 'j'],
|
||||||
|
// type: 'bool',
|
||||||
|
// help: 'JSON stream output.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['yes', 'y'],
|
||||||
|
// type: 'bool',
|
||||||
|
// help: 'Answer yes to confirmations, e.g. confirmation of deletion.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// group: 'Action Options'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['add', 'a'],
|
||||||
|
// type: 'arrayOfString',
|
||||||
|
// helpArg: 'ROLE[,ROLE...]',
|
||||||
|
// help: 'Add the given role tags. Can be specified multiple times.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['set', 's'],
|
||||||
|
// type: 'arrayOfString',
|
||||||
|
// helpArg: 'ROLE[,ROLE...]',
|
||||||
|
// help: 'Set role tags to the given value(s). Can be specified ' +
|
||||||
|
// 'multiple times.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['edit', 'e'],
|
||||||
|
// type: 'bool',
|
||||||
|
// help: 'Edit role tags in your $EDITOR.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['delete', 'd'],
|
||||||
|
// type: 'arrayOfString',
|
||||||
|
// helpArg: 'ROLE[,ROLE...]',
|
||||||
|
// help: 'Delete the given role tags. Can be specified multiple times.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// names: ['delete-all', 'D'],
|
||||||
|
// type: 'bool',
|
||||||
|
// help: 'Delete all role tags from the given resource.'
|
||||||
|
// }
|
||||||
|
//];
|
||||||
|
//do_role_tags.help = [
|
||||||
|
// /* BEGIN JSSTYLED */
|
||||||
|
// 'List and manage role tags for the given instance.',
|
||||||
|
// '',
|
||||||
|
// 'Usage:',
|
||||||
|
// ' {{name}} instance-role-tags INST # list role tags',
|
||||||
|
// ' {{name}} instance-role-tags -a ROLE[,ROLE...] INST # add',
|
||||||
|
// ' {{name}} instance-role-tags -s ROLE[,ROLE...] INST # set/replace',
|
||||||
|
// ' {{name}} instance-role-tags -e INST # edit in $EDITOR',
|
||||||
|
// ' {{name}} instance-role-tags -d ROLE[,ROLE...] INST # delete',
|
||||||
|
// ' {{name}} instance-role-tags -D INST # delete all',
|
||||||
|
// '',
|
||||||
|
// '{{options}}',
|
||||||
|
// 'Where "ROLE" is a role tag name (see `triton rbac roles`) and INST is',
|
||||||
|
// 'an instance "id", "name" or short id.'
|
||||||
|
// /* END JSSTYLED */
|
||||||
|
//].join('\n');
|
||||||
|
|
||||||
|
function makeRoleTagsFunc(cfg) {
|
||||||
|
assert.string(cfg.resourceType, 'cfg.resourceType');
|
||||||
|
assert.string(cfg.funcName, 'cfg.funcName');
|
||||||
|
assert.string(cfg.cmdName, 'cfg.cmdName');
|
||||||
|
assert.string(cfg.argName, 'cfg.argName');
|
||||||
|
assert.string(cfg.helpArgIs, 'cfg.helpArgIs');
|
||||||
|
|
||||||
|
var func = function (subcmd, opts, args, cb) {
|
||||||
if (opts.help) {
|
if (opts.help) {
|
||||||
this.do_help('help', {}, [subcmd], cb);
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
return;
|
return;
|
||||||
@ -453,7 +517,8 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
|
|
||||||
// Arg count validation.
|
// Arg count validation.
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
return cb(new errors.UsageError('INST argument is required'));
|
return cb(new errors.UsageError(cfg.argName +
|
||||||
|
' argument is required'));
|
||||||
} else if (args.length > 1) {
|
} else if (args.length > 1) {
|
||||||
return cb(new errors.UsageError('too many arguments'));
|
return cb(new errors.UsageError('too many arguments'));
|
||||||
}
|
}
|
||||||
@ -462,6 +527,7 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
case 'list':
|
case 'list':
|
||||||
_listRoleTags({
|
_listRoleTags({
|
||||||
cli: this.top,
|
cli: this.top,
|
||||||
|
resourceType: cfg.resourceType,
|
||||||
resourceId: args[0],
|
resourceId: args[0],
|
||||||
json: opts.json
|
json: opts.json
|
||||||
}, cb);
|
}, cb);
|
||||||
@ -469,6 +535,7 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
case 'add':
|
case 'add':
|
||||||
_addRoleTags({
|
_addRoleTags({
|
||||||
cli: this.top,
|
cli: this.top,
|
||||||
|
resourceType: cfg.resourceType,
|
||||||
resourceId: args[0],
|
resourceId: args[0],
|
||||||
roleTags: _roleTagsFromArrayOfString(opts.add)
|
roleTags: _roleTagsFromArrayOfString(opts.add)
|
||||||
}, cb);
|
}, cb);
|
||||||
@ -476,12 +543,14 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
case 'edit':
|
case 'edit':
|
||||||
_editRoleTags({
|
_editRoleTags({
|
||||||
cli: this.top,
|
cli: this.top,
|
||||||
|
resourceType: cfg.resourceType,
|
||||||
resourceId: args[0]
|
resourceId: args[0]
|
||||||
}, cb);
|
}, cb);
|
||||||
break;
|
break;
|
||||||
case 'set':
|
case 'set':
|
||||||
_setRoleTags({
|
_setRoleTags({
|
||||||
cli: this.top,
|
cli: this.top,
|
||||||
|
resourceType: cfg.resourceType,
|
||||||
resourceId: args[0],
|
resourceId: args[0],
|
||||||
roleTags: _roleTagsFromArrayOfString(opts.set),
|
roleTags: _roleTagsFromArrayOfString(opts.set),
|
||||||
yes: opts.yes
|
yes: opts.yes
|
||||||
@ -490,6 +559,7 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
case 'delete':
|
case 'delete':
|
||||||
_deleteRoleTags({
|
_deleteRoleTags({
|
||||||
cli: this.top,
|
cli: this.top,
|
||||||
|
resourceType: cfg.resourceType,
|
||||||
resourceId: args[0],
|
resourceId: args[0],
|
||||||
roleTags: _roleTagsFromArrayOfString(opts['delete']),
|
roleTags: _roleTagsFromArrayOfString(opts['delete']),
|
||||||
yes: opts.yes
|
yes: opts.yes
|
||||||
@ -498,6 +568,7 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
case 'deleteAll':
|
case 'deleteAll':
|
||||||
_deleteAllRoleTags({
|
_deleteAllRoleTags({
|
||||||
cli: this.top,
|
cli: this.top,
|
||||||
|
resourceType: cfg.resourceType,
|
||||||
resourceId: args[0],
|
resourceId: args[0],
|
||||||
yes: opts.yes
|
yes: opts.yes
|
||||||
}, cb);
|
}, cb);
|
||||||
@ -505,9 +576,11 @@ function do_instance_role_tags(subcmd, opts, args, cb) {
|
|||||||
default:
|
default:
|
||||||
return cb(new errors.InternalError('unknown action: ' + action));
|
return cb(new errors.InternalError('unknown action: ' + action));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
do_instance_role_tags.options = [
|
func.name = cfg.funcName;
|
||||||
|
|
||||||
|
func.options = [
|
||||||
{
|
{
|
||||||
names: ['help', 'h'],
|
names: ['help', 'h'],
|
||||||
type: 'bool',
|
type: 'bool',
|
||||||
@ -556,27 +629,56 @@ do_instance_role_tags.options = [
|
|||||||
help: 'Delete all role tags from the given resource.'
|
help: 'Delete all role tags from the given resource.'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
do_instance_role_tags.help = [
|
|
||||||
|
func.helpOpts = {
|
||||||
|
helpCol: 23
|
||||||
|
};
|
||||||
|
|
||||||
|
func.help = [
|
||||||
/* BEGIN JSSTYLED */
|
/* BEGIN JSSTYLED */
|
||||||
'List and manage role tags for the given instance.',
|
'List and manage role tags for the given $resourceType.',
|
||||||
'',
|
'',
|
||||||
'Usage:',
|
'Usage:',
|
||||||
' {{name}} instance-role-tags INST # list role tags',
|
' {{name}} $cmdName $argName # list role tags',
|
||||||
' {{name}} instance-role-tags -a ROLE[,ROLE...] INST # add',
|
' {{name}} $cmdName -a ROLE[,ROLE...] $argName # add',
|
||||||
' {{name}} instance-role-tags -s ROLE[,ROLE...] INST # set/replace',
|
' {{name}} $cmdName -s ROLE[,ROLE...] $argName # set/replace',
|
||||||
' {{name}} instance-role-tags -e INST # edit in $EDITOR',
|
' {{name}} $cmdName -e $argName # edit in $EDITOR',
|
||||||
' {{name}} instance-role-tags -d ROLE[,ROLE...] INST # delete',
|
' {{name}} $cmdName -d ROLE[,ROLE...] $argName # delete',
|
||||||
' {{name}} instance-role-tags -D INST # delete all',
|
' {{name}} $cmdName -D $argName # delete all',
|
||||||
'',
|
'',
|
||||||
'{{options}}',
|
'{{options}}',
|
||||||
'Where "ROLE" is a role tag name (see `triton rbac roles`) and INST is',
|
'Where "ROLE" is a role tag name (see `triton rbac roles`) and',
|
||||||
'an instance "id", "name" or short id.'
|
'$argName is $helpArgIs.'
|
||||||
/* END JSSTYLED */
|
/* END JSSTYLED */
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
['resourceType', 'cmdName', 'argName', 'helpArgIs'].forEach(function (key) {
|
||||||
|
func.help = func.help.replace(new RegExp('\\$' + key, 'g'), cfg[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var do_role_tags = makeRoleTagsFunc({
|
||||||
|
resourceType: 'resource',
|
||||||
|
funcName: 'do_role_tags',
|
||||||
|
cmdName: 'role-tags',
|
||||||
|
argName: 'RESOURCE-URL',
|
||||||
|
helpArgIs: 'an RBAC resource URL'
|
||||||
|
});
|
||||||
|
|
||||||
|
var do_instance_role_tags = makeRoleTagsFunc({
|
||||||
|
resourceType: 'instance',
|
||||||
|
funcName: 'do_instance_role_tags',
|
||||||
|
cmdName: 'instance-role-tags',
|
||||||
|
argName: 'INST',
|
||||||
|
helpArgIs: 'an instance "id", "name" or short id'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
//do_role_tags: do_role_tags,
|
do_role_tags: do_role_tags,
|
||||||
do_instance_role_tags: do_instance_role_tags
|
do_instance_role_tags: do_instance_role_tags
|
||||||
};
|
};
|
||||||
|
@ -42,7 +42,8 @@ function RbacCLI(top) {
|
|||||||
'roles',
|
'roles',
|
||||||
'role',
|
'role',
|
||||||
{ group: 'Role Tags' },
|
{ group: 'Role Tags' },
|
||||||
'instance-role-tags'
|
'instance-role-tags',
|
||||||
|
'role-tags'
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -67,7 +68,7 @@ RbacCLI.prototype.do_roles = require('./do_roles');
|
|||||||
RbacCLI.prototype.do_role = require('./do_role');
|
RbacCLI.prototype.do_role = require('./do_role');
|
||||||
|
|
||||||
var doRoleTags = require('./do_role_tags');
|
var doRoleTags = require('./do_role_tags');
|
||||||
//RbacCLI.prototype.do_role_tags = doRoleTags.do_role_tags;
|
RbacCLI.prototype.do_role_tags = doRoleTags.do_role_tags;
|
||||||
RbacCLI.prototype.do_instance_role_tags = doRoleTags.do_instance_role_tags;
|
RbacCLI.prototype.do_instance_role_tags = doRoleTags.do_instance_role_tags;
|
||||||
|
|
||||||
module.exports = RbacCLI;
|
module.exports = RbacCLI;
|
||||||
|
215
lib/tritonapi.js
215
lib/tritonapi.js
@ -32,6 +32,30 @@ var loadConfigSync = require('./config').loadConfigSync;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---- internal support stuff
|
||||||
|
|
||||||
|
function _assertRoleTagResourceType(resourceType, errName) {
|
||||||
|
assert.string(resourceType, errName);
|
||||||
|
var knownResourceTypes = ['resource', 'instance', 'image',
|
||||||
|
'package', 'network'];
|
||||||
|
assert.ok(knownResourceTypes.indexOf(resourceType) !== -1,
|
||||||
|
'unknown resource type: ' + resourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _roleTagResourceUrl(account, type, id) {
|
||||||
|
var ns = {
|
||||||
|
instance: 'machines',
|
||||||
|
image: 'images',
|
||||||
|
'package': 'packages',
|
||||||
|
network: 'networks'
|
||||||
|
}[type];
|
||||||
|
assert.ok(ns, 'unknown resource type: ' + type);
|
||||||
|
|
||||||
|
return format('/%s/%s/%s', account, ns, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---- TritonApi class
|
//---- TritonApi class
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +90,7 @@ function TritonApi(opts) {
|
|||||||
if (this.config.cacheDir) {
|
if (this.config.cacheDir) {
|
||||||
this.cacheDir = path.resolve(this.config._configDir,
|
this.cacheDir = path.resolve(this.config._configDir,
|
||||||
this.config.cacheDir,
|
this.config.cacheDir,
|
||||||
common.slug(this.profile));
|
common.profileSlug(this.profile));
|
||||||
this.log.trace({cacheDir: this.cacheDir}, 'cache dir');
|
this.log.trace({cacheDir: this.cacheDir}, 'cache dir');
|
||||||
// TODO perhaps move this to an async .init()
|
// TODO perhaps move this to an async .init()
|
||||||
if (!fs.existsSync(this.cacheDir)) {
|
if (!fs.existsSync(this.cacheDir)) {
|
||||||
@ -593,58 +617,163 @@ TritonApi.prototype.getInstance = function getInstance(name, cb) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get instance role tags.
|
* Get role tags for a resource.
|
||||||
*
|
*
|
||||||
* @param {String} name The instance id, name, or short id.
|
* @param {Object} opts
|
||||||
* @param {Function} callback `function (err, roleTags, inst)`
|
* - resourceType {String} One of:
|
||||||
|
* resource (a raw RBAC resource URL)
|
||||||
|
* instance
|
||||||
|
* image
|
||||||
|
* package
|
||||||
|
* network
|
||||||
|
* - id {String} The resource identifier. E.g. for an instance this can be
|
||||||
|
* the ID (a UUID), login or short id. Whatever `triton` typically allows
|
||||||
|
* for identification.
|
||||||
|
* @param {Function} callback `function (err, roleTags, resource)`
|
||||||
*/
|
*/
|
||||||
TritonApi.prototype.getInstanceRoleTags =
|
TritonApi.prototype.getRoleTags = function getRoleTags(opts, cb) {
|
||||||
function getInstanceRoleTags(name, cb) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
assert.string(name, 'name');
|
assert.object(opts, 'opts');
|
||||||
|
_assertRoleTagResourceType(opts.resourceType, 'opts.resourceType');
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
var roleTags;
|
function roleTagsFromRes(res) {
|
||||||
var inst;
|
return (
|
||||||
|
(res.headers['role-tag'] || '')
|
||||||
vasync.pipeline({arg: {}, funcs: [
|
|
||||||
function getInst(ctx, next) {
|
|
||||||
self.getInstance(name, function (err, inst_, res) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inst = inst_;
|
|
||||||
ctx.res = res;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function getMachineIfNecessary(ctx, next) {
|
|
||||||
// Sometimes `getInstance` returns a CloudAPI `GetMachine` res on
|
|
||||||
// which there is a 'role-tag' header that we want. Sometimes not.
|
|
||||||
if (ctx.res) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cloudapi.getMachine(inst.id, function (err, inst_, res) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctx.res = res;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function getRolesFromRes(ctx, next) {
|
|
||||||
roleTags = (ctx.res.headers['role-tag'] || '')
|
|
||||||
/* JSSTYLED */
|
/* JSSTYLED */
|
||||||
.split(/\s*,\s*/)
|
.split(/\s*,\s*/)
|
||||||
.filter(function (r) { return r.trim(); });
|
.filter(function (r) { return r.trim(); })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var roleTags;
|
||||||
|
var resource;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
|
function resolveResourceId(ctx, next) {
|
||||||
|
if (opts.resourceType === 'resource') {
|
||||||
next();
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var getFuncName = {
|
||||||
|
instance: 'getInstance',
|
||||||
|
image: 'getImage',
|
||||||
|
'package': 'getPackage',
|
||||||
|
network: 'getNetwork'
|
||||||
|
}[opts.resourceType];
|
||||||
|
self[getFuncName](opts.id, function (err, resource_, res) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resource = resource_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sometimes `getInstance` et al return a CloudAPI `GetMachine`
|
||||||
|
* res on which there is a 'role-tag' header that we want.
|
||||||
|
*/
|
||||||
|
if (res) {
|
||||||
|
roleTags = roleTagsFromRes(res);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function getResourceIfNecessary(ctx, next) {
|
||||||
|
if (roleTags) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceUrl = (opts.resourceType === 'resource'
|
||||||
|
? opts.id
|
||||||
|
: _roleTagResourceUrl(self.profile.account,
|
||||||
|
opts.resourceType, resource.id));
|
||||||
|
self.cloudapi.getRoleTags({resource: resourceUrl},
|
||||||
|
function (err, roleTags_, resource_) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
roleTags = roleTags_;
|
||||||
|
resource = resource_;
|
||||||
|
next();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
]}, function (err) {
|
]}, function (err) {
|
||||||
cb(err, roleTags, inst);
|
cb(err, roleTags, resource);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set role tags for a resource.
|
||||||
|
*
|
||||||
|
* @param {Object} opts
|
||||||
|
* - resourceType {String} One of:
|
||||||
|
* resource (a raw RBAC resource URL)
|
||||||
|
* instance
|
||||||
|
* image
|
||||||
|
* package
|
||||||
|
* network
|
||||||
|
* - id {String} The resource identifier. E.g. for an instance this can be
|
||||||
|
* the ID (a UUID), login or short id. Whatever `triton` typically allows
|
||||||
|
* for identification.
|
||||||
|
* - roleTags {Array}
|
||||||
|
* @param {Function} callback `function (err)`
|
||||||
|
*/
|
||||||
|
TritonApi.prototype.setRoleTags = function setRoleTags(opts, cb) {
|
||||||
|
var self = this;
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
_assertRoleTagResourceType(opts.resourceType, 'opts.resourceType');
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
|
assert.arrayOfString(opts.roleTags, 'opts.roleTags');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
|
function resolveResourceId(ctx, next) {
|
||||||
|
if (opts.resourceType === 'resource') {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var getFuncName = {
|
||||||
|
instance: 'getInstance',
|
||||||
|
image: 'getImage',
|
||||||
|
'package': 'getPackage',
|
||||||
|
network: 'getNetwork'
|
||||||
|
}[opts.resourceType];
|
||||||
|
self[getFuncName](opts.id, function (err, resource, res) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.resource = resource;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
function setTheRoleTags(ctx, next) {
|
||||||
|
var resourceUrl = (opts.resourceType === 'resource'
|
||||||
|
? opts.id
|
||||||
|
: _roleTagResourceUrl(self.profile.account,
|
||||||
|
opts.resourceType, ctx.resource.id));
|
||||||
|
self.cloudapi.setRoleTags({
|
||||||
|
resource: resourceUrl,
|
||||||
|
roleTags: opts.roleTags
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
cb(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user