diff --git a/src/docker.js b/src/docker.js index d122ec37a..b26742742 100644 --- a/src/docker.js +++ b/src/docker.js @@ -586,7 +586,8 @@ async function deleteImage(imageRef) { // registry v1 used to pull down all *tags*. this meant that deleting image by tag was not enough (since that // just removes the tag). we used to remove the image by id. this is not required anymore because aliases are // not created anymore after https://github.com/docker/docker/pull/10571 - const [error] = await safe(gConnection.getImage(imageRef).remove(removeOptions)); + debug(`deleteImage: removing ${imageRef}`); + const [error] = await safe(gConnection.getImage(imageRef.replace(/@sha256:.*/,'')).remove(removeOptions)); // can't have the manifest id. won't remove anythin if (error && error.statusCode === 400) return; // invalid image format. this can happen if user installed with a bad --docker-image if (error && error.statusCode === 404) return; // not found if (error && error.statusCode === 409) return; // another container using the image diff --git a/src/mailserver.js b/src/mailserver.js index 61c13d473..8e8769f0f 100644 --- a/src/mailserver.js +++ b/src/mailserver.js @@ -213,6 +213,8 @@ async function start(existingInfra) { debug('startMail: starting'); await restart(); + + if (existingInfra.version !== 'none' && existingInfra.images.mail !== infra.images.mail) await docker.deleteImage(existingInfra.images.mail); } async function restartIfActivated() { diff --git a/src/platform.js b/src/platform.js index a031a9b65..2efb3d6ff 100644 --- a/src/platform.js +++ b/src/platform.js @@ -22,7 +22,6 @@ const apps = require('./apps.js'), dashboard = require('./dashboard.js'), database = require('./database.js'), debug = require('debug')('box:platform'), - docker = require('./docker.js'), dockerProxy = require('./dockerproxy.js'), fs = require('fs'), infra = require('./infra_version.js'), @@ -46,37 +45,6 @@ function getStatus() { return { message: gStatusMessage }; } -async function pruneInfraImages() { - debug('pruneInfraImages: checking existing images'); - - // cannot blindly remove all unused images since redis image may not be used - const imageRefs = Object.keys(infra.images).map(addon => infra.images[addon]); - const [error, output] = await safe(shell.bash('docker images --digests --format "{{.ID}} {{.Repository}} {{.Tag}} {{.Digest}}"', { encoding: 'utf8' })); - if (error) { - debug(`Failed to list images ${error.message}`); - throw error; - } - const lines = output.trim().split('\n'); - - for (const imageRef of imageRefs) { - const parsedRef = docker.parseImageRef(imageRef); - - for (const line of lines) { - if (!line) continue; - const [, repo, tag, digest] = line.split(' '); // [ ID, Repo, Tag, Digest ] - const normalizedRepo = repo.replace('registry.ipv6.docker.com/', '').replace('registry-1.docker.io/', '').replace('registry.docker.com/', ''); - if (!parsedRef.fullRepositoryName.endsWith(normalizedRepo)) continue; // some other repo - if (imageRef === `${repo}:${tag}@${digest}`) continue; // the image we want to keep - - const imageIdToPrune = tag === '' ? `${repo}@${digest}` : `${repo}:${tag}`; // untagged, use digest - console.log(`pruneInfraImages: removing unused image of ${imageRef}: ${imageIdToPrune}`); - - const [error] = await safe(shell.spawn('docker', [ 'rmi', imageIdToPrune ], {})); - if (error) console.log(`Error removing image ${imageIdToPrune}: ${error.mesage}`); - } - } -} - async function pruneVolumes() { debug('pruneVolumes: remove all unused local volumes'); @@ -138,11 +106,7 @@ async function onInfraReady(infraChanged) { debug(`onInfraReady: platform is ready. infra changed: ${infraChanged}`); gStatusMessage = 'Ready'; - if (infraChanged) { - await safe(pruneInfraImages(), { debug }); // ignore error - await safe(pruneVolumes(), { debug }); // ignore error - } - + if (infraChanged) await safe(pruneVolumes(), { debug }); // ignore error await apps.schedulePendingTasks(AuditSource.PLATFORM); await appTaskManager.start(); } diff --git a/src/services.js b/src/services.js index 55cb5cfac..25ea813b9 100644 --- a/src/services.js +++ b/src/services.js @@ -1035,6 +1035,8 @@ async function startTurn(existingInfra) { debug('startTurn: starting turn container'); await shell.bash(runCmd, {}); + + if (existingInfra.version !== 'none' && existingInfra.images.turn !== image) await docker.deleteImage(existingInfra.images.turn); } async function teardownTurn(app, options) { @@ -1248,6 +1250,8 @@ async function startMysql(existingInfra) { await waitForContainer('mysql', 'CLOUDRON_MYSQL_TOKEN'); if (upgrading) await importDatabase('mysql'); } + + if (existingInfra.version !== 'none' && existingInfra.images.mysql !== image) await docker.deleteImage(existingInfra.images.mysql); } async function setupMySql(app, options) { @@ -1466,6 +1470,8 @@ async function startPostgresql(existingInfra) { await waitForContainer('postgresql', 'CLOUDRON_POSTGRESQL_TOKEN'); if (upgrading) await importDatabase('postgresql'); } + + if (existingInfra.version !== 'none' && existingInfra.images.postgresql !== image) await docker.deleteImage(existingInfra.images.postgresql); } async function setupPostgreSql(app, options) { @@ -1615,6 +1621,8 @@ async function startMongodb(existingInfra) { await waitForContainer('mongodb', 'CLOUDRON_MONGODB_TOKEN'); if (upgrading) await importDatabase('mongodb'); } + + if (existingInfra.version !== 'none' && existingInfra.images.mongodb !== image) await docker.deleteImage(existingInfra.images.mongodb); } async function setupMongoDb(app, options) { @@ -1782,6 +1790,8 @@ async function startGraphite(existingInfra) { debug('startGraphite: starting graphite container'); await shell.bash(runCmd, {}); + if (existingInfra.version !== 'none' && existingInfra.images.graphite !== image) await docker.deleteImage(existingInfra.images.graphite); + // restart collectd to get the disk stats after graphite starts. currently, there is no way to do graphite health check setTimeout(async () => await safe(shell.promises.sudo([ RESTART_SERVICE_CMD, 'collectd' ], {})), 60000); } @@ -1872,6 +1882,8 @@ async function startRedis(existingInfra) { } if (upgrading) await importDatabase('redis'); + + if (existingInfra.version !== 'none' && existingInfra.images.redis !== image) await docker.deleteImage(existingInfra.images.redis); } // Ensures that app's addon redis container is running. Can be called when named container already exists/running diff --git a/src/sftp.js b/src/sftp.js index 8988924a6..2985d420a 100644 --- a/src/sftp.js +++ b/src/sftp.js @@ -125,6 +125,8 @@ async function start(existingInfra) { debug('startSftp: starting sftp container'); await shell.bash(runCmd, {}); + + if (existingInfra.version !== 'none' && existingInfra.images.sftp !== image) await docker.deleteImage(existingInfra.images.sftp); } async function status() {