diff --git a/src/apps.js b/src/apps.js index 553de4531..66703bbe9 100644 --- a/src/apps.js +++ b/src/apps.js @@ -2177,6 +2177,11 @@ async function updateApp(app, data, auditSource) { const hasSso = !!updateConfig.manifest.addons?.proxyAuth || !!updateConfig.manifest.addons?.ldap || !!manifest.addons?.oidc; if (!hasSso && app.sso) values.sso = false; // turn off sso flag, if the update removes sso options + // 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 task = { args: { updateConfig }, values diff --git a/src/apptask.js b/src/apptask.js index 12f8128ff..f6fdd575e 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -221,6 +221,9 @@ async function downloadImage(manifest) { // skip for relay app if (manifest.id === constants.PROXY_APP_APPSTORE_ID) return; + // only download non-local images, local ones are built locally + if (manifest.dockerImage.indexOf('local/') === 0) return; + const info = await docker.info(); const [dfError, diskUsage] = await safe(df.file(info.DockerRootDir)); if (dfError) throw new BoxError(BoxError.FS_ERROR, `Error getting file system info: ${dfError.message}`); @@ -346,11 +349,8 @@ 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); } - // 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: 40, message: 'Downloading image' }); + await downloadImage(app.manifest); await progressCallback({ percent: 50, message: 'Creating app data directory' }); await createAppDir(app); @@ -682,6 +682,12 @@ async function updateCommand(app, args, progressCallback) { await progressCallback({ percent: 60, message: 'Updating addons' }); await services.setupAddons(app, updateConfig.manifest.addons); + // now we have the local package tarball, so lets build + if (app.manifest.dockerImage.indexOf('local/') === 0) { + await progressCallback({ percent: 65, message: 'Building image' }); + await buildLocalImage(app); + } + await progressCallback({ percent: 70, message: 'Creating container' }); await createContainer(app); diff --git a/src/routes/apps.js b/src/routes/apps.js index b20c35304..5370bcec1 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -691,10 +691,10 @@ async function restart(req, res, next) { } async function update(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); + assert(typeof req.body === 'object' || typeof req.fields === 'object'); assert.strictEqual(typeof req.resources.app, 'object'); - const data = req.body; + const data = req.body || req.fields; // atleast one if ('manifest' in data && typeof data.manifest !== 'object') return next(new HttpError(400, 'manifest must be an object')); @@ -713,6 +713,13 @@ async function update(req, res, next) { data.appStoreId = appStoreId; data.manifest = manifest; + data.sourceArchiveFilePath = req.files.sourceArchive?.path || null; + + // if we have a source archive upload, craft a custom docker image URI for later + if (data.sourceArchiveFilePath) { + data.manifest.dockerImage = `local/${data.manifest.id}:${data.manifest.version}-${Date.now()}`; + } + [error, result] = await safe(apps.updateApp(req.resources.app, data, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/server.js b/src/server.js index b1ace00d8..99af1d31e 100644 --- a/src/server.js +++ b/src/server.js @@ -298,7 +298,7 @@ async function initializeExpressSync() { router.post('/api/v1/apps/:id/configure/upstream_uri', json, token, routes.apps.load, authorizeOperator, routes.apps.setUpstreamUri); router.post('/api/v1/apps/:id/repair', json, token, routes.apps.load, authorizeOperator, routes.apps.repair); router.post('/api/v1/apps/:id/check_update', json, token, routes.apps.load, authorizeOperator, routes.apps.checkUpdate); - router.post('/api/v1/apps/:id/update', json, token, routes.apps.load, authorizeOperator, routes.apps.update); + router.post('/api/v1/apps/:id/update', jsonOptional, token, multipart, routes.apps.load, authorizeOperator, routes.apps.update); router.post('/api/v1/apps/:id/restore', json, token, routes.apps.load, authorizeOperator, routes.apps.restore); router.post('/api/v1/apps/:id/import', json, token, routes.apps.load, authorizeOperator, routes.apps.importApp); router.post('/api/v1/apps/:id/export', json, token, routes.apps.load, authorizeOperator, routes.apps.exportApp);