Skip to content

Commit

Permalink
PUBAPI-1420 Add ability to mount NFS volumes with CreateMachine endpoint
Browse files Browse the repository at this point in the history
Reviewed by: Trent Mick <trentm@gmail.com>
Approved by: Trent Mick <trentm@gmail.com>
  • Loading branch information
joshwilsdon committed Sep 22, 2017
1 parent 8438f44 commit 45ed888
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Known issues:
## not yet released

- [joyent/node-triton#226] added new `triton volume sizes` subcommand.
- [PUBAPI-1420] added support for mounting volumes in LX and SmartOS instances.
E.g., `triton instance create --volume VOLUME ...`.

## 5.3.1

Expand Down
111 changes: 110 additions & 1 deletion lib/do_instance/do_create.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

/*
* Copyright 2016 Joyent, Inc.
* Copyright 2017 Joyent, Inc.
*
* `triton instance create ...`
*/
Expand All @@ -20,6 +20,62 @@ var distractions = require('../distractions');
var errors = require('../errors');
var mat = require('../metadataandtags');

function parseVolMount(volume) {
var components;
var volMode;
var volMountpoint;
var volName;
var VALID_MODES = ['ro', 'rw'];
var VALID_VOLUME_NAME_REGEXP = /^[a-zA-Z0-9][a-zA-Z0-9_\.\-]+$/;

assert.string(volume, 'volume');

components = volume.split(':');
if (components.length !== 2 && components.length !== 3) {
return new errors.UsageError('invalid volume specified, must be in ' +
'the form "<volume name>:<mount path>[:<mode>]", got: "' + volume +
'"');
}

volName = components[0];
volMountpoint = components[1];
volMode = components[2];

// first component should be a volume name. We only check here that it
// syntactically looks like a volume name, we'll leave the upstream to
// determine if it's not actually a volume.
if (!VALID_VOLUME_NAME_REGEXP.test(volName)) {
return new errors.UsageError('invalid volume name, got: "' + volume +
'"');
}

// second component should be an absolute path
// NOTE: if we ever move past node 0.10, we could use path.isAbsolute(path)
if (volMountpoint.length === 0 || volMountpoint[0] !== '/') {
return new errors.UsageError('invalid volume mountpoint, must be ' +
'absolute path, got: "' + volume + '"');
}
if (volMountpoint.indexOf('\0') !== -1) {
return new errors.UsageError('invalid volume mountpoint, contains ' +
'invalid characters, got: "' + volume + '"');
}
if (volMountpoint.search(/[^\/]/) === -1) {
return new errors.UsageError('invalid volume mountpoint, must contain' +
' at least one non-/ character, got: "' + volume + '"');
}

// third component is optional mode: 'ro' or 'rw'
if (components.length === 3 && VALID_MODES.indexOf(volMode) === -1) {
return new errors.UsageError('invalid volume mode, got: "' + volume +
'"');
}

return {
mode: volMode || 'rw',
mountpoint: volMountpoint,
name: volName
};
}

function do_create(subcmd, opts, args, cb) {
if (opts.help) {
Expand Down Expand Up @@ -132,6 +188,42 @@ function do_create(subcmd, opts, args, cb) {
next();
},

/*
* Make sure if volumes were passed, they're in the correct form.
*/
function parseVolMounts(ctx, next) {
var idx;
var validationErrs = [];
var parsedObj;
var volMounts = [];

if (!opts.volume) {
next();
return;
}

for (idx = 0; idx < opts.volume.length; idx++) {
parsedObj = parseVolMount(opts.volume[idx]);
if (parsedObj instanceof Error) {
validationErrs.push(parsedObj);
} else {
// if it's not an error, it's a volume
volMounts.push(parsedObj);
}
}

if (validationErrs.length > 0) {
next(new errors.MultiError(validationErrs));
return;
}

if (volMounts.length > 0) {
ctx.volMounts = volMounts;
}

next();
},

/*
* Determine `ctx.locality` according to what CloudAPI supports
* based on `ctx.affinities` parsed earlier.
Expand Down Expand Up @@ -274,13 +366,19 @@ function do_create(subcmd, opts, args, cb) {
},

function createInst(ctx, next) {
assert.optionalArrayOfObject(ctx.volMounts, 'ctx.volMounts');

var createOpts = {
name: opts.name,
image: ctx.img.id,
'package': ctx.pkg && ctx.pkg.id,
networks: ctx.nets && ctx.nets.map(
function (net) { return net.id; })
};

if (ctx.volMounts) {
createOpts.volumes = ctx.volMounts;
}
if (ctx.locality) {
createOpts.locality = ctx.locality;
}
Expand Down Expand Up @@ -446,6 +544,17 @@ do_create.options = [
help: 'Enable Cloud Firewall on this instance. See ' +
'<https://docs.joyent.com/public-cloud/network/firewall>'
},
{
names: ['volume', 'v'],
type: 'arrayOfString',
help: 'Mount a volume into the instance (non-KVM only). VOLMOUNT is ' +
'"<volume-name:/mount/point>[:access-mode]" where access mode is ' +
'one of "ro" for read-only or "rw" for read-write (default). For ' +
'example: "-v myvolume:/mnt:ro" to mount "myvolume" read-only on ' +
'/mnt in this instance.',
helpArg: 'VOLMOUNT',
hidden: true
},

{
group: ''
Expand Down

0 comments on commit 45ed888

Please sign in to comment.