docker: rework image pruning
with our new retagging approach, the Digest ID remains <null> because this is only set by docker if truly fetched from the registry. this means that redis container always gets removed...
This commit is contained in:
+2
-1
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+1
-37
@@ -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 === '<none>' ? `${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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user