@nasriya/orchestriq
Version:
A package to generate Docker files
383 lines (382 loc) • 19.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const helpers_1 = __importDefault(require("../../../../../utils/helpers"));
class StackVolume {
#_container;
#_data = { name: '' };
constructor(options, container) {
if (container) {
this.#_container = container;
}
if (typeof options === 'object' && Object.keys(options).length > 0) {
if (helpers_1.default.hasOwnProperty(options, 'name')) {
this.#_data.name = options.name;
}
else {
throw new Error('A volume name must be provided.');
}
if (helpers_1.default.hasOwnProperty(options, 'driver')) {
this.#_data.driver = options.driver;
}
if (helpers_1.default.hasOwnProperty(options, 'driverOpts')) {
this.#_data.driverOpts = options.driverOpts;
}
if (helpers_1.default.hasOwnProperty(options, 'external')) {
this.#_data.external = options.external;
}
if (helpers_1.default.hasOwnProperty(options, 'labels')) {
this.#_data.labels = options.labels;
}
if (helpers_1.default.hasOwnProperty(options, 'scope')) {
this.#_data.scope = options.scope;
}
if (helpers_1.default.hasOwnProperty(options, 'accessMode')) {
this.#_data.accessMode = options.accessMode;
}
if (helpers_1.default.hasOwnProperty(options, 'tmpfs')) {
this.#_data.tmpfs = options.tmpfs;
}
if (helpers_1.default.hasOwnProperty(options, 'size')) {
this.#_data.size = options.size;
}
}
else {
throw new TypeError('options must be an object.');
}
}
/**
* Converts the volume to a JSON object.
* @param type The type of the JSON object to be returned. The default value is 'regular'.
* @returns A JSON object representing the volume.
*/
toJSON(type = 'regular') {
if (type === 'regular') {
return helpers_1.default.deepClone(this.#_data);
}
else {
const json = { Name: this.name };
if (this.driver) {
json.Driver = this.driver;
}
if (this.driverOpts && Object.keys(this.driverOpts).length > 0) {
json.DriverOpts = helpers_1.default.deepClone(this.driverOpts);
}
if (this.labels && Object.keys(this.labels).length > 0) {
json.Labels = { ...this.labels };
}
return json;
}
}
/**
* Returns the name of the volume.
* @returns {string} The name of the volume.
*/
get name() { return this.#_data.name; }
/**
* Sets the name of the volume.
* The name must be a string and unique among all volumes in the container.
* If the name is already set, this method will throw an error.
* @param value A string representing the name of the volume.
* @throws {TypeError} If the provided value is not a string.
* @throws {Error} If the name is already set or if a volume with the same name already exists.
*/
set name(value) {
if (typeof value !== 'string') {
throw new TypeError("The volume's 'name' property must be a string.");
}
if (value.length === 0) {
throw new RangeError("The volume's 'name' property must not be empty.");
}
if (this.#_container) {
if (value in this.#_container.volumes.list) {
throw new Error(`Volume with name "${value}" already exists.`);
}
}
this.#_data.name = value;
}
/**
* Returns the volume driver.
*
* @returns {VolumeDriver | undefined} The volume driver, or undefined if no driver is set.
*/
get driver() { return this.#_data.driver; }
/**
* Sets the volume driver.
* The volume driver determines how the volume is handled.
* If not set, the default driver is used.
* @param value A string representing the volume driver.
* @throws {TypeError} If the provided driver is not a valid string.
* @throws {Error} If the volume driver is already set.
*/
set driver(value) {
if (typeof value !== 'string') {
throw new TypeError("The volume's 'driver' property (when defined) must be a string.");
}
if (value.length === 0) {
throw new RangeError("The volume's 'driver' property must not be empty.");
}
this.#_data.driver = value;
}
/**
* Returns the driver options for the volume.
* The driver options provide additional configuration specific to the volume driver.
* If not set, the default driver options are used.
* @returns {VolumeDriverOptionsUnion | undefined} An object representing the driver options, or undefined if no options are set.
*/
get driverOpts() { return this.#_data.driverOpts; }
/**
* Sets the driver options for the volume.
* The driver options provide additional configuration specific to the volume driver.
*
* @param value An object representing the driver options, which must include:
* - `type`: A non-empty string indicating the driver type. Valid types include 'local', 'nfs', 'tmpfs', 'azurefile', 'rexray', 'glusterfs', 'ceph', and 'digitalocean'.
* Additional properties depend on the driver `type`:
* - For 'local': May include `device` (string).
* - For 'nfs': Must include `device` (string), may include `nfsVersion` ('4').
* - For 'tmpfs': May include `device`, `size`, `mode` (strings).
* - For 'azurefile': Must include `shareName`, `storageAccountName` (strings), may include `device`.
* - For 'rexray': Must include `volumeID` (string), may include `device`, `fsType` (string), `iops` (number), `mountOptions` (array of strings).
* - For 'glusterfs': Must include `device` (string).
* - For 'ceph': Must include `device`, `secret`, `fsid` (strings).
* - For 'digitalocean': Must include `access` ('readwrite' | 'readonly'), `size` (number > 0), `region` (string), may include `device`.
* Common properties for any type may include:
* - `mountpoint`: A string representing the mount point.
* - `o`: A string or array of strings representing mount options.
*
* @throws {TypeError} If `value` is not an object or if any property has an incorrect type.
* @throws {SyntaxError} If required properties are missing or have invalid values.
*/
set driverOpts(value) {
if (!(typeof value === 'object' && Object.keys(value).length > 0)) {
throw new TypeError("The volume's 'driverOpts' property (when defined) must be an object.");
}
if (!('type' in value) || typeof value.type !== 'string' || value.type.length === 0) {
throw new SyntaxError("The volume's 'driverOpts' property must include a 'type' property of type non-empty string.");
}
const driverTypes = ['local', 'nfs', 'tmpfs', 'azurefile', 'rexray', 'glusterfs', 'ceph', 'digitalocean'];
if (!driverTypes.includes(value.type)) {
throw new SyntaxError(`The volume's 'driverOpts' property must have a 'type' property from: ${driverTypes.join(', ')}.`);
}
// Validation functions
const validateProperty = (prop, type, isRequired = false, additionalCheck, errorMessage) => {
const objectValue = value;
if (!(prop in objectValue)) {
if (isRequired)
throw new SyntaxError(`The volume's 'driverOpts' must include the '${prop}' property.`);
return;
}
const propValue = objectValue[prop];
if (typeof propValue !== type) {
throw new TypeError(`The '${prop}' property must be of type ${type}.`);
}
if (additionalCheck && !additionalCheck(propValue)) {
throw new SyntaxError(errorMessage || `The '${prop}' property is invalid.`);
}
};
const typeValidators = {
local: () => validateProperty('device', 'string'),
nfs: () => {
validateProperty('device', 'string', true);
validateProperty('nfsVersion', 'string', false, v => v === '4', "The 'nfsVersion' property must be '4'.");
},
tmpfs: () => {
validateProperty('device', 'string');
validateProperty('size', 'string');
validateProperty('mode', 'string');
},
azurefile: () => {
validateProperty('device', 'string');
validateProperty('shareName', 'string', true);
validateProperty('storageAccountName', 'string', true);
},
rexray: () => {
validateProperty('device', 'string');
validateProperty('volumeID', 'string', true);
validateProperty('fsType', 'string');
validateProperty('iops', 'number');
validateProperty('mountOptions', 'object', false, v => Array.isArray(v) && v.every(i => typeof i === 'string'), "The 'mountOptions' property must be an array of strings.");
},
glusterfs: () => validateProperty('device', 'string', true),
ceph: () => {
validateProperty('device', 'string', true);
validateProperty('secret', 'string', true);
validateProperty('fsid', 'string', true);
},
digitalocean: () => {
validateProperty('device', 'string');
validateProperty('access', 'string', true, v => ['readwrite', 'readonly'].includes(v), "The 'access' property must be 'readwrite' or 'readonly'.");
validateProperty('size', 'number', true, v => v > 0, "The 'size' property must be greater than 0.");
validateProperty('region', 'string', true);
},
};
// Common validators
validateProperty('mountpoint', 'string');
validateProperty('o', 'object', false, v => Array.isArray(v) || typeof v === 'string', "The 'o' property must be a string or an array of strings.");
// Apply type-specific validation
typeValidators[value.type]();
this.#_data.driverOpts = value;
}
/**
* Returns whether the volume is external.
* If not set, the default value is undefined.
* If set to a boolean, the volume is marked as external or not.
* If set to an object, the volume is marked as external and uses the object's 'name' property as the external volume name.
* @returns {boolean | { name: string } | undefined} A boolean indicating whether the volume is external, or an object with a 'name' property if set to an object.
*/
get external() { return this.#_data.external; }
/**
* Sets whether the volume is external.
* If the volume is external, it will not be created on the host and will not be accessible from outside the container.
* If not set, the default value is undefined.
* If set to a boolean, the volume is marked as external or not.
* If set to an object, the volume is marked as external and uses the object's 'name' property as the external volume name.
* @param value A boolean indicating whether the volume is external, or an object with a 'name' property if set to an object.
* @throws {TypeError} If the provided value is not a boolean or an object.
*/
set external(value) {
if (typeof value === 'boolean') {
this.#_data.external = value;
}
else if (!(typeof value === 'object' && Object.keys(value).length > 0)) {
if (helpers_1.default.hasOwnProperty(value, 'name')) {
if (!(typeof value.name === 'string' && value.name.length > 0)) {
throw new TypeError("The external volume's 'name' property must be a non-empty string.");
}
}
else {
throw new TypeError("The external volume must have a 'name' property when defined as an object.");
}
this.#_data.external = value;
}
else {
throw new TypeError("The external volume must be a boolean or an object.");
}
}
/**
* Returns the labels associated with the volume.
* If not set, the default value is undefined.
* The labels are a set of key-value pairs that can be used to identify or categorize the volume.
* The keys must be strings and the values must be strings.
* The labels must be non-empty.
* @returns {Record<string, string> | undefined} An object containing key-value pairs of labels, or undefined if not set.
*/
get labels() { return this.#_data.labels; }
/**
* Sets the labels for the volume.
* The labels are a set of key-value pairs that can be used to identify or categorize the volume.
* The keys must be strings and the values must be strings.
* The labels must be non-empty.
* @param value A non-empty object containing key-value pairs of labels.
* @throws {TypeError} If the labels are not a non-empty object or if the values are not strings.
* @throws {RangeError} If any of the values are empty.
*/
set labels(value) {
if (!(typeof value === 'object' && Object.keys(value).length > 0)) {
throw new TypeError("The volume's 'labels' property must be a non-empty object.");
}
for (const [key, val] of Object.entries(value)) {
if (typeof val !== 'string') {
throw new TypeError(`The volume's label '${key}' must be a string, instead of ${typeof val}.`);
}
if (val.length === 0) {
throw new RangeError(`The volume's label '${key}' must not be empty.`);
}
}
this.#_data.labels = value;
}
/**
* Returns the scope of the volume.
* The scope determines the lifetime of the volume.
* If the scope is 'global', the volume is created once and is shared across all containers in all services.
* If the scope is 'local', a new volume is created for each container in each service.
* If not set, the default scope is 'local'.
* @returns {'global' | 'local' | undefined} The scope of the volume, or undefined if not set.
*/
get scope() { return this.#_data.scope; }
/**
* Sets the scope of the volume.
* The scope of the volume determines its lifetime.
* If the scope is 'global', the volume is created once and is shared across all containers in all services.
* If the scope is 'local', a new volume is created for each container in each service.
* If not set, the default value is undefined.
* @param value A string indicating the scope of the volume, either 'global' or 'local'.
* @throws {SyntaxError} If the provided value is not 'global' or 'local'.
*/
set scope(value) {
if (value !== 'global' && value !== 'local') {
throw new SyntaxError("The volume's 'scope' property must be either 'global' or 'local'.");
}
this.#_data.scope = value;
}
/**
* Returns the access mode for the volume.
* The access mode determines how the volume is used.
* If the access mode is 'read-write', the volume is read-write.
* If the access mode is 'read-only', the volume is read-only.
* If not set, the default value is undefined.
* @returns {'read-write' | 'read-only' | undefined} A string indicating the access mode of the volume, either 'read-write' or 'read-only', or undefined if not set.
*/
get accessMode() { return this.#_data.accessMode; }
/**
* Sets the access mode for the volume.
* The access mode determines how the volume is used.
* If the access mode is 'read-write', the volume is read-write.
* If the access mode is 'read-only', the volume is read-only.
* If not set, the default value is undefined.
* @param value A string indicating the access mode of the volume, either 'read-write' or 'read-only'.
* @throws {SyntaxError} If the provided value is not 'read-write' or 'read-only'.
*/
set accessMode(value) {
if (value !== 'read-write' && value !== 'read-only') {
throw new SyntaxError("The volume's 'accessMode' property must be either 'read-write' or 'read-only'.");
}
this.#_data.accessMode = value;
}
/**
* Returns whether the volume is a temporary filesystem.
* A temporary filesystem is deleted when the container is deleted.
* If not set, the default value is undefined.
* @returns {boolean | undefined} A boolean indicating if the volume is a temporary filesystem, or undefined if not set.
*/
get tmpfs() { return this.#_data.tmpfs; }
/**
* Sets the flag indicating whether the volume is a temporary filesystem.
* The volume is deleted when the container is deleted.
* If not set, the default value is undefined.
* @param value A boolean indicating whether the volume is a temporary filesystem.
* @throws {TypeError} If the provided value is not a boolean.
*/
set tmpfs(value) {
if (typeof value !== 'boolean') {
throw new TypeError("The volume's 'tmpfs' property must be a boolean.");
}
this.#_data.tmpfs = value;
}
/**
* Returns the size of the volume.
* The size property is optional and must be a string if provided.
* If provided, it must be a size string (e.g., '10m', '1g', etc.).
* If not provided, the volume size is determined by the driver.
* @returns {string | undefined} A size string indicating the size of the volume, or undefined if not set.
*/
get size() { return this.#_data.size; }
/**
* Sets the size of the volume.
* The size property is optional and must be a string if provided.
* If provided, it must be a size string (e.g., '10m', '1g', etc.).
* If not provided, the volume size is determined by the driver.
*
* @param value A non-empty string representing the size of the volume.
* @throws {TypeError} If the provided value is not a non-empty string.
*/
set size(value) {
if (!(typeof value === 'string' && value.length > 0)) {
throw new TypeError("The volume's 'size' property must be a non-empty string.");
}
this.#_data.size = value;
}
}
exports.default = StackVolume;