balena-cli
Version:
The official balena Command Line Interface
155 lines • 5.69 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.getQemuPath = exports.QEMU_BIN_NAME = exports.QEMU_VERSION = void 0;
exports.qemuPathInContext = qemuPathInContext;
exports.copyQemu = copyQemu;
exports.installQemuIfNeeded = installQemuIfNeeded;
const errors_1 = require("../errors");
const lazy_1 = require("./lazy");
exports.QEMU_VERSION = 'v7.0.0+balena1';
exports.QEMU_BIN_NAME = 'qemu-execve';
function qemuPathInContext(context) {
const path = require('path');
const binDir = path.join(context, '.balena');
const binPath = path.join(binDir, exports.QEMU_BIN_NAME);
return path.relative(context, binPath);
}
function copyQemu(context, arch) {
const path = require('path');
const fs = require('fs');
const binDir = path.join(context, '.balena');
const binPath = path.join(binDir, exports.QEMU_BIN_NAME);
return fs.promises
.mkdir(binDir)
.catch(function (err) {
if (err.code === 'EEXIST') {
return;
}
throw err;
})
.then(() => (0, exports.getQemuPath)(arch))
.then((qemu) => new Promise(function (resolve, reject) {
const read = fs.createReadStream(qemu);
const write = fs.createWriteStream(binPath);
read
.on('error', reject)
.pipe(write)
.on('error', reject)
.on('finish', resolve);
}))
.then(() => fs.promises.chmod(binPath, '755'))
.then(() => path.relative(context, binPath));
}
const getQemuPath = function (balenaArch) {
const qemuArch = balenaArchToQemuArch(balenaArch);
const balena = (0, lazy_1.getBalenaSdk)();
const path = require('path');
const { promises: fs } = require('fs');
return balena.settings.get('binDirectory').then((binDir) => fs
.mkdir(binDir)
.catch(function (err) {
if (err.code === 'EEXIST') {
return;
}
throw err;
})
.then(() => path.join(binDir, `${exports.QEMU_BIN_NAME}-${qemuArch}-${exports.QEMU_VERSION}`)));
};
exports.getQemuPath = getQemuPath;
async function installQemu(arch, qemuPath) {
const qemuArch = balenaArchToQemuArch(arch);
const fileVersion = exports.QEMU_VERSION.replace('v', '').replace('+', '.');
const urlFile = encodeURIComponent(`qemu-${fileVersion}-${qemuArch}.tar.gz`);
const urlVersion = encodeURIComponent(exports.QEMU_VERSION);
const qemuUrl = `https://github.com/balena-io/qemu/releases/download/${urlVersion}/${urlFile}`;
const request = await Promise.resolve().then(() => require('request'));
const fs = await Promise.resolve().then(() => require('fs'));
const zlib = await Promise.resolve().then(() => require('zlib'));
const tar = await Promise.resolve().then(() => require('tar-stream'));
const installStream = fs.createWriteStream(qemuPath);
try {
await new Promise((resolve, reject) => {
const extract = tar.extract();
extract.on('entry', function (header, stream, next) {
try {
stream.on('end', next);
if (header.name.includes(`qemu-${qemuArch}-static`)) {
stream.pipe(installStream);
}
else {
stream.resume();
}
}
catch (err) {
reject(err);
}
});
request(qemuUrl)
.on('error', reject)
.pipe(zlib.createGunzip())
.on('error', reject)
.pipe(extract)
.on('error', reject)
.on('finish', function () {
fs.chmodSync(qemuPath, '755');
resolve();
});
});
}
catch (err) {
try {
await fs.promises.unlink(qemuPath);
}
catch (_a) {
}
throw err;
}
}
const balenaArchToQemuArch = function (arch) {
switch (arch) {
case 'rpi':
case 'arm':
case 'armhf':
case 'armv7hf':
return 'arm';
case 'arm64':
case 'aarch64':
return 'aarch64';
default:
throw new errors_1.ExpectedError((0, lazy_1.stripIndent) `
Unknown ARM architecture identifier "${arch}".
Known ARM identifiers: rpi arm armhf armv7hf arm64 aarch64`);
}
};
async function installQemuIfNeeded(emulated, logger, arch, docker) {
const needsQemu = await platformNeedsQemu(docker, logger);
if (!emulated || !needsQemu) {
return false;
}
const { promises: fs } = await Promise.resolve().then(() => require('fs'));
const qemuPath = await (0, exports.getQemuPath)(arch);
try {
const stats = await fs.stat(qemuPath);
if (stats.size === 0) {
await fs.unlink(qemuPath);
}
await fs.access(qemuPath);
}
catch (_a) {
logger.logInfo(`Installing qemu for ${arch} emulation...`);
await installQemu(arch, qemuPath);
}
return true;
}
async function platformNeedsQemu(docker, logger) {
const dockerInfo = await docker.info();
const isDockerDesktop = /(?:Docker Desktop)|(?:Docker for Mac)/i.test(dockerInfo.OperatingSystem);
if (isDockerDesktop) {
logger.logInfo((0, lazy_1.stripIndent) `
Docker Desktop detected (daemon architecture: "${dockerInfo.Architecture}")
Docker itself will determine and enable architecture emulation if required,
without balena-cli intervention and regardless of the --emulated option.`);
}
return !isDockerDesktop;
}
//# sourceMappingURL=qemu.js.map
;