From 939ac84c97521899ca20dd712e8be9e1ebabbcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Tue, 15 Nov 2022 18:51:37 +0100 Subject: [PATCH] Add manifest and config For showing the raw manifest and the image config, both in json. Add basic pretty-printing, for advanced topics one can use "jq". --- README.md | 3 +++ undocker.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/README.md b/README.md index 0714eba..8b798aa 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,11 @@ Unpacks a Docker image. --verbose, -v --debug, -d --list, --ls List layers in an image + --manifest, -m Dump manifest contained in archive --layer LAYER, -l LAYER Extract only the specified layer + --config, -c Dump config contained in archive + --pretty, -p Pretty print the json dump output ## Examples diff --git a/undocker.py b/undocker.py index 4384372..b6f5e55 100644 --- a/undocker.py +++ b/undocker.py @@ -31,15 +31,24 @@ def parse_args(): p.add_argument('--layers', action='store_true', help='List layers in an image') + p.add_argument('--config', '-c', + action='store_true', + help='Dump config contained in archive') p.add_argument('--list', '--ls', action='store_true', help='List images/tags contained in archive') + p.add_argument('--manifest', '-m', + action='store_true', + help='Dump manifest contained in archive') p.add_argument('--layer', '-l', action='append', help='Extract only the specified layer') p.add_argument('--no-whiteouts', '-W', action='store_true', help='Do not process whiteout (.wh.*) files') + p.add_argument('--pretty', '-p', + action='store_true', + help='Pretty print the json dump output') g = p.add_argument_group('Logging options') g.add_argument('--verbose', '-v', @@ -95,6 +104,10 @@ def main(): args = parse_args() logging.basicConfig(level=args.loglevel) + pretty = {} + if args.pretty: + pretty = {"indent": 4} + with tempfile.NamedTemporaryFile() as fd, ( open(args.image, 'rb') if args.image else io.open(sys.stdin.fileno(), 'rb')) as image: @@ -124,6 +137,34 @@ def main(): LOG.error('No image name specified and multiple ' 'images contained in archive') sys.exit(1) + if args.manifest or args.config: + try: + mj = img.extractfile('manifest.json') + manifest = json.loads(mj.read().decode('utf-8')) + except KeyError: + LOG.error('failed to find manifest.json') + sys.exit(1) + if args.manifest: + print(json.dumps(manifest, **pretty)) + sys.exit(0) + configs = [] + repotag = None + if args.image: + name, tag = parse_image_spec(args.image) + repotag = "%s:%s" % (name, tag) + for image in manifest: + if repotag and repotag not in image['RepoTags']: + continue + try: + cj = img.extractfile(image['Config']) + config = json.loads(cj.read().decode('utf-8')) + except KeyError: + LOG.error('failed to read config json') + sys.exit(1) + configs.append(config) + if args.config: + print(json.dumps(configs, **pretty)) + sys.exit(0) try: top = repos[name][tag]