Skip to content

Instantly share code, notes, and snippets.

@allbinmani
Created April 16, 2016 06:08
Show Gist options
  • Save allbinmani/3510de25560b6ca522935c8727a26acd to your computer and use it in GitHub Desktop.
Save allbinmani/3510de25560b6ca522935c8727a26acd to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
/**
* Crude script to find images that are lingering and/or missing in the docker registry 2.0
*
* Finds all unreferences images in all repos, given the path to the registry-data
* directory (the one that contains 'storage'). Also lists all images that are referenced,
* but are missing on disk.
*
* License: GPL 2.0
* Author: Mattias Nilsson <mattias@allbinary.se>
*
* Changelog:
* - 2016-04-15: Initial revision
*
*/
var fs = require('fs');
var path = require('path');
var regDataDir = process.argv[2];
if(!regDataDir) {
console.error('Specify registry-data directory as argument');
process.exit(1);
}
var storageDir = path.join(regDataDir, 'storage');
var reposDir = path.join(storageDir, 'repositories');
var imagesDir = path.join(storageDir, 'images');
var numRepos = 0;
var _imageIndex = {};
function formatImageList(imageList) {
return imageList.map(function(img_id) {
return img_id + (_imageIndex[img_id] ? ' ( ' + _imageIndex[img_id].join(',') + ' )' : '');
}).join('\n');
}
fs.readdir(reposDir, function(err, files) {
if(err) {
console.error('failed to scan repositories directory %s: %s', reposDir, err);
process.exit(1);
}
function imageExists(image_id) {
var image_dir = path.join(imagesDir, image_id);
var json_file = path.join(image_dir, 'json');
try {
var stat = fs.statSync(image_dir);
if(!stat || !stat.isDirectory()) {
return false;
}
stat = fs.statSync(json_file);
return stat && stat.isFile();
} catch(e) {
return false;
}
}
Promise.all(
files.filter(function(f) {
var stat = fs.statSync(path.join(reposDir, f));
return stat && stat.isDirectory();
}).map(function(subName) {
var subDir = path.join(reposDir, subName);
return new Promise(function(resolve, reject) {
fs.readdir(subDir, function(err, repoList) {
if(err) {
return reject(err);
}
numRepos++;
resolve({path: subDir,
name: subName,
repos: repoList.map(function(repoName) {
var imgsFile = path.join(subDir, repoName, '_index_images');
var _index_images = JSON.parse(fs.readFileSync(imgsFile).toString());
return {name: repoName,
path: path.join(subDir, repoName),
index_images: _index_images.map(function(img) {
if(!_imageIndex[img.id]) { _imageIndex[img.id] = [];}
_imageIndex[img.id].push(repoName);
return {id: img.id, exists: imageExists(img.id)};
})
};
})
});
});
}).then(function(x) {
return x;
});
})
).then(function(result) {
var _usedImages = {};
var allUsedImages = result.map(function(sub) {
return sub.repos.map(function(repo) {
return repo.index_images.map(function(im) {
_usedImages[im.id] = true;
return im.id;
});
});
}).reduce(function(a, b) {
return a.concat(b);
}, []).reduce(function(a, b) {
return a.concat(b);
}, []);
fs.readdir(imagesDir, function(err, image_dir_list) {
var unusedImages = image_dir_list.filter(function(img_dir) {
return !_usedImages[img_dir];
});
var missingImages = Object.keys(_usedImages).filter(function(img_id) {
return image_dir_list.indexOf(img_id) === -1;
});
console.log('Scanned %d repositories', numRepos);
console.log('- total # of images: %d', image_dir_list.length);
console.log('- referenced images: %d', Object.keys(_usedImages).length);
console.log('- unreferenced images: %d', unusedImages.length);
if(unusedImages.length > 0) {
console.log(formatImageList(unusedImages));
}
console.log('- missing images: %d', missingImages.length);
if(missingImages.length > 0) {
console.log(formatImageList(missingImages));
}
process.exit(0);
});
}).catch(function(err) {
console.error('Error occurred: %s', err);
process.exit(1);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment