Compare commits

...

2 Commits

Author SHA1 Message Date
Sven Slootweg f85ab90be1 WIP 4 years ago
Sven Slootweg 98a4bcb6d8 WIP 4 years ago

@ -1,6 +1,6 @@
MARKER:
- Replace local `unreachable` with @joepie91/unreachable
- Update all Validatem usage to new validateArguments API
- Move to pegjs-import
- Replace all 'this is a bug' errors with @joepie91/unreachable
- LVM / mdraid support and tabs (+ complete refactoring LVM implementation)
- Switch hashing to argon2id
- Switch child_process to execa

@ -14,15 +14,25 @@
"license": "WTFPL",
"dependencies": {
"@babel/register": "^7.8.3",
"@invisible/pegjs-import": "^1.1.1",
"@joepie91/express-react-views": "^1.0.1",
"@joepie91/unreachable": "^1.0.0",
"@validatem/allow-extra-properties": "^0.1.0",
"@validatem/anything": "^0.1.0",
"@validatem/array-of": "^0.1.2",
"@validatem/core": "^0.3.15",
"@validatem/default-to": "^0.1.0",
"@validatem/dynamic": "^0.1.2",
"@validatem/either": "^0.1.9",
"@validatem/error": "^1.1.0",
"@validatem/is-array": "^0.1.1",
"@validatem/is-boolean": "^0.1.1",
"@validatem/is-function": "^0.1.0",
"@validatem/is-number": "^0.1.3",
"@validatem/is-plain-object": "^0.1.1",
"@validatem/is-regular-expression": "^0.1.0",
"@validatem/is-string": "^1.0.0",
"@validatem/require-either": "^0.1.0",
"@validatem/required": "^0.1.1",
"@validatem/when": "^0.1.0",
"JSONStream": "^1.1.4",
@ -74,8 +84,7 @@
"sse-channel": "^3.1.1",
"syncpipe": "^1.0.0",
"through2": "^2.0.1",
"uuid": "^2.0.2",
"validatem": "^0.2.0"
"uuid": "^2.0.2"
},
"devDependencies": {
"@babel/core": "^7.8.4",

@ -4,12 +4,12 @@ const Promise = require("bluebird");
const memoizee = require("memoizee");
const asExpression = require("as-expression");
const fs = Promise.promisifyAll(require("fs"));
const unreachable = require("@joepie91/unreachable")("cvm");
const lsblk = require("../../packages/exec-lsblk");
const All = require("../../packages/graphql-interface/symbols/all");
const treecutter = require("../../packages/treecutter");
const findInTree = require("../../packages/find-in-tree");
const shallowMerge = require("../../packages/shallow-merge");
const unreachable = require("../../packages/unreachable");
module.exports = function () {
let lsblkOnce = memoizee(() => {

@ -101,6 +101,9 @@ module.exports = function () {
// }
console.log(errorChain.getContext(sourceError));
res.render("error", {
error: err
});

@ -7,7 +7,7 @@ const util = require("util");
const execFileAsync = util.promisify(require("child_process").execFile);
const debug = require("debug")("cvm:execBinary");
const asExpression = require("as-expression");
const { rethrowAs } = require("error-chain");
const { rethrowAs, chain } = require("error-chain");
const textParser = require("../text-parser");
const errors = require("./errors");
@ -194,7 +194,7 @@ module.exports = function createBinaryInvocation(command, args = []) {
if (!this._settings.expectedExitCodes.includes(exitCode)) {
// FIXME: Can we actually pass `error` to be chained onto here, when there's a case where `error` is undefined? Namely, when requiring a non-zero exit code, but the process exits with 0.
throw new errors.NonZeroExitCode.chain(error, `Expected exit code to be one of ${JSON.stringify(this._settings.expectedExitCodes)}, but got '${exitCode}'`, {
throw chain(error, errors.NonZeroExitCode, `Expected exit code to be one of ${JSON.stringify(this._settings.expectedExitCodes)}, but got '${exitCode}'`, {
exitCode: exitCode,
stdout: stdout,
stderr: stderr
@ -212,6 +212,7 @@ module.exports = function createBinaryInvocation(command, args = []) {
return undefined;
}
} else {
// FIXME: use @joepie91/unreachable
throw new Error(`Encountered expectation for unexpected channel '${expectation.channel}'; this is a bug, please report it`, {
failedChannel: expectation.channel
});
@ -230,7 +231,7 @@ module.exports = function createBinaryInvocation(command, args = []) {
return undefined;
}
} else {
throw errors.OutputParsingFailed.chain(error, `An error occurred while parsing '${expectation.channel}'`, {
throw chain(error, errors.OutputParsingFailed, `An error occurred while parsing '${expectation.channel}'`, {
failedChannel: expectation.channel
});
}
@ -270,7 +271,7 @@ module.exports = function createBinaryInvocation(command, args = []) {
? `Failed while processing ${error.failedChannel} of command`
: "Failed while processing result of command execution";
throw errors.CommandExecutionFailed.chain(error, message, {
throw chain(error, errors.CommandExecutionFailed, message, {
exitCode: exitCode,
stdout: stdout,
stderr: stderr

@ -1,6 +1,7 @@
"use strict";
const Promise = require("bluebird");
const { chain } = require("error-chain");
const execBinary = require("../exec-binary");
const parseIECBytes = require("../parse-bytes-iec");
@ -120,11 +121,11 @@ module.exports = {
}).then((_output) => {
return true;
}).catch(hasFlag("deviceNotFound"), (error) => {
throw errors.InvalidPath.chain(error, `Specified device '${devicePath}' does not exist`, {
throw chain(error, errors.InvalidPath, `Specified device '${devicePath}' does not exist`, {
path: devicePath
});
}).catch(hasFlag("partitionTableExists"), (error) => {
throw errors.PartitionExists.chain(error, `Refused to create a Physical Volume, as a partition or partition table already exists on device '${devicePath}'`, {
throw chain(error, errors.PartitionExists, `Refused to create a Physical Volume, as a partition or partition table already exists on device '${devicePath}'`, {
path: devicePath
});
});
@ -141,11 +142,11 @@ module.exports = {
}).then((_output) => {
return true;
}).catch(hasFlag("deviceNotFound"), (error) => {
throw errors.InvalidPath.chain(error, `Specified device '${devicePath}' does not exist`, {
throw chain(error, errors.InvalidPath, `Specified device '${devicePath}' does not exist`, {
path: devicePath
});
}).catch(hasFlag("notAPhysicalVolume"), (error) => {
throw errors.InvalidPath.chain(error, `Specified device '${devicePath}' is not a Physical Volume`, {
throw chain(error, errors.InvalidPath, `Specified device '${devicePath}' is not a Physical Volume`, {
path: devicePath
});
});
@ -178,17 +179,17 @@ module.exports = {
}).catch(hasFlag("deviceNotFound"), (error) => {
let failedDevices = error.getAllContext().result.deviceNotFound;
throw errors.InvalidPath.chain(error, `The following specified devices do not exist: ${failedDevices.join(", ")}`, {
throw chain(error, errors.InvalidPath, `The following specified devices do not exist: ${failedDevices.join(", ")}`, {
paths: failedDevices
});
}).catch(hasFlag("partitionTableExists"), (error) => {
let failedDevices = error.getAllContext().result.partitionTableExists;
throw errors.PartitionExists.chain(error, `Refused to create a Volume Group, as partitions or partition tables already exist on the following devices: ${failedDevices.join(", ")}`, {
throw chain(error, errors.PartitionExists, `Refused to create a Volume Group, as partitions or partition tables already exist on the following devices: ${failedDevices.join(", ")}`, {
paths: failedDevices
});
}).catch(hasFlag("volumeGroupExists"), (error) => {
throw errors.VolumeGroupExists.chain(error, `A volume group with the name '${name}' already exists`, {
throw chain(error, errors.VolumeGroupExists, `A volume group with the name '${name}' already exists`, {
volumeGroupName: name
});
}).catch(hasFlag("physicalVolumeInUse"), (error) => {
@ -198,7 +199,7 @@ module.exports = {
return `${device} (${volumeGroup})`;
}).join(", ");
throw errors.PhysicalVolumeInUse.chain(error, `The following specified Physical Volumes are already in use in another Volume Group: ${failedItemString}`, {
throw chain(error, errors.PhysicalVolumeInUse, `The following specified Physical Volumes are already in use in another Volume Group: ${failedItemString}`, {
volumes: failedItems
});
});
@ -218,21 +219,21 @@ module.exports = {
}).then((_output) => {
return true;
}).catch(hasFlag("deviceNotFound"), (error) => {
throw errors.InvalidPath.chain(error, `Specified device '${physicalVolume}' does not exist`, {
throw chain(error, errors.InvalidPath, `Specified device '${physicalVolume}' does not exist`, {
path: physicalVolume
});
}).catch(hasFlag("volumeGroupNotFound"), (error) => {
throw errors.InvalidVolumeGroup.chain(error, `Specified Volume Group '${volumeGroup}' does not exist`, {
throw chain(error, errors.InvalidVolumeGroup, `Specified Volume Group '${volumeGroup}' does not exist`, {
volumeGroupName: volumeGroup
});
}).catch(hasFlag("physicalVolumeInUse"), (error) => {
let volume = error.getAllContext().result.physicalVolumeInUse;
throw errors.PhysicalVolumeInUse.chain(error, `Specified Physical Volume '${physicalVolume}' is already in use in another Volume Group (${volume.volumeGroup})`, {
throw chain(error, errors.PhysicalVolumeInUse, `Specified Physical Volume '${physicalVolume}' is already in use in another Volume Group (${volume.volumeGroup})`, {
volume: volume
});
}).catch(hasFlag("partitionTableExists"), (error) => {
throw errors.PartitionExists.chain(error, `Refused to add device to Volume Group, as a partition or partition table already exists on device '${physicalVolume}'`, {
throw chain(error, errors.PartitionExists, `Refused to add device to Volume Group, as a partition or partition table already exists on device '${physicalVolume}'`, {
path: physicalVolume
});
});

@ -8,22 +8,25 @@ const itemsToObject = require("../items-to-object");
/* FIXME: Error handling, eg. device not found errors */
function outputParser(rootRule) {
function outputParser(parserPath) {
return createPegParser({
grammarFile: path.join(__dirname, "./parser.pegjs"),
options: {
allowedStartRules: [ rootRule ]
}
grammarFile: path.join(__dirname, parserPath)
});
}
let attributesParser = outputParser("./parsers/commands/attributes.pegjs");
let infoParser = outputParser("./parsers/commands/info.pegjs");
let scanParser = outputParser("./parsers/commands/scan.pegjs");
module.exports = {
attributes: function ({ devicePath }) {
return Promise.try(() => {
return attributesParser;
}).then((parser) => {
return execBinary("smartctl", [devicePath])
.asRoot()
.withFlags({ attributes: true })
.requireOnStdout(outputParser("RootAttributes"))
.requireOnStdout(parser)
.execute();
}).then((output) => {
// NOTE: Ignore the header, for now
@ -32,10 +35,12 @@ module.exports = {
},
info: function ({ devicePath }) {
return Promise.try(() => {
return infoParser;
}).then((parser) => {
return execBinary("smartctl", [devicePath])
.asRoot()
.withFlags({ info: true })
.requireOnStdout(outputParser("RootInfo"))
.requireOnStdout(parser)
.execute();
}).then((output) => {
// NOTE: Ignore the header, for now
@ -44,10 +49,12 @@ module.exports = {
},
scan: function () {
return Promise.try(() => {
return scanParser;
}).then((parser) => {
return execBinary("smartctl")
.asRoot()
.withFlags({ scan: true })
.requireOnStdout(outputParser("RootScan"))
.requireOnStdout(parser)
.execute();
}).then((output) => {
// NOTE: Ignore the header, for now

@ -1,249 +0,0 @@
{
const matchValue = require("match-value");
const syncpipe = require("syncpipe");
const {B} = require("../unit-bytes-iec");
const mapAttributeFlags = require("./map-attribute-flags");
}
RootInfo
= header:Header infoSection:InfoSection Newline* {
return { ...header, fields: infoSection }
};
RootScan
= devices:ScanDevice* {
return { devices: devices };
}
RootAttributes
= header:Header attributesSection:AttributesSection Newline* {
return { ...header, attributes: attributesSection }
};
_
= (" " / "\t")*
RestOfLine
= content:$[^\n]+ Newline {
return content;
}
Newline
= "\n"
/ "\r\n"
Header 'header'
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
return { versionString, copyrightStatement };
}
BytesValue
= value:SeparatedNumberValue {
return B(value);
}
NumberValue
= value:$[0-9]+ {
return parseInt(value);
}
SeparatedNumberValue
= value:$[0-9,]+ {
return syncpipe(value, [
(_) => _.replace(/,/g, ""),
(_) => parseInt(_)
]);
}
HexNumberValue
= value:$[0-9A-Fa-f]+ {
return parseInt(value, 16);
}
IdentifierValue
= value:$[a-zA-Z_-]+ {
return value;
}
// smartctl --scan
ScanDevice 'scanned device'
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
return { path: path, interface: interface_ };
}
// smartctl --info
InfoSection 'information section'
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
return fields.filter((field) => field != null);
}
InfoField 'information field'
= InfoFieldSimple
/ InfoFieldIgnored
/ InfoFieldSize
/ InfoFieldRPM
/ InfoFieldSectorSizes
/ InfoFieldBoolean
/ InfoFieldUnknown
InfoFieldSimpleKey
= "Device Model" { return "model"; }
/ "Model Number" { return "model"; }
/ "Model Family" { return "modelFamily"; }
/ "Serial Number" { return "serialNumber"; }
/ "LU WWN Device Id" { return "wwn"; }
/ "Firmware Version" { return "firmwareVersion"; }
/ "Form Factor" { return "formFactor"; }
/ "ATA Version is" { return "ataVersion"; }
/ "SATA Version is" { return "sataVersion"; }
InfoFieldSimple
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
return { key: key, value: value };
}
InfoFieldUnknown
= key:$[^:]+ ":" _ RestOfLine {
console.warn(`Encountered unrecognized SMART info key: ${key}`);
return null;
}
InfoFieldIgnoredKey
= "Device is"
/ "Local Time is"
InfoFieldIgnored
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
return null;
}
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
return null;
}
InfoFieldSize
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
return {
key: "size",
value: B(value)
};
}
InfoFieldSizeKey
= "User Capacity:"
/ "Total NVM Capacity:"
InfoFieldRPM
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
return {
key: "rpm",
value: value
};
}
InfoFieldSectorSizes
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
return {
key: "sectorSizes",
value: {
logical: logicalSize,
physical: physicalSize
}
};
}
InfoFieldBooleanKey
= "SMART support is" { return "smartEnabled"; }
InfoFieldBoolean
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
return {
key: key,
value: matchValue(value, {
Enabled: true,
Disabled: false
})
};
}
// smartctl --attributes
AttributesSection
= AttributesSectionSATA
/ AttributesSectionNVMe
AttributesSectionSATA
= "=== START OF READ SMART DATA SECTION ===" Newline
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
"Vendor Specific SMART Attributes with Thresholds:" Newline
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
attributes:AttributeFieldSATA+ {
return attributes;
}
AttributesSectionNVMe
= "=== START OF SMART DATA SECTION ===" Newline
"SMART/Health Information (NVMe Log 0x02)" Newline
attributes:AttributeFieldNVMe+ {
return attributes;
}
AttributeFlags
= "0x" number:HexNumberValue {
return mapAttributeFlags(number);
}
AttributeUpdatedWhen
= "Always"
/ "Offline"
AttributeFailedWhen
= "FAILING_NOW"
/ "In_the_past"
/ "-"
AttributeFieldType
= "Pre-fail"
/ "Old_age"
AttributeFieldSATA
= _ id:NumberValue
_ attributeName:IdentifierValue
_ flags:AttributeFlags
_ value:NumberValue
_ worstValue:NumberValue
_ threshold:NumberValue
_ type:AttributeFieldType
_ updatedWhen:AttributeUpdatedWhen
_ failedWhen:AttributeFailedWhen
_ rawValue:RestOfLine {
return {
id,
attributeName,
flags,
value,
worstValue,
threshold,
rawValue,
updatedWhen: matchValue(updatedWhen, {
"Always": "always",
"Offline": "offline"
}),
type: matchValue(type, {
"Pre-fail": "preFail",
"Old_age": "oldAge"
}),
failingNow: (failedWhen === "FAILING_NOW"),
/* TODO: Should the below include the FAILING_NOW state? */
failedBefore: (failedWhen === "In_the_past")
};
}
AttributeFieldNVMe
= label:$[^:]+ ":" _ value:RestOfLine {
return { label: label, value };
}

@ -0,0 +1,88 @@
import { _, RestOfLine, Newline, NumberValue, HexNumberValue, IdentifierValue } from "../primitives"
import { Header } from "../shared"
{
const matchValue = require("match-value");
const mapAttributeFlags = require("../../map-attribute-flags");
}
RootAttributes
= header:Header attributesSection:AttributesSection Newline* {
return { ...header, attributes: attributesSection }
};
AttributesSection
= AttributesSectionSATA
/ AttributesSectionNVMe
AttributesSectionSATA
= "=== START OF READ SMART DATA SECTION ===" Newline
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
"Vendor Specific SMART Attributes with Thresholds:" Newline
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
attributes:AttributeFieldSATA+ {
return attributes;
}
AttributesSectionNVMe
= "=== START OF SMART DATA SECTION ===" Newline
"SMART/Health Information (NVMe Log 0x02)" Newline
attributes:AttributeFieldNVMe+ {
return attributes;
}
AttributeFlags
= "0x" number:HexNumberValue {
return mapAttributeFlags(number);
}
AttributeUpdatedWhen
= "Always"
/ "Offline"
AttributeFailedWhen
= "FAILING_NOW"
/ "In_the_past"
/ "-"
AttributeFieldType
= "Pre-fail"
/ "Old_age"
AttributeFieldSATA
= _ id:NumberValue
_ attributeName:IdentifierValue
_ flags:AttributeFlags
_ value:NumberValue
_ worstValue:NumberValue
_ threshold:NumberValue
_ type:AttributeFieldType
_ updatedWhen:AttributeUpdatedWhen
_ failedWhen:AttributeFailedWhen
_ rawValue:RestOfLine {
return {
id,
attributeName,
flags,
value,
worstValue,
threshold,
rawValue,
updatedWhen: matchValue(updatedWhen, {
"Always": "always",
"Offline": "offline"
}),
type: matchValue(type, {
"Pre-fail": "preFail",
"Old_age": "oldAge"
}),
failingNow: (failedWhen === "FAILING_NOW"),
/* TODO: Should the below include the FAILING_NOW state? */
failedBefore: (failedWhen === "In_the_past")
};
}
AttributeFieldNVMe
= label:$[^:]+ ":" _ value:RestOfLine {
return { label: label, value };
}

@ -0,0 +1,106 @@
import { _, RestOfLine, Newline, NumberValue, SeparatedNumberValue, BytesValue } from "../primitives"
import { Header } from "../shared"
{
const matchValue = require("match-value");
}
RootInfo
= header:Header infoSection:InfoSection Newline* {
return { ...header, fields: infoSection }
};
InfoSection 'information section'
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
return fields.filter((field) => field != null);
}
InfoField 'information field'
= InfoFieldSimple
/ InfoFieldIgnored
/ InfoFieldSize
/ InfoFieldRPM
/ InfoFieldSectorSizes
/ InfoFieldBoolean
/ InfoFieldUnknown
InfoFieldSimpleKey
= "Device Model" { return "model"; }
/ "Model Number" { return "model"; }
/ "Model Family" { return "modelFamily"; }
/ "Serial Number" { return "serialNumber"; }
/ "LU WWN Device Id" { return "wwn"; }
/ "Firmware Version" { return "firmwareVersion"; }
/ "Form Factor" { return "formFactor"; }
/ "ATA Version is" { return "ataVersion"; }
/ "SATA Version is" { return "sataVersion"; }
InfoFieldSimple
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
return { key: key, value: value };
}
InfoFieldUnknown
= key:$[^:]+ ":" _ RestOfLine {
console.warn(`Encountered unrecognized SMART info key: ${key}`);
return null;
}
InfoFieldIgnoredKey
= "Device is"
/ "Local Time is"
InfoFieldIgnored
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
return null;
}
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
return null;
}
InfoFieldSize
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
return {
key: "size",
value: B(value)
};
}
InfoFieldSizeKey
= "User Capacity:"
/ "Total NVM Capacity:"
InfoFieldRPM
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
return {
key: "rpm",
value: value
};
}
InfoFieldSectorSizes
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
return {
key: "sectorSizes",
value: {
logical: logicalSize,
physical: physicalSize
}
};
}
InfoFieldBooleanKey
= "SMART support is" { return "smartEnabled"; }
InfoFieldBoolean
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
return {
key: key,
value: matchValue(value, {
Enabled: true,
Disabled: false
})
};
}

@ -0,0 +1,11 @@
import { _, RestOfLine } from "../primitives"
RootScan
= devices:ScanDevice* {
return { devices: devices };
}
ScanDevice 'scanned device'
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
return { path: path, interface: interface_ };
}

@ -0,0 +1,44 @@
{
const syncpipe = require("syncpipe");
const {B} = require("../../unit-bytes-iec");
}
_
= (" " / "\t")*
RestOfLine
= content:$[^\n]+ Newline {
return content;
}
Newline
= "\n"
/ "\r\n"
BytesValue
= value:SeparatedNumberValue {
return B(value);
}
NumberValue
= value:$[0-9]+ {
return parseInt(value);
}
SeparatedNumberValue
= value:$[0-9,]+ {
return syncpipe(value, [
(_) => _.replace(/,/g, ""),
(_) => parseInt(_)
]);
}
HexNumberValue
= value:$[0-9A-Fa-f]+ {
return parseInt(value, 16);
}
IdentifierValue
= value:$[a-zA-Z_-]+ {
return value;
}

@ -0,0 +1,6 @@
import { RestOfLine, Newline } from "./primitives"
Header 'header'
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
return { versionString, copyrightStatement };
}

@ -1,9 +1,13 @@
"use strict";
const { validateOptions, required, isFunction, isString } = require("validatem");
const assureArray = require("assure-array");
const isIterable = require("is-iterable");
const { validateOptions } = require("@validatem/core");
const required = require("@validatem/required");
const isFunction = require("@validatem/is-function");
const isString = require("@validatem/is-string");
module.exports = function findInTree(options) {
validateOptions(arguments, {
tree: [ required ],

@ -9,7 +9,8 @@ ensure NaN is handled correctly
const util = require("util");
const chalk = require("chalk");
const { validateArguments, required } = require("@validatem/core");
const { validateArguments } = require("@validatem/core");
const required = require("@validatem/required");
const arrayOf = require("@validatem/array-of");
const isString = require("@validatem/is-string");
const isNumber = require("@validatem/is-number");

@ -2,14 +2,17 @@
// FIXME: Finish this later
const { validateArguments, required, isString, isFunction } = require("validatem");
const { validateArguments } = require("@validatem/core");
const isString = require("@validatem/is-string");
const isFunction = require("@validatem/is-function");
const required = require("@validatem/required");
module.exports = function mapTree(tree, predicate, childrenProperty) {
validateArguments(arguments, [
[ "tree", required ],
[ "predicate", required, isFunction ],
[ "childrenProperty", isString ]
]);
validateArguments(arguments, {
tree: [ required ],
predicate: [ required, isFunction ],
childrenProperty: [ isString ]
});
};

@ -1,14 +1,15 @@
"use strict";
const { validateArguments, required } = require("@validatem/core");
const { validateArguments } = require("@validatem/core");
const isString = require("@validatem/is-string");
const isRegularExpression = require("@validatem/is-regular-expression");
const required = require("@validatem/required");
module.exports = function matchOrError(regex, string) {
validateArguments(arguments, [
[ "regex", required, isRegularExpression ],
[ "string", required, isString ]
]);
validateArguments(arguments, {
regex: [ required, isRegularExpression ],
string: [ required, isString ]
});
let match = regex.exec(string);

@ -1,6 +1,8 @@
"use strict";
const { validateArguments, required, isString } = require("validatem");
const { validateArguments } = require("@validatem/core");
const isString = require("@validatem/is-string");
const required = require("@validatem/required");
// FIXME: Length validation?
function parseModeDigit(modeDigit) {
@ -65,12 +67,12 @@ function intoDigits(modeString) {
}
module.exports = function parseModeString(modeString, { mask } = {}) {
validateArguments(arguments, [
[ "modeString", required, isString ],
[ "options", {
validateArguments(arguments, {
modeString: [ required, isString ],
options: {
mask: isString
}]
]);
}
});
let hasSpecialBits = (modeString.length === 4);
let modeDigits = intoDigits(modeString);

@ -1,73 +1,74 @@
"use strict";
const pegjs = require("pegjs");
const { validateOptions, either, required, isString, isPlainObject, allowExtraProperties } = require("validatem");
const fs = require("fs");
const Promise = require("bluebird");
const pegRedux = require("peg-redux");
const moduleEval = require("eval");
const vm = require("vm");
const asExpression = require("as-expression");
const { chain } = require("error-chain");
const textParser = require("../text-parser");
module.exports = function createPegParser({ grammar, grammarFile, options }) {
validateOptions(arguments, [
const { validateOptions } = require("@validatem/core");
const isString = require("@validatem/is-string");
const isPlainObject = require("@validatem/is-plain-object");
const requireEither = require("@validatem/require-either");
module.exports = function createPegParser(_options) {
let { grammar, grammarFile, options } = validateOptions(arguments, [
{
grammar: [ isString ],
grammarFile: [ isString ],
options: [ isPlainObject ]
},
// FIXME: require-either
either(
allowExtraProperties({ grammar: [ required ] }),
allowExtraProperties({ grammarFile: [ required ] })
)
}, requireEither([ "grammar", "grammarFile" ])
]);
if (grammarFile != null) {
// FIXME: cache
grammar = fs.readFileSync(grammarFile, "utf8");
}
let parserCode = pegjs.generate(grammar, {
let parserOptions = {
... options,
output: "source",
format: "commonjs"
});
let parser = asExpression(() => {
if (grammarFile != null) {
return moduleEval(parserCode, grammarFile, {}, true);
} else {
let exports_ = {};
};
let sandbox = {
exports: exports_,
module: {
return Promise.try(() => {
return (grammarFile != null)
? pegRedux.generateFromFile(grammarFile, parserOptions)
: pegRedux.generate(grammar, parserOptions);
}).then((parserCode) => {
let parser = asExpression(() => {
if (grammarFile != null) {
return moduleEval(parserCode, grammarFile, {}, true);
} else {
let exports_ = {};
let sandbox = {
exports: exports_,
},
require: function () {
throw new Error("You cannot use require() when loading a grammar as a string; use the `grammarFile` option instead");
}
};
let script = new vm.Script(parserCode.replace(/^\#\!.*/, ''));
script.runInNewContext(sandbox);
return sandbox.module.exports;
}
});
module: {
exports: exports_,
},
require: function () {
throw new Error("You cannot use require() when loading a grammar as a string; use the `grammarFile` option instead");
}
};
let script = new vm.Script(parserCode.replace(/^\#\!.*/, ''));
script.runInNewContext(sandbox);
return {
supportsStreams: false,
parse: function (text) {
try {
return parser.parse(text);
} catch (error) {
if (error.name === "SyntaxError") {
throw textParser.NoResult.chain(error, "Parsing output failed");
} else {
throw error;
return sandbox.module.exports;
}
});
return {
supportsStreams: false,
parse: function (text) {
try {
return parser.parse(text);
} catch (error) {
if (error.name === "SyntaxError") {
throw chain(error, textParser.NoResult, "Parsing output failed");
} else {
throw error;
}
}
}
}
};
};
});
};

@ -1,9 +1,14 @@
"use strict";
const { validateArguments, required, isString, isArray, ValidationError } = require("validatem");
const assureArray = require("assure-array");
const shallowMerge = require("../shallow-merge");
const { validateArguments } = require("@validatem/core");
const required = require("@validatem/required");
const isString = require("@validatem/is-string");
const isArray = require("@validatem/is-array");
const ValidationError = require("@validatem/error");
function createListValidator() {
let lastSequenceNumber = null;
@ -32,10 +37,10 @@ function defaultOptions(options = {}) {
module.exports = {
flatten: function (tree, options) {
validateArguments(arguments, [
[ "tree", required ],
[ "options", validateTreecutterOptions ]
]);
validateArguments(arguments, {
tree: [ required ],
options: [ validateTreecutterOptions ]
});
let { childrenProperty } = defaultOptions(options);
@ -69,10 +74,10 @@ module.exports = {
rebuild: function (list, options) {
let isTreecutterList = createListValidator();
validateArguments(arguments, [
[ "list", required, isTreecutterList ],
[ "options", validateTreecutterOptions ]
]);
validateArguments(arguments, {
list: [ required, isTreecutterList ],
options: [ validateTreecutterOptions ]
});
let { childrenProperty } = defaultOptions(options);

@ -1,6 +0,0 @@
"use strict";
module.exports = function unreachable(reason) {
// TODO: Parse the package name out of the stacktrace and include it in the error message?
throw new Error(`${reason} -- this is a bug, please report it!`);
};

@ -26,11 +26,11 @@ return Promise.try(() => {
// return lvm.addVolumeToVolumeGroup({ volumeGroup: "vg-name", physicalVolume: "/dev/loop1" });
// return lvm.destroyPhysicalVolume({ devicePath: "/dev/loop0" });
// return lsblk();
// return smartctl.scan();
return smartctl.scan();
// return smartctl.info({ devicePath: "/dev/sda" })
// return smartctl.info({ devicePath: process.argv[2] })
// return smartctl.attributes({ devicePath: process.argv[2] });
return findmnt();
// return findmnt();
// return nvmeCli.listNamespaces({ devicePath: "/dev/nvme0" });
}).then((result) => {
console.log(util.inspect(result, {colors: true, depth: null}));

@ -949,6 +949,13 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@invisible/pegjs-import@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@invisible/pegjs-import/-/pegjs-import-1.1.1.tgz#1c5feb6fd768604cadd63efc9dfb999e8b0a0c7f"
integrity sha512-TiUoDxO08miDb6EQaWQeuBtkPPAsOpw55HBCbBN+EtIXy7URT1fwWNt/5k/k0pI+U58FnnGSj361oRSPATuSmw==
dependencies:
pegjs "^0.10.0"
"@joepie91/eslint-config@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@joepie91/eslint-config/-/eslint-config-1.1.0.tgz#9397e6ce0a010cb57dcf8aef8754d3a5ce0ae36a"
@ -968,6 +975,11 @@
lodash.escaperegexp "^4.1.2"
object-assign "^4.1.1"
"@joepie91/unreachable@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@joepie91/unreachable/-/unreachable-1.0.0.tgz#8032bb8a5813e81bbbe516cb3031d60818526687"
integrity sha512-vZRJ5UDq4mqP1vgSrcOLD3aIfS/nzwsvGFOOHv5sj5fa1Ss0dT1xnIzrXKLD9pu5EcUvF3K6n6jdaMW8uXpNEQ==
"@phc/format@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4"
@ -1076,6 +1088,17 @@
dependencies:
"@validatem/combinator" "^0.1.1"
"@validatem/either@^0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@validatem/either/-/either-0.1.9.tgz#0d753ef8fe04486d2b7122de3dd3ac51b3acaacf"
integrity sha512-cUqlRjy02qDcZ166/D6duk8lrtqrHynHuSakU0TvMGMBiLzjWpMJ+3beAWHe+kILB5/dlXVyc68ZIjSNhBi8Kw==
dependencies:
"@validatem/combinator" "^0.1.1"
"@validatem/error" "^1.0.0"
"@validatem/match-validation-error" "^0.1.0"
"@validatem/validation-result" "^0.1.2"
flatten "^1.0.3"
"@validatem/error@^1.0.0", "@validatem/error@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@validatem/error/-/error-1.1.0.tgz#bef46e7066c39761b494ebe3eec2ecdc7348f4ed"
@ -1103,7 +1126,7 @@
default-value "^1.0.0"
flatten "^1.0.3"
"@validatem/is-array@^0.1.0":
"@validatem/is-array@^0.1.0", "@validatem/is-array@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@validatem/is-array/-/is-array-0.1.1.tgz#fbe15ca8c97c30b622a5bbeb536d341e99cfc2c5"
integrity sha512-XD3C+Nqfpnbb4oO//Ufodzvui7SsCIW/stxZ39dP/fyRsBHrdERinkFATH5HepegtDlWMQswm5m1XFRbQiP2oQ==
@ -1208,6 +1231,18 @@
dependencies:
"@validatem/error" "^1.0.0"
"@validatem/require-either@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@validatem/require-either/-/require-either-0.1.0.tgz#250e35ab06f124ea90f3925d74b5f53a083923b0"
integrity sha512-UyZtJieT3aJhO9tj1OJp47V9jpHCE7RSohue9jg3FyDGwmIBVYXCfASeM19mWg9W0lp6IevsqTmaGQhqQOQYJg==
dependencies:
"@validatem/allow-extra-properties" "^0.1.0"
"@validatem/either" "^0.1.9"
"@validatem/forbidden" "^0.1.0"
"@validatem/required" "^0.1.1"
assure-array "^1.0.0"
flatten "^1.0.3"
"@validatem/required@^0.1.0", "@validatem/required@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@validatem/required/-/required-0.1.1.tgz#64f4a87333fc5955511634036b7f8948ed269170"
@ -2087,9 +2122,9 @@ camelcase@^4.0.0:
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
caniuse-lite@^1.0.30001135:
version "1.0.30001150"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001150.tgz#6d0d829da654b0b233576de00335586bc2004df1"
integrity sha512-kiNKvihW0m36UhAFnl7bOAv0i1K1f6wpfVtTF5O5O82XzgtBnb05V0XeV3oZ968vfg2sRNChsHw8ASH2hDfoYQ==
version "1.0.30001151"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001151.tgz#1ddfde5e6fff02aad7940b4edb7d3ac76b0cb00b"
integrity sha512-Zh3sHqskX6mHNrqUerh+fkf0N72cMxrmflzje/JyVImfpknscMnkeJrlFGJcqTmaa0iszdYptGpWMJCRQDkBVw==
capitalize@^2.0.0:
version "2.0.3"

Loading…
Cancel
Save