You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
3.3 KiB
JavaScript

"use strict";
const DataLoader = require("dataloader");
const fs = require("fs").promises;
const matchValue = require("match-value");
const memoizee = require("memoizee");
const { InvalidObject } = require("dlayer");
const unreachable = require("@joepie91/unreachable")("@sysquery/block-devices");
const dlayerSource = require("../dlayer-source");
const All = require("../dlayer-symbol-all");
const lsblk = require("../exec-lsblk");
const mapFromSource = require("../map-from-source");
const treeMapAsync = require("../tree-map-async");
const treeFind = require("../tree-find");
function makePredicate({ path, name }) {
if (path != null) {
return (device) => device.path === path;
} else if (name != null) {
return (device) => device.name === name;
} else {
unreachable("No selector specified for lsblk");
}
}
module.exports = {
name: "sysquery.blockDevice",
makeContext: function () {
let lsblkOnce = memoizee(async () => {
return treeMapAsync(await lsblk(), async (device) => {
return {
... device,
path: await fs.realpath(device.path)
};
});
});
return {
// FIXME: This is probably broken; {path,name} selectors are always unique keys!
lsblk: new DataLoader(async function (selectors) {
let blockDeviceTree = await lsblkOnce();
return selectors.map((selector) => {
if (selector === All) {
return blockDeviceTree;
} else {
return treeFind(blockDeviceTree, makePredicate(selector));
}
});
})
};
},
types: {
"sysquery.blockDevices.BlockDevice": async function ({ name, path }, { lsblk }) {
let entry = await lsblk.load({ name, path });
if (entry != null) {
return {
... dlayerSource("lsblk", {
[dlayerSource.ID]: { name, path },
name: "name",
path: (device) => fs.realpath(device.path),
type: (device) => matchValue(device.type, {
partition: "PARTITION",
disk: "DISK",
loopDevice: "LOOP_DEVICE"
}),
size: "size",
mountpoint: "mountpoint", // FIXME: Isn't this obsoleted by `mounts`?
deviceNumber: "deviceNumber",
removable: "removable",
readOnly: "readOnly",
children: (device, { $make }) => device.children.map((child) => {
return $make("sysquery.blockDevices.BlockDevice", { name: child.name });
})
})
};
} else {
return InvalidObject;
}
}
},
extensions: {
"sysquery.mounts.Mount": {
sourceDevice: async function (_, { $make, $getProperty }) {
let sourceDevicePath = await $getProperty(this, "sourceDevicePath");
if (sourceDevicePath == null) {
// This occurs when the mount is not backed by a device, eg. an sshfs FUSE mount
return null;
} else {
return $make("sysquery.blockDevices.BlockDevice", {
path: await fs.realpath(sourceDevicePath)
});
}
}
}
},
root: {
resources: {
blockDevices: ({ names, paths }, { lsblk, $make }) => {
// TODO: Design better abstraction for this sort of case
let selectors = (names == null && paths == null)
? null
: [
... (names ?? []).map((name) => ({ name: name })),
... (paths ?? []).map((path) => ({ path: path }))
];
return mapFromSource(lsblk, selectors, (device) => {
return $make("sysquery.blockDevices.BlockDevice", { name: device.name });
});
}
}
}
};