diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index e322bab0d..f888f16f9 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -3320,13 +3320,12 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; - Client.prototype.addVolume = function (name, mountType, hostPath, mountOptions, callback) { + Client.prototype.addVolume = function (name, mountType, mountOptions, callback) { var data = { name: name, mountType: mountType, mountOptions: mountOptions }; - if (hostPath) data.hostPath = hostPath; post('/api/v1/volumes', data, null, function (error, data, status) { if (error) return callback(error); diff --git a/dashboard/src/views/volumes.html b/dashboard/src/views/volumes.html index 1723c6ed0..c1914828b 100644 --- a/dashboard/src/views/volumes.html +++ b/dashboard/src/views/volumes.html @@ -12,7 +12,7 @@
- +
@@ -26,23 +26,23 @@
- +
- +
- +
- +
@@ -53,32 +53,32 @@
- +
- +
- +
- +
- +
- +
diff --git a/dashboard/src/views/volumes.js b/dashboard/src/views/volumes.js index 5738dda07..2cab4e723 100644 --- a/dashboard/src/views/volumes.js +++ b/dashboard/src/views/volumes.js @@ -88,7 +88,8 @@ angular.module('Application').controller('VolumesController', ['$scope', '$locat remoteDir: '', username: '', password: '', - diskPath: '', + diskPath: {}, // { path, type } + customDiskPath: '', user: '', seal: false, port: 22, @@ -104,7 +105,7 @@ angular.module('Application').controller('VolumesController', ['$scope', '$locat $scope.volumeAdd.remoteDir = ''; $scope.volumeAdd.username = ''; $scope.volumeAdd.password = ''; - $scope.volumeAdd.diskPath = ''; + $scope.volumeAdd.diskPath = {}; $scope.volumeAdd.customDiskPath = ''; $scope.volumeAdd.user = ''; $scope.volumeAdd.seal = false; @@ -130,7 +131,7 @@ angular.module('Application').controller('VolumesController', ['$scope', '$locat result.forEach(function (d) { d.label = d.path; }); // add custom fake option - result.push({ path: 'custom', label: 'Custom' }); + result.push({ path: 'custom', label: 'Custom Path' }); $scope.blockDevices = result; $scope.volumeAdd.diskPath = $scope.blockDevices[0]; @@ -170,16 +171,13 @@ angular.module('Application').controller('VolumesController', ['$scope', '$locat mountOptions = { diskPath: $scope.volumeAdd.diskPath === 'custom' ? $scope.volumeAdd.customDiskPath : $scope.volumeAdd.diskPath }; + } else if ($scope.volumeAdd.mountType === 'mountpoint' || $scope.volumeAdd.mountType === 'filesystem') { + mountOptions = { + hostPath: $scope.volumeAdd.hostPath + }; } - var hostPath; - if ($scope.volumeAdd.mountType === 'mountpoint' || $scope.volumeAdd.mountType === 'filesystem') { - hostPath = $scope.volumeAdd.hostPath; - } else { - hostPath = null; - } - - Client.addVolume($scope.volumeAdd.name, $scope.volumeAdd.mountType, hostPath, mountOptions, function (error) { + Client.addVolume($scope.volumeAdd.name, $scope.volumeAdd.mountType, mountOptions, function (error) { $scope.volumeAdd.busy = false; if (error) { $scope.volumeAdd.error = error.message; diff --git a/migrations/schema.sql b/migrations/schema.sql index 394079df6..a49a8828a 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -280,7 +280,7 @@ CREATE TABLE IF NOT EXISTS appPasswords( CREATE TABLE IF NOT EXISTS volumes( id VARCHAR(128) NOT NULL UNIQUE, name VARCHAR(256) NOT NULL UNIQUE, - hostPath VARCHAR(1024) NOT NULL UNIQUE, + hostPath VARCHAR(1024) NOT NULL UNIQUE, // computed hostPath creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, mountType VARCHAR(16) DEFAULT "noop", mountOptionsJson TEXT, diff --git a/src/mounts.js b/src/mounts.js index f849b9a06..c32fd000d 100644 --- a/src/mounts.js +++ b/src/mounts.js @@ -34,6 +34,7 @@ function validateMountOptions(type, options) { switch (type) { case 'filesystem': case 'mountpoint': + if (typeof options.hostPath !== 'string') return new BoxError(BoxError.BAD_FIELD, 'hostPath is not a string'); return null; case 'cifs': if (typeof options.username !== 'string') return new BoxError(BoxError.BAD_FIELD, 'username is not a string'); diff --git a/src/routes/volumes.js b/src/routes/volumes.js index 4833a54da..f271ee3fd 100644 --- a/src/routes/volumes.js +++ b/src/routes/volumes.js @@ -33,9 +33,7 @@ async function add(req, res, next) { if (typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be a string')); if (typeof req.body.mountType !== 'string') return next(new HttpError(400, 'mountType must be a string')); - if (typeof req.body.mountOptions !== 'object') return next(new HttpError(400, 'mountOptions must be a object')); - - if ('hostPath' in req.body && typeof req.body.hostPath !== 'string') return next(new HttpError(400, 'hostPath must be a string')); + if (!req.body.mountOptions || typeof req.body.mountOptions !== 'object') return next(new HttpError(400, 'mountOptions must be a non-null object')); req.clearTimeout(); // waiting for mount can take time diff --git a/src/volumes.js b/src/volumes.js index b6130a151..459737e5f 100644 --- a/src/volumes.js +++ b/src/volumes.js @@ -81,21 +81,24 @@ async function add(volume, auditSource) { const id = uuid.v4().replace(/-/g, ''); // to make systemd mount file names more readable + let hostPath; if (mountType === 'mountpoint' || mountType === 'filesystem') { - error = validateHostPath(volume.hostPath, mountType); + error = validateHostPath(mountOptions.hostPath, mountType); if (error) throw error; + hostPath = mountOptions.hostPath; } else { - volume.hostPath = path.join(paths.VOLUMES_MOUNT_DIR, id); - await mounts.tryAddMount(volume, { timeout: 10 }); // 10 seconds + hostPath = path.join(paths.VOLUMES_MOUNT_DIR, id); + const mount = { name, hostPath, mountType, mountOptions }; + await mounts.tryAddMount(mount, { timeout: 10 }); // 10 seconds } - [error] = await safe(database.query('INSERT INTO volumes (id, name, hostPath, mountType, mountOptionsJson) VALUES (?, ?, ?, ?, ?)', [ id, name, volume.hostPath, mountType, JSON.stringify(mountOptions) ])); + [error] = await safe(database.query('INSERT INTO volumes (id, name, hostPath, mountType, mountOptionsJson) VALUES (?, ?, ?, ?, ?)', [ id, name, hostPath, mountType, JSON.stringify(mountOptions) ])); if (error && error.code === 'ER_DUP_ENTRY' && error.sqlMessage.indexOf('name') !== -1) throw new BoxError(BoxError.ALREADY_EXISTS, 'name already exists'); if (error && error.code === 'ER_DUP_ENTRY' && error.sqlMessage.indexOf('hostPath') !== -1) throw new BoxError(BoxError.ALREADY_EXISTS, 'hostPath already exists'); if (error && error.code === 'ER_DUP_ENTRY' && error.sqlMessage.indexOf('PRIMARY') !== -1) throw new BoxError(BoxError.ALREADY_EXISTS, 'id already exists'); if (error) throw error; - await eventlog.add(eventlog.ACTION_VOLUME_ADD, auditSource, { id, name, hostPath: volume.hostPath }); + await eventlog.add(eventlog.ACTION_VOLUME_ADD, auditSource, { id, name, hostPath }); // in theory, we only need to do this mountpoint volumes. but for some reason a restart is required to detect new "mounts" safe(services.rebuildService('sftp', auditSource), { debug });