import hashlib import json import pathlib import subprocess import sys import tarfile import tempfile def config_digest(archive): with tarfile.open(archive) as image: manifests = json.load(image.extractfile("manifest.json")) if len(manifests) != 1: raise ValueError("expected exactly one image in the archive") manifest = manifests[0] config = image.extractfile(manifest["Config"]).read() layers = json.loads(config)["rootfs"]["diff_ids"] if len(layers) != len(manifest["Layers"]): raise ValueError("archive layer count differs from image config") for path, expected in zip(manifest["Layers"], layers): digest = hashlib.sha256() with image.extractfile(path) as layer: for chunk in iter(lambda: layer.read(1024 * 1024), b""): digest.update(chunk) if "sha256:" + digest.hexdigest() != expected: raise ValueError("archive layer differs from image config") return "sha256:" + hashlib.sha256(config).hexdigest() if __name__ == "__main__": with tempfile.TemporaryDirectory() as tmp: archive = pathlib.Path(tmp) / "image.tar" subprocess.run(["docker", "image", "save", "-o", str(archive), sys.argv[1]], check=True) print(config_digest(archive))