Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bolt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ Usage:

bolt run <remote> <package-name>
Execute a bolt package on a remote device
--develop Run with elevated privileges to simplify debugging
--clear-storage Clear persistent storage before running the package
--uid=<uid> Run with the specified user ID
--gid=<gid> Run with the specified group ID
--userns=<true/false> Enable/disable user namespace

Where:
target Basename of a file named <target>.bolt.json, which defines build instructions
Expand Down
9 changes: 7 additions & 2 deletions bolt/src/bolt.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const { diff } = require('./diff.cjs');
const { extract } = require('./extract.cjs');
const { pack } = require('./pack.cjs');
const { push } = require('./push.cjs');
const { run } = require('./run.cjs');
const { run, runOptions } = require('./run.cjs');
const { make, makeOptions } = require('./make.cjs');

function help() {
Expand All @@ -47,6 +47,11 @@ Usage:

bolt run <remote> <package-name>
Execute a bolt package on a remote device
--develop Run with elevated privileges to simplify debugging
--clear-storage Clear persistent storage before running the package
--uid=<uid> Run with the specified user ID
--gid=<gid> Run with the specified group ID
--userns=<true/false> Enable/disable user namespace

Where:
target Basename of a file named <target>.bolt.json, which defines build instructions
Expand All @@ -72,7 +77,7 @@ const commands = {
extract: { args: 2, handler: extract },
pack: { args: 2, handler: pack },
push: { args: 2, handler: push },
run: { args: 2, handler: run },
run: { args: 2, handler: run, options: runOptions },
make: { args: 1, handler: make, options: makeOptions },
};

Expand Down
3 changes: 3 additions & 0 deletions bolt/src/config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ exports.REMOTE_BUNDLES_DIR = "/data/bolt/bundles";
exports.REMOTE_GPU_LAYER_FS = "/usr/share/gpu-layer/rootfs";
exports.REMOTE_GPU_CONFIG = "/usr/share/gpu-layer/config.json";
exports.AI2_MANAGERS_ENABLED_FILE = "/opt/ai2managers";
// select random UID and GID (34567) to avoid conflicts with existing users/groups
exports.DEFAULT_UID = 34567;
exports.DEFAULT_GID = 34567;
80 changes: 74 additions & 6 deletions bolt/src/run.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,20 @@ function setupResources(remote, pkg) {
}
}

function prepareBundle(remote, pkg, bundleConfig, layers) {
function prepareBundle(remote, pkg, bundleConfig, layers, options) {
const bundleDir = remote.getPkgBundleDir(pkg);
const bundleRootfsDir = bundleDir + "/rootfs";

if (remote.isMounted(bundleRootfsDir)) {
remote.unmount(bundleRootfsDir);
}

/* remove mount points inside /usr/lib in rw overlay to cleanup leftovers from previous runs */
remote.rmdir(`${bundleDir}/rw/upper/usr/lib`);
if (options.clearStorage) {
remote.rmdir(`${bundleDir}`);
}

remote.mkdir(`${bundleRootfsDir} ${bundleDir}/rw/{upper,work}`);
remote.exec(`chmod 777 ${bundleDir}/rw/{upper,work}`);

remote.exec(`mount -t overlay overlay -o lowerdir=${layers.join(":")},upperdir=${bundleDir}/rw/upper,workdir=${bundleDir}/rw/work ${bundleRootfsDir}`);
remote.storeObject(`${bundleDir}/config.json`, bundleConfig);
Expand Down Expand Up @@ -251,7 +253,7 @@ function addDeviceGPULayer(remote, bundleConfig, layerDirs) {
return result;
}

function run(remoteName, pkg) {
function run(remoteName, pkg, options) {
const remote = new Remote(remoteName);

const configs = getConfigs(remote, pkg);
Expand All @@ -260,7 +262,7 @@ function run(remoteName, pkg) {
console.log(`Running ${pkg} using:`);
console.log(`${JSON.stringify(configs, null, 2)}`);

const bundleConfig = makeTemplate();
const bundleConfig = makeTemplate(options);
for (const { pkg, config } of configs) {
if (config.entryPoint) {
bundleConfig.process.args.push(config.entryPoint);
Expand Down Expand Up @@ -308,7 +310,7 @@ function run(remoteName, pkg) {
}

layerDirs.reverse();
prepareBundle(remote, pkg, bundleConfig, layerDirs);
prepareBundle(remote, pkg, bundleConfig, layerDirs, options);

if (!remote.socketExists(rialtoSocketPath)) {
console.warn('\n\n\x1b[31mRialto socket not available! Playback not supported!\x1b[0m\n\n');
Expand All @@ -322,3 +324,69 @@ function run(remoteName, pkg) {
}

exports.run = run;

exports.runOptions = {
develop(params, result) {
if (params.options.develop === "") {
Object.assign(result, {
uid: result.uid ?? 0,
gid: result.gid ?? 0,
userns: result.userns ?? false,
});
return true;
}
return false;
},

uid(params, result) {
if (params.options.uid) {
Object.assign(result, {
uid: +params.options.uid,
});
return true;
}
return false;
},

gid(params, result) {
if (params.options.gid) {
Object.assign(result, {
gid: +params.options.gid,
});
return true;
}
return false;
},

userns(params, result) {
const userns = params.options.userns;
let value;

switch (userns) {
case "true":
value = true;
break;
case "false":
value = false;
break;
default:
return false;
}

Object.assign(result, {
userns: value,
});

return true;
},

"clear-storage"(params, result) {
if (params.options["clear-storage"] === "") {
Object.assign(result, {
clearStorage: true,
});
return true;
}
return false;
},
};
62 changes: 42 additions & 20 deletions bolt/src/runtime-config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
* limitations under the License.
*/

const config = require('./config.cjs');

const template = {
"ociVersion": "1.0.2",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
"uid": config.DEFAULT_UID,
"gid": config.DEFAULT_GID,
},
"args": [
],
Expand Down Expand Up @@ -197,18 +199,8 @@ const template = {
},
"linux": {
"uidMappings": [
{
"containerID": 0,
"hostID": 1000,
"size": 1
}
],
"gidMappings": [
{
"containerID": 0,
"hostID": 1000,
"size": 1
}
],
"namespaces": [
{
Expand Down Expand Up @@ -255,8 +247,33 @@ const template = {
};


function makeTemplate() {
return template;
function makeTemplate(options) {
const result = JSON.parse(JSON.stringify(template));

if (options.uid !== undefined || options.gid !== undefined) {
result.process.user.uid = options.uid ?? config.DEFAULT_UID;
result.process.user.gid = options.gid ?? config.DEFAULT_GID;
}

if (options.userns ?? true) {
result.linux.namespaces.push({
type: "user",
});

result.linux.uidMappings.push({
containerID: result.process.user.uid,
hostID: result.process.user.uid,
size: 1,
});

result.linux.gidMappings.push({
containerID: result.process.user.gid,
hostID: result.process.user.gid,
size: 1,
});
}

return result;
}

function addDevice(config, path, type, major, minor) {
Expand Down Expand Up @@ -286,12 +303,17 @@ function hexToNumber(str) {
throw new Error(`Cannot parse ${str} as hex number`);
}

function processDevNodeEntry(remote, config, devNode) {
function processDevNodeEntry(remote, config, devNode, groupIds) {
try {
const [perm, majorHex, minorHex] = remote.exec(`stat -c '%A:%t:%T' ${devNode}`).trim().split(":");
const type = perm[0];
if (type === "c" || type === "b") {
addDevice(config, devNode, type, hexToNumber(majorHex), hexToNumber(minorHex));
const [perm, majorHex, minorHex, groupName] = remote.exec(`stat -c '%A:%t:%T:%G' ${devNode}`).trim().split(":");
if (perm.length === 10 && (perm[0] === "c" || perm[0] === "b")) {
addDevice(config, devNode, perm[0], hexToNumber(majorHex), hexToNumber(minorHex));
if (perm[7] === "r" && perm[8] === "w") {
} else if (groupIds.includes(groupName) && perm[4] === "r" && perm[5] === "w") {
} else {
console.warn(`Changing access rights for ${devNode}! (was ${perm}, group: ${groupName})`);
remote.exec(`chmod a+rw ${devNode}`);
}
} else {
throw new Error(`not a device (perm: ${perm})`);
}
Expand Down Expand Up @@ -352,7 +374,7 @@ function applyGPUConfig(remote, config, gpuConfig) {
const groupIds = gpuConfig?.vendorGpuSupport?.groupIds ?? [];

for (let node of devNodes) {
processDevNodeEntry(remote, config, node);
processDevNodeEntry(remote, config, node, groupIds);
}
for (let file of files) {
processFileEntry(config, file);
Expand Down