feature/node-rewrite
Sven Slootweg 2 years ago
parent a93e3cf8dd
commit e9d0abba70

@ -37,6 +37,7 @@
"@validatem/required": "^0.1.1", "@validatem/required": "^0.1.1",
"@validatem/when": "^0.1.0", "@validatem/when": "^0.1.0",
"JSONStream": "^1.1.4", "JSONStream": "^1.1.4",
"ansi-html-community": "^0.0.8",
"ansi-to-html": "^0.7.2", "ansi-to-html": "^0.7.2",
"argon2": "^0.27.0", "argon2": "^0.27.0",
"array.prototype.flat": "^1.2.1", "array.prototype.flat": "^1.2.1",
@ -70,6 +71,7 @@
"function-rate-limit": "^1.1.0", "function-rate-limit": "^1.1.0",
"generate-lookup-table": "^1.0.0", "generate-lookup-table": "^1.0.0",
"graphql": "^14.2.1", "graphql": "^14.2.1",
"htmlentities": "^1.0.0",
"is-iterable": "^1.1.1", "is-iterable": "^1.1.1",
"is-plain-obj": "^3.0.0", "is-plain-obj": "^3.0.0",
"knex": "^0.21.18", "knex": "^0.21.18",

@ -126,15 +126,16 @@ table.drives th.unknown {
color: rgb(59, 59, 59); color: rgb(59, 59, 59);
} }
.stacktrace { .stacktrace, .debugPrint {
white-space: pre-wrap; white-space: pre-wrap;
font-family: monospace; font-family: monospace;
background-color: rgb(12, 12, 12); background-color: rgb(20, 20, 20);
border: 1px solid black; border: 1px solid black;
padding: 0.8em; padding: 0.8em;
max-width: 1200px; max-width: 1200px;
color: white;
} }
.stacktrace .irrelevant { .stacktrace .irrelevant, .debugPrint .irrelevant {
color: gray; color: gray;
} }

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../src/scss/style.scss"],"names":[],"mappings":"AAIA;EACC,kBALqB;EAMrB;EACA;;;AAGD;EACC;;;AAOD;EACC;;;AAGD;EACC,kBAtBqB;;AAwBrB;EACC;EACA;;AAGD;EACC;;AAIA;EACC;EACA;EACA;;AAIA;AAEC;EACA,kBA3CqB;EA4CrB;;AAKD;EACC;EACA;;;AAMJ;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAKD;EACC,kBArFmB;EAsFnB;EACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;;AAKD;EACC;;AAIA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAIF;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC","file":"style.css"} {"version":3,"sourceRoot":"","sources":["../../src/scss/style.scss"],"names":[],"mappings":"AAIA;EACC,kBALqB;EAMrB;EACA;;;AAGD;EACC;;;AAOD;EACC;;;AAGD;EACC,kBAtBqB;;AAwBrB;EACC;EACA;;AAGD;EACC;;AAIA;EACC;EACA;EACA;;AAIA;AAEC;EACA,kBA3CqB;EA4CrB;;AAKD;EACC;EACA;;;AAMJ;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAKD;EACC,kBArFmB;EAsFnB;EACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;;AAKD;EACC;;AAIA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAIF;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC","file":"style.css"}

@ -74,6 +74,10 @@ module.exports = function () {
extended: true extended: true
})); }));
app.get("/debug-query", (req, res) => {
res.render("debug-query");
});
app.use(require("./routes/index")); app.use(require("./routes/index"));
app.use("/disk-images", require("./routes/disk-images")(state)); app.use("/disk-images", require("./routes/disk-images")(state));
app.use("/instances", require("./routes/instances")(state)); app.use("/instances", require("./routes/instances")(state));

@ -1,197 +0,0 @@
"use strict";
const Promise = require("bluebird");
const graphql = require("graphql");
const util = require("util");
const chalk = require("chalk");
const gql = require("./graphql/tag");
const api = require("./api/index");
function debugDisplay(results) {
if (results.errors != null && results.errors.length > 0) {
results.errors.forEach((graphqlError) => {
let errorHeader;
if (graphqlError.path != null) {
errorHeader = `Error occurred for path: ${graphqlError.path.join(" -> ")}`;
} else if (graphqlError.locations != null && graphqlError.locations.length > 0) {
errorHeader = `Error occurred at line ${graphqlError.locations[0].line}, column ${graphqlError.locations[0].column}`;
} else {
errorHeader = "Error occurred in GraphQL";
}
console.log(chalk.bgBlue.white(errorHeader));
let error = graphqlError.originalError;
if (error != null) {
if (error.showChain != null) {
console.log(error.showChain());
} else {
console.log(error.stack);
}
} else {
console.log(graphqlError.stack);
}
console.log("-----------------------------");
});
}
console.log(util.inspect(results.data, {colors: true, depth: null}));
}
// ###############################################
let makeQuery = api();
// FIXME: If we intend to target macOS, a lot of whitespace-based output splitting won't work: https://www.mail-archive.com/austin-group-l@opengroup.org/msg01678.html
// findmnt --json -o +SIZE,AVAIL
// -> map back to mountPoint stuff?
// blkid
// to discover the filesystem that a given path exists on: stat -c %m
// partx
// (rest of util-linux)
// memory usage: /proc/meminfo
return Promise.try(() => {
let query = gql`
query {
hardware {
drives {
smartHealth
size
rpm
serialNumber
model
modelFamily
firmwareVersion
blockDevice {
name
}
partitions: allBlockDevices(type: PARTITION) {
name
size
mounts {
mountpoint
}
}
}
}
}
`;
// let query = gql`
// # query SomeDrives($drivePaths: [String]) {
// query SomeDrives {
// hardware {
// drives {
// path
// interface
// model
// modelFamily
// blockDevice {
// submounts: mounts(type: SUBMOUNT) {
// mountpoint
// filesystem
// }
// }
// # smartAvailable
// # smartEnabled
// # serialNumber
// # wwn
// # firmwareVersion
// # size
// # rpm
// # logicalSectorSize
// # physicalSectorSize
// # formFactor
// # ataVersion
// # sataVersion
// # smartHealth
// # smartAttributes {
// # name
// # type
// # value
// # failingNow
// # flags {
// # affectsPerformance
// # indicatesFailure
// # }
// # }
// # blockDevice {
// # removable
// # children {
// # name
// # mountpoint
// # size
// # }
// # }
// }
// }
// # resources {
// # blockDevices {
// # name
// # mountpoint
// # size
// # deviceNumber
// # removable
// # readOnly
// # parent { name }
// # children {
// # name
// # mountpoint
// # size
// # deviceNumber
// # removable
// # readOnly
// # parent { name }
// # }
// # }
// # lvm {
// # physicalVolumes {
// # path
// # blockDevice {
// # name
// # deviceNumber
// # }
// # volumeGroup {
// # name
// # }
// # format
// # size
// # freeSpace
// # duplicate
// # allocatable
// # used
// # exported
// # missing
// # }
// # }
// # }
// }
// `;
return makeQuery(query, {
// drivePaths: ["/dev/sda", "/dev/sdb"]
});
}).then((results) => {
debugDisplay(results);
});

@ -50,6 +50,7 @@ function mapDeviceList(devices) {
deviceNumber: device["maj:min"], deviceNumber: device["maj:min"],
removable: parseBoolean(device.rm), removable: parseBoolean(device.rm),
readOnly: parseBoolean(device.ro), readOnly: parseBoolean(device.ro),
rotational: parseBoolean(device.rota),
size: B(device.size), size: B(device.size),
children: (device.children != null) ? mapDeviceList(device.children) : [] children: (device.children != null) ? mapDeviceList(device.children) : []
}; };

@ -3,22 +3,20 @@ import { RestOfLine } from "../../peg-rest-of-line"
{ {
const syncpipe = require("syncpipe"); const syncpipe = require("syncpipe");
const mapVersionTitle = require("../map-version-title"); const fromNamedEntries = require("../../from-named-entries");
const mapVersionLabel = require("../map-version-label");
} }
Output Output
= entries:VersionLine+ { = entries:VersionLine+ {
// FIXME/MARKER: Build a generic abstraction for fromNamedEntries or so return fromNamedEntries(entries);
return syncpipe(entries, [
(_) => _.map(({ key, value }) => [ key, value ]),
(_) => Object.fromEntries(_)
]);
} }
VersionLine VersionLine
= _ label:$[A-Za-z ]+ ":" _ version:RestOfLine { = _ label:$[A-Za-z ]+ ":" _ version:RestOfLine {
return { return {
key: mapVersionTitle(label), // FIXME/MARKER: Rename to mapVersionLabel key: mapVersionLabel(label),
value: version.trim() value: version.trim()
}; };
} }

@ -0,0 +1,10 @@
"use strict";
// Like Object.fromEntries, but for {key,value} instead of [key,value]
module.exports = function fromNamedEntries(namedEntries) {
return Object.fromEntries(namedEntries.map((entry) => {
let { key, value } = entry;
return [ key, value ];
}));
};

@ -16,7 +16,6 @@ function createResultObject(isSuccessful, containedValue) {
} }
}, },
value: function () { value: function () {
// MARKER; either return value or throw the error it contains, to emulate standard throw behaviour
if (isSuccessful) { if (isSuccessful) {
return containedValue; return containedValue;
} else { } else {

@ -1,428 +0,0 @@
scalar ByteSize
scalar TimeSize
type AccessPermissions {
read: Boolean
write: Boolean
execute: Boolean
}
type Permissions {
owner: AccessPermissions
group: AccessPermissions
everybody: AccessPermissions
setUID: Boolean
setGID: Boolean
sticky: Boolean
}
type User {
name: String
id: Int
# FIXME
}
type Group {
name: String
id: Int
# FIXME
}
type RawMountValueOption {
key: String!
value: String
}
type RawMountFlagOption {
key: String!
}
union RawMountOption = RawMountFlagOption | RawMountValueOption
enum MountErrorHandlingMode {
PANIC
CONTINUE
REMOUNT_READ_ONLY
}
enum ExtAllocator {
OLD
ORLOV
}
enum ExtJournalingMode {
JOURNAL
ORDERED
WRITEBACK
}
enum ExtBufferErrorHandlingMode {
IGNORE_ERROR
ABORT_JOURNAL
}
enum ExtQuotaSystem {
OLD
V0
V1
}
enum FatTimestampsAllowedFrom {
EVERYBODY
GROUP
OWNER
}
enum FatNameStrictness {
RELAXED
NORMAL
STRICT
}
enum FatShortNameMode {
LOWER
WINDOWS_95
WINDOWS_NT
MIXED
}
enum FatLineEndingConversionPolicy {
BINARY
TEXT
AUTO
}
type MountOptions {
writable: Boolean
userMountable: Boolean
freelyMountable: Boolean
asynchronous: Boolean
asynchronousDirectoryUpdates: Boolean
allowDeviceNodes: Boolean
allowExecution: Boolean
allowMandatoryLocks: Boolean
allowSetUIDBits: Boolean
requiresNetworkAccess: Boolean
allowOfflineChecks: Boolean
updateAccessTime: Boolean
updateDirectoryAccessTime: Boolean
updateAccessTimeRelatively: Boolean
updateAccessTimeStrictly: Boolean
automaticallyMountable: Boolean
incrementIVersion: Boolean
printDebugInformation: Boolean
reportMountingErrors: Boolean
onError: MountErrorHandlingMode
owner: User
group: Group
filesystemOwner: User
filesystemGroup: Group
filesystemPermissions: Permissions
# devpts
newPTYOwner: User
newPTYGroup: Group
newPTYPermissions: Permissions
isolatePTYs: Boolean
ptmxPermissions: Permissions
# tmpfs, hugetlbfs
rootOwner: User
rootGroup: Group
rootPermissions: Permissions
maximumInodes: Int
size: ByteSize
# hugetlbfs
pageSize: ByteSize
sizeAsPoolPercentage: Float
minimumSize: ByteSize
minimumSizeAsPoolPercentage: Float
# tmpfs
blockCount: Int
numaPolicy: String
# ext2, ext3, ext4
allow32bitIdentifiers: Boolean
allow64bitInodeVersions: Boolean
allowExtendedAttributes: Boolean
supportACL: Boolean
showOnlyUsableSpace: Boolean
useGroupIDFromDirectory: Boolean
allowAutomaticFilesystemResizing: Boolean
enableMetadataBlockTracking: Boolean
simulateAbort: Boolean
attachBufferHeads: Boolean # obsolete
checkFilesystemOnMount: Boolean
enableWriteBarriers: Boolean
enableHardwareDeleteCalls: Boolean
allocator: ExtAllocator
reservedSpaceForGroup: Group
reservedSpaceForUser: User
superblockIndex: Int
inodeBlockReadAheadLimit: Int
stripeBlocks: Int
directorySizeLimit: ByteSize
updateJournalFormat: Boolean
enableJournalChecksumming: Boolean
asynchronousJournalCommits: Boolean
processJournal: Boolean
journalInode: Int
journalDevice: String # FIXME: Translate this into a BlockDevice somehow? Expected 'number' format here is unclear.
journalPath: String
journalingMode: ExtJournalingMode
journalIOPriority: Int
onBufferError: ExtBufferErrorHandlingMode
commitInterval: TimeSize
allowDeferredAllocations: Boolean
batchingTimeLimit: TimeSize
batchingTimeMinimum: TimeSize
enableMetadataBlockCache: Boolean
enableDirectIOReadLocking: Boolean
enableInodeTableBlockInitialization: Boolean
inodeTableBlockInitializationDelay: Int
automaticallySynchronizeBeforeRename: Boolean
enableGroupQuota: Boolean
enableUserQuota: Boolean
enableProjectQuota: Boolean
quotaSystem: ExtQuotaSystem
userQuotaFile: String
groupQuotaFile: String
# fat, vfat
blockSize: ByteSize
allocationTableBitness: Int
compressedVolumeFileModule: String
compressedVolumeFileOption: String
defaultFileMode: Permissions
defaultFolderMode: Permissions
nameStrictness: FatNameStrictness
timestampChangesAllowedFrom: FatTimestampsAllowedFrom
restrictExecutableModeToWindowsBinaries: Boolean
treatAttrSysFlagAsImmutable: Boolean
allowDifferentlyCasedNameConflicts: Boolean
shortNameMode: FatShortNameMode
preferShortNamesWithoutSequenceNumber: Boolean
enableUnicodeCharacterEscaping: Boolean
supportUtf8: Boolean
enableReadOnlyFlagSupport: Boolean
enableDOS1xFallbackConfiguration: Boolean
enableQuietModeFailures: Boolean
enableNFSInodeCache: Boolean
enableEagerFlushing: Boolean
enableFreeClusterCache: Boolean
enableTimezoneConversion: Boolean
timestampOffset: TimeSize
lineEndingConversionPolicy: FatLineEndingConversionPolicy
conversionCharacterSet: String
codepage: Int
}
enum MountType {
ROOT_MOUNT
SUBMOUNT
}
type Mount {
mountpoint: String!
type: MountType!
id: Int!
taskID: Int!
sourceDevice: BlockDevice!
rootPath: String
options: MountOptions!
filesystem: String!
label: String
uuid: String
partitionLabel: String
partitionUUID: String
deviceNumber: String
totalSpace: ByteSize
freeSpace: ByteSize
usedSpace: ByteSize
optionalFields: String
propagationFlags: String
children: [Mount!]!
# FIXME
# rawOptions: [RawMountOption]
}
type SmartAttributeFlags {
autoKeep: Boolean!
eventCount: Boolean!
errorRate: Boolean!
affectsPerformance: Boolean!
updatedOnline: Boolean!
indicatesFailure: Boolean!
}
enum SmartAttributeType {
PRE_FAIL
OLD_AGE
}
enum SmartAttributeUpdateType {
ALWAYS
OFFLINE
}
enum SmartHealth {
HEALTHY
DETERIORATING
FAILING
}
type SmartAttribute {
id: Int!
name: String!
flags: SmartAttributeFlags!
value: Int!
rawValue: String!
worstValueSeen: Int!
failureThreshold: Int!
type: SmartAttributeType!
failingNow: Boolean!
failedBefore: Boolean!
updatedWhen: SmartAttributeUpdateType!
}
enum BlockDeviceType {
DISK
PARTITION
LOOP_DEVICE
}
type BlockDevice {
name: String!
type: BlockDeviceType!
path: String!
mounts(type: MountType): [Mount!]!
# mountpoint: String
deviceNumber: String!
removable: Boolean!
readOnly: Boolean!
size: ByteSize!
children: [BlockDevice!]!
# For tree linearization
_treecutterDepth: Int
_treecutterSequenceNumber: Int
}
type PhysicalDrive {
path: String!
interface: String!
blockDevice: BlockDevice
allBlockDevices(type: BlockDeviceType): [BlockDevice!]!
smartAvailable: Boolean!
smartEnabled: Boolean
smartHealth: SmartHealth
smartAttributes: [SmartAttribute!]!
model: String
modelFamily: String
serialNumber: String
wwn: String,
firmwareVersion: String
size: ByteSize
rpm: Int
logicalSectorSize: ByteSize
physicalSectorSize: ByteSize
formFactor: String
ataVersion: String
sataVersion: String
}
type LVMPhysicalVolume {
path: String!
blockDevice: BlockDevice!
volumeGroup: LVMVolumeGroup!
format: String!
size: ByteSize!
freeSpace: ByteSize!
duplicate: Boolean!
allocatable: Boolean!
used: Boolean!
exported: Boolean!
missing: Boolean!
}
type LVMVolumeGroup {
name: String!
}
interface Image {
id: String!
name: String!
description: String
thumbnail: String
originalSource: String!
# The below are only available after the image has been downloaded
filesize: ByteSize
storagePath: String
}
type InstallationMedium implements Image {
id: String!
name: String!
description: String
thumbnail: String
originalSource: String!
# The below are only available after the image has been downloaded
filesize: ByteSize
storagePath: String
}
type VMImage implements Image {
id: String!
name: String!
description: String
thumbnail: String
originalSource: String!
# The below are only available after the image has been downloaded
filesize: ByteSize
storagePath: String
}
type HardwareQuery {
drives(paths: [String]): [PhysicalDrive!]!
}
type LVMQuery {
physicalVolumes: [LVMPhysicalVolume!]!
volumeGroups: [LVMVolumeGroup!]!
}
type ImagesQuery {
installationMedia: [InstallationMedium!]!
vmImages: [VMImage!]!
}
type ResourcesQuery {
blockDevices: [BlockDevice!]!
lvm: LVMQuery
# TODO: RAID
images: ImagesQuery
}
type Query {
hardware: HardwareQuery!
resources: ResourcesQuery!
}

@ -176,13 +176,14 @@ table.drives {
} }
} }
.stacktrace { .stacktrace, .debugPrint {
white-space: pre-wrap; white-space: pre-wrap;
font-family: monospace; font-family: monospace;
background-color: rgb(12, 12, 12); background-color: rgb(20, 20, 20);
border: 1px solid black; border: 1px solid black;
padding: .8em; padding: .8em;
max-width: 1200px; max-width: 1200px;
color: white; // Default text color only
.irrelevant { .irrelevant {
color: gray; color: gray;

@ -0,0 +1,47 @@
"use strict";
const React = require("react");
const ansiHTML = require("ansi-html-community");
const htmlentities = require("htmlentities");
const util = require("util");
const Layout = require("./layout.jsx");
module.exports = {
query: {
hardware: {
drives: {
path: true,
partitions: {
$key: "allBlockDevices",
name: true,
size: true,
mounts: {
mountpoint: true
},
children: {
$recurse: true,
$recurseLimit: Infinity, // 3 by default
}
}
}
}
},
template: function StorageDeviceList({data}) {
if (process.env.NODE_ENV === "development") {
return (
<Layout title="Debug query">
<pre
className="debugPrint"
dangerouslySetInnerHTML={{ __html: ansiHTML(htmlentities.encode(util.inspect(data, { colors: true, depth: null })))}}
/>
</Layout>
);
} else {
// FIXME: Proper error, or just prevent the route from being set outside of development mode at all
return "This view is only accessible in development mode.";
}
}
};

@ -1343,6 +1343,11 @@ ansi-escapes@^4.2.1:
dependencies: dependencies:
type-fest "^0.11.0" type-fest "^0.11.0"
ansi-html-community@^0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
ansi-regex@^0.2.0, ansi-regex@^0.2.1: ansi-regex@^0.2.0, ansi-regex@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9"
@ -4146,6 +4151,11 @@ hosted-git-info@^2.1.4:
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
htmlentities@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/htmlentities/-/htmlentities-1.0.0.tgz#093a8c1fb09dfe5e169fc33d09574825d61e40a4"
integrity sha512-FVEsyLbpWqXh/qIaRrOuZo3sKN4bihKYSUKvI0byJhPV9rO6DokXDWew+uFnA41dGmTQqmyF7hSFfV5DDVcL/A==
htmlescape@^1.1.0: htmlescape@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"

Loading…
Cancel
Save