Keep the app source archive with the app instance data dir
This commit is contained in:
@@ -1486,6 +1486,11 @@ async function install(data, auditSource) {
|
||||
const appId = crypto.randomUUID();
|
||||
debug(`Installing app ${appId}`);
|
||||
|
||||
// if we have a sourceArchive rename it to appId to be picked up later in the apptask
|
||||
if (data.sourceArchiveFilePath) {
|
||||
if (!safe.fs.renameSync(data.sourceArchiveFilePath, `/tmp/${appId}.tar.gz`)) throw new BoxError(BoxError.FS_ERROR, 'Error moving source archive');
|
||||
}
|
||||
|
||||
const app = {
|
||||
accessRestriction,
|
||||
operators,
|
||||
|
||||
+40
-6
@@ -25,6 +25,7 @@ const apps = require('./apps.js'),
|
||||
docker = require('./docker.js'),
|
||||
ejs = require('ejs'),
|
||||
fs = require('node:fs'),
|
||||
fsPromises = require('node:fs').promises,
|
||||
iputils = require('./iputils.js'),
|
||||
manifestFormat = require('@cloudron/manifest-format'),
|
||||
os = require('node:os'),
|
||||
@@ -226,11 +227,35 @@ async function downloadImage(manifest) {
|
||||
|
||||
if (diskUsage.available < (1024*1024*1024)) throw new BoxError(BoxError.DOCKER_ERROR, `Not enough disk space to pull docker image. available: ${diskUsage.available}`);
|
||||
|
||||
if (manifest.dockerImage.indexOf('local/') === 0) {
|
||||
await docker.buildImage(manifest);
|
||||
} else {
|
||||
await docker.downloadImage(manifest);
|
||||
await docker.downloadImage(manifest);
|
||||
}
|
||||
|
||||
async function buildLocalImage(app) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
|
||||
// TODO some precondition checks like downloadImage maybe
|
||||
|
||||
const sourceFilePath = path.join(paths.APPS_DATA_DIR, app.id, 'source.tar.gz');
|
||||
|
||||
// if we have a newly uploaded source archive, use that
|
||||
const uploadedSourceArchiveFilePath = `/tmp/${app.id}.tar.gz`;
|
||||
if (fs.existsSync(uploadedSourceArchiveFilePath)) {
|
||||
const [renameError] = await safe(fsPromises.rename(uploadedSourceArchiveFilePath, sourceFilePath));
|
||||
if (renameError) {
|
||||
if (renameError.code === 'EXDEV') {
|
||||
// Cross-device rename not permitted, so copy and remove
|
||||
let [error] = await safe(fsPromises.copyFile(uploadedSourceArchiveFilePath, sourceFilePath));
|
||||
if (error) throw new BoxError(BoxError.FS_ERROR, error);
|
||||
|
||||
[error] = await safe(fsPromises.unlink(uploadedSourceArchiveFilePath));
|
||||
if (error) throw new BoxError(BoxError.FS_ERROR, error);
|
||||
} else {
|
||||
throw new BoxError(BoxError.FS_ERROR, renameError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await docker.buildImage(app.manifest.dockerImage, sourceFilePath);
|
||||
}
|
||||
|
||||
async function updateChecklist(app, newChecks, acknowledged = false) {
|
||||
@@ -321,8 +346,11 @@ async function installCommand(app, args, progressCallback) {
|
||||
await dns.registerLocations([ { subdomain: app.subdomain, domain: app.domain }].concat(app.secondaryDomains).concat(app.redirectDomains).concat(app.aliasDomains), { overwriteDns }, progressCallback);
|
||||
}
|
||||
|
||||
await progressCallback({ percent: 40, message: 'Downloading image' });
|
||||
await downloadImage(app.manifest);
|
||||
// only download non-local images, local ones are built after restore
|
||||
if (app.manifest.dockerImage.indexOf('local/') !== 0) {
|
||||
await progressCallback({ percent: 40, message: 'Downloading image' });
|
||||
await downloadImage(app.manifest);
|
||||
}
|
||||
|
||||
await progressCallback({ percent: 50, message: 'Creating app data directory' });
|
||||
await createAppDir(app);
|
||||
@@ -360,6 +388,12 @@ async function installCommand(app, args, progressCallback) {
|
||||
await services.restoreAddons(app, app.manifest.addons);
|
||||
}
|
||||
|
||||
// now we have the local package tarball, so lets build
|
||||
if (app.manifest.dockerImage.indexOf('local/') === 0) {
|
||||
await progressCallback({ percent: 75, message: 'Building image' });
|
||||
await buildLocalImage(app);
|
||||
}
|
||||
|
||||
await progressCallback({ percent: 80, message: 'Creating container' });
|
||||
await createContainer(app);
|
||||
|
||||
|
||||
+9
-10
@@ -160,16 +160,15 @@ async function pullImage(imageRef) {
|
||||
});
|
||||
}
|
||||
|
||||
async function buildImage(manifest) {
|
||||
assert.strictEqual(typeof manifest, 'object');
|
||||
async function buildImage(dockerImage, sourceArchiveFilePath) {
|
||||
assert.strictEqual(typeof dockerImage, 'string');
|
||||
assert.strictEqual(typeof sourceArchiveFilePath, 'string');
|
||||
|
||||
const sourceArchivePath = '/tmp/' + manifest.dockerImage.slice('local/'.length);
|
||||
debug(`buildImage: building ${dockerImage} from ${sourceArchiveFilePath}`);
|
||||
|
||||
debug(`buildImage: building ${manifest.dockerImage} from ${sourceArchivePath}`);
|
||||
|
||||
const tarStream = fs.createReadStream(sourceArchivePath);
|
||||
const [error, stream] = await safe(gConnection.buildImage(tarStream, { t: manifest.dockerImage }));
|
||||
if (error) throw new BoxError(BoxError.DOCKER_ERROR, `Unable to build image from ${sourceArchivePath}: ${error.message}`);
|
||||
const tarStream = fs.createReadStream(sourceArchiveFilePath);
|
||||
const [error, stream] = await safe(gConnection.buildImage(tarStream, { t: dockerImage }));
|
||||
if (error) throw new BoxError(BoxError.DOCKER_ERROR, `Unable to build image from ${sourceArchiveFilePath}: ${error.message}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let buildError = null;
|
||||
@@ -190,14 +189,14 @@ async function buildImage(manifest) {
|
||||
debug(`buildImage: error ${buildError}`);
|
||||
return reject(new BoxError(buildError.message.includes('no space') ? BoxError.FS_ERROR : BoxError.DOCKER_ERROR, buildError.message));
|
||||
} else {
|
||||
debug(`buildImage: success ${manifest.dockerImage}`);
|
||||
debug(`buildImage: success ${dockerImage}`);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
stream.on('error', (error) => {
|
||||
debug(`buildImage: error building image ${manifest.dockerImage}: %o`, error);
|
||||
debug(`buildImage: error building image ${dockerImage}: %o`, error);
|
||||
reject(new BoxError(BoxError.DOCKER_ERROR, error.message));
|
||||
});
|
||||
});
|
||||
|
||||
+3
-4
@@ -205,12 +205,11 @@ async function install(req, res, next) {
|
||||
|
||||
data.appStoreId = result.appStoreId;
|
||||
data.manifest = result.manifest;
|
||||
data.sourceArchiveFilePath = req.files.sourceArchive?.path || null;
|
||||
|
||||
// if we have a source archive upload, craft a custom docker image URI for later
|
||||
if (req.files.sourceArchive?.path) {
|
||||
const newFileName = `${data.manifest.id}:${data.manifest.version}-${Date.now()}`;
|
||||
if (!safe.fs.renameSync(req.files.sourceArchive?.path, `/tmp/${newFileName}`)) return next(new HttpError(500, 'unable to move source archive'));
|
||||
data.manifest.dockerImage = `local/${newFileName}`;
|
||||
if (data.sourceArchiveFilePath) {
|
||||
data.manifest.dockerImage = `local/${data.manifest.id}:${data.manifest.version}-${Date.now()}`;
|
||||
}
|
||||
|
||||
[error, result] = await safe(apps.install(data, AuditSource.fromRequest(req)));
|
||||
|
||||
Reference in New Issue
Block a user