Skip to content

Instantly share code, notes, and snippets.

@cwgem
Created July 2, 2018 00:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cwgem/78841cf7cef57cf564b7565d81fd2754 to your computer and use it in GitHub Desktop.
Save cwgem/78841cf7cef57cf564b7565d81fd2754 to your computer and use it in GitHub Desktop.
`docker save` output tar analysis

A basic look

You can use docker save to output a tar file of an image, potentially making it workable for analyzing the filesystem contents ala anti-virus scans and such:

$ docker pull microsoft/dotnet
$ docker save microsoft/dotnet --output dotnet.tar

So here we pull the official dotnet image from the docker store, then use docker save to push it to a tar file. After that taking a look shows:

$ ls -1p
4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/
58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/
7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/
9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/
9d32878ab9fe85ddbd2e18230e7fa9fe23a4d9a1231a7eaca6c668ad9ffd4244.json
a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/
ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/
ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/
manifest.json
repositories

So there are a bunch of weirdly named directories plus two JSON files and a repositories file. Repositories seems interesting:

{"microsoft/dotnet":{"latest":"58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635"}}

Given that latest points to one of the directories my guess is that it's the diff tail for that named tag. Out of further curiousity I decided to see what happend if I pulled down a tagged image then did a docker save on the image without specifying a tag:

$ docker pull microsoft/dotnet:2.1.1-runtime-stretch-slim
$ docker save microsoft/dotnet --output dotnet_tagged.tar

Now the end result directory comes out like this:

0bb0001e31bac78432d38bac6d4e8512c9dec172bd1be54fc8be2303884ba6c3/
26b54366ad86b0948ddf01aca72e1ac498b3412ea9c187263bda42b2ec7153c5.json
4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/
5169dbebd5a2f17f7e45a6fbedcd507de5504c045d448414c2ab8b8802dee058/
58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/
7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/
83bc496b3e6ec08b26041758650a33eee75ace3b0da7313445b26e879afced06/
9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/
9d32878ab9fe85ddbd2e18230e7fa9fe23a4d9a1231a7eaca6c668ad9ffd4244.json
a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/
cc0c2ba935e6ef0726e63108d2c744f8ee4abeba00e2e25f7570b07c8366f94f/
ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/
ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/
manifest.json
repositories

The number of folders and toplevel .json files has increased, and the repositories file?

{"microsoft/dotnet":{"2.1.1-runtime-stretch-slim":"cc0c2ba935e6ef0726e63108d2c744f8ee4abeba00e2e25f7570b07c8366f94f","latest":"58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635"}}

So because we don't specify a tag and we had both the latest and 2.1.1-runtime-stretch-slim tagged of the image we ended up with two entries. If you were to do:

docker save microsoft/dotnet:2.1.1-runtime-stretch-slim --output dotnet_tagged.tar

instead then the repositories file would be the same as before just that you'd have the tag instead of latest. As with before the 2.1.1-runtime-stretch-slim tagged entry also leads to a folder indicating it's probably the point in the list.

Layers

So you're probably wondering what the heck is going on. What's with all these directories? Well docker works with incremental builds. That way when you make a slight change to update a single package you don't have to upload the entire OS (assuming you've uploaded base images to the appropriate registry before). Layers are essentially just that with each having one of the following formats:

$ ls -1p 4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/
VERSION
json
layer.tar

VERSION here is the version of the JSON schema presented in json. Basically if you're writing tools around these folders you'll want to pin them to VERSION so you're not screwed trying to parse JSON you can't deal with. Now to look at the json file:

{
	"id": "4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc",
	"parent": "a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6",
	"created": "1970-01-01T00:00:00Z",
	"container_config": {
		"Hostname": "",
		"Domainname": "",
		"User": "",
		"AttachStdin": false,
		"AttachStdout": false,
		"AttachStderr": false,
		"Tty": false,
		"OpenStdin": false,
		"StdinOnce": false,
		"Env": null,
		"Cmd": null,
		"Image": "",
		"Volumes": null,
		"WorkingDir": "",
		"Entrypoint": null,
		"OnBuild": null,
		"Labels": null
	}
}

I've beautified it here for readability but the actual file is mostly minified JSON. There's not really a lot to work with here. Mostly you'll be interested in the "id" and "parent" values. The "id" is mostly a self identifier while the "parent" tells you another layer is required to make this work. Remember what I was talking about the repositories showing the tail value? Well that's the last layer that gets applied from the base to make it work. So let's start there and work our way up:

$ grep -o '"parent":"[^"]*"' 58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/json
"parent":"7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3"
$ grep -o '"parent":"[^"]*"' 7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/json
"parent":"4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc"
$ grep -o '"parent":"[^"]*"' 4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/json
"parent":"a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6"
$ grep -o '"parent":"[^"]*"' a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/json
"parent":"ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8"
$ grep -o '"parent":"[^"]*"' ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/json
"parent":"ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e"
$ grep -o '"parent":"[^"]*"' ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/json
"parent":"9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f"
$ grep -o '"parent":"[^"]*"' 9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/json
$

So as shown here you pretty much get:

9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f
ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e
ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8
a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6
4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc
7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3
58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635

As the order you need to apply your layers in order to get to the final image. In fact if you look at docker history output:

$ docker history microsoft/dotnet
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
9d32878ab9fe        4 days ago          /bin/sh -c dotnet help                          1.08GB
<missing>           4 days ago          /bin/sh -c #(nop)  ENV ASPNETCORE_URLS=http:…   0B
<missing>           4 days ago          /bin/sh -c curl -SL --output dotnet.tar.gz h…   337MB
<missing>           4 days ago          /bin/sh -c #(nop)  ENV DOTNET_SDK_VERSION=2.…   0B
<missing>           4 days ago          /bin/sh -c apt-get update     && apt-get ins…   32.5MB
<missing>           5 days ago          /bin/sh -c apt-get update && apt-get install…   142MB
<missing>           5 days ago          /bin/sh -c set -ex;  if ! command -v gpg > /…   7.8MB
<missing>           5 days ago          /bin/sh -c apt-get update && apt-get install…   23.2MB
<missing>           5 days ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           5 days ago          /bin/sh -c #(nop) ADD file:f21d7c14104d5d9fa…   101MB

Then compare it with the filesize of layer.tar in each of the layer folders:

$ docker history --human="false" --format "{{.ID}} | {{.CreatedSince}} | {{.CreatedBy}} | {{.Size}}" microsoft/dotnet
9d32878ab9fe | 2018-06-27T11:18:56-05:00 | /bin/sh -c dotnet help | 1081967095
<missing> | 2018-06-27T11:18:05-05:00 | /bin/sh -c #(nop)  ENV ASPNETCORE_URLS=http:… | 0
<missing> | 2018-06-27T11:18:04-05:00 | /bin/sh -c curl -SL --output dotnet.tar.gz h… | 336706535
<missing> | 2018-06-27T11:17:08-05:00 | /bin/sh -c #(nop)  ENV DOTNET_SDK_VERSION=2.… | 0
<missing> | 2018-06-27T11:17:08-05:00 | /bin/sh -c apt-get update     && apt-get ins… | 32498826
<missing> | 2018-06-26T17:17:29-05:00 | /bin/sh -c apt-get update && apt-get install… | 141752094
<missing> | 2018-06-26T17:16:48-05:00 | /bin/sh -c set -ex;  if ! command -v gpg > /… | 7802802
<missing> | 2018-06-26T17:16:40-05:00 | /bin/sh -c apt-get update && apt-get install… | 23221329
<missing> | 2018-06-26T16:24:58-05:00 | /bin/sh -c #(nop)  CMD ["bash"] | 0
<missing> | 2018-06-26T16:24:58-05:00 | /bin/sh -c #(nop) ADD file:f21d7c14104d5d9fa… | 100561066
$ stat -f "%N %z" */layer.tar
4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/layer.tar 32578048
58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/layer.tar 1102282240
7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/layer.tar 337792000
9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/layer.tar 105512960
a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/layer.tar 146386432
ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/layer.tar 24097280
ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/layer.tar 7995904

You'll notice that the amount of layer folders doesn't quite match the number of entries in history. Turns out you really don't need those unless you're actually modifying the filesystem. The 0 byte updates end up being metadata changes which:

$ grep -Rl 'ASPNETCORE_URLS' *
58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/json
9d32878ab9fe85ddbd2e18230e7fa9fe23a4d9a1231a7eaca6c668ad9ffd4244.json

You can see hit a toplevel JSON file and the JSON file of the latest tag ID pointer. Now if we do this with tags:

$ docker history --human="false" --format "{{.ID}} | {{.CreatedSince}} | {{.CreatedBy}} | {{.Size}}" microsoft/dotnet:2.1.1-runtime-stretch-slim
26b54366ad86 | 2018-06-27T11:16:42-05:00 | /bin/sh -c curl -SL --output dotnet.tar.gz h… | 74211917
<missing> | 2018-06-27T11:16:36-05:00 | /bin/sh -c #(nop)  ENV DOTNET_VERSION=2.1.1 | 0
<missing> | 2018-06-27T11:16:36-05:00 | /bin/sh -c apt-get update     && apt-get ins… | 7020304
<missing> | 2018-06-27T11:16:18-05:00 | /bin/sh -c #(nop)  ENV ASPNETCORE_URLS=http:… | 0
<missing> | 2018-06-27T11:16:17-05:00 | /bin/sh -c apt-get update     && apt-get ins… | 43773214
<missing> | 2018-06-26T16:25:25-05:00 | /bin/sh -c #(nop)  CMD ["bash"] | 0
<missing> | 2018-06-26T16:25:25-05:00 | /bin/sh -c #(nop) ADD file:28fbc9fd012eef727… | 55292915

You'll notice that it's not the same set. What about if we check those variables in an unpacked image with multiple tags?

$ grep -Rl 'ASPNETCORE_URLS' *
26b54366ad86b0948ddf01aca72e1ac498b3412ea9c187263bda42b2ec7153c5.json
58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/json
9d32878ab9fe85ddbd2e18230e7fa9fe23a4d9a1231a7eaca6c668ad9ffd4244.json
cc0c2ba935e6ef0726e63108d2c744f8ee4abeba00e2e25f7570b07c8366f94f/json

So it hits not one, but two top level JSON files as well as JSON files in the tail layer ID folder. This essentially means that non-file related nops will filter up metadata related changes to the tail layer's JSON file. If you're wondering what they top level JSON files are about, they are metadata for an image:

$ docker images --no-trunc
REPOSITORY          TAG                          IMAGE ID                                                                  CREATED             SIZE
microsoft/dotnet    latest                       sha256:9d32878ab9fe85ddbd2e18230e7fa9fe23a4d9a1231a7eaca6c668ad9ffd4244   4 days ago          1.72GB
microsoft/dotnet    2.1.1-runtime-stretch-slim   sha256:26b54366ad86b0948ddf01aca72e1ac498b3412ea9c187263bda42b2ec7153c5   4 days ago          180MB

The sha's given match the filename identifiers minus the json extension. Looking at the latest tag's JSON entries you'll find some interesting information:

{
	"architecture": "amd64",
	"config": {
		"Hostname": "",
		"Domainname": "",
		"User": "",
		"AttachStdin": false,
		"AttachStdout": false,
		"AttachStderr": false,
		"Tty": false,
		"OpenStdin": false,
		"StdinOnce": false,
		"Env": [
			"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
			"DOTNET_SDK_VERSION=2.1.301",
			"ASPNETCORE_URLS=http://+:80",
			"DOTNET_RUNNING_IN_CONTAINER=true",
			"DOTNET_USE_POLLING_FILE_WATCHER=true",
			"NUGET_XMLDOC_MODE=skip"
		],
		"Cmd": [
			"bash"
		],
		"ArgsEscaped": true,
		"Image": "sha256:dbf7c9ea5c79113a57eac7c7cfd1348c377f2378274db7964df4e3296743e44b",
		"Volumes": null,
		"WorkingDir": "",
		"Entrypoint": null,
		"OnBuild": [],
		"Labels": null
	},
	"container": "9d266f53b8a701301650a6fbccf72df45b1774b40b15aa92891fda2ea42b87f8",
	"container_config": {
		"Hostname": "",
		"Domainname": "",
		"User": "",
		"AttachStdin": false,
		"AttachStdout": false,
		"AttachStderr": false,
		"Tty": false,
		"OpenStdin": false,
		"StdinOnce": false,
		"Env": [
			"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
			"DOTNET_SDK_VERSION=2.1.301",
			"ASPNETCORE_URLS=http://+:80",
			"DOTNET_RUNNING_IN_CONTAINER=true",
			"DOTNET_USE_POLLING_FILE_WATCHER=true",
			"NUGET_XMLDOC_MODE=skip"
		],
		"Cmd": [
			"/bin/sh",
			"-c",
			"dotnet help"
		],
		"ArgsEscaped": true,
		"Image": "sha256:dbf7c9ea5c79113a57eac7c7cfd1348c377f2378274db7964df4e3296743e44b",
		"Volumes": null,
		"WorkingDir": "",
		"Entrypoint": null,
		"OnBuild": [],
		"Labels": null
	},
	"created": "2018-06-27T16:18:56.2008874Z",
	"docker_version": "18.03.1-ce",
	"history": [
		{
			"created": "2018-06-26T21:24:58.552395569Z",
			"created_by": "/bin/sh -c #(nop) ADD file:f21d7c14104d5d9fa99f271177e765a3472f5a69398bb78f34f7401e9b2df837 in / "
		},
		{
			"created": "2018-06-26T21:24:58.974831097Z",
			"created_by": "/bin/sh -c #(nop)  CMD [\"bash\"]",
			"empty_layer": true
		},
		{
			"created": "2018-06-26T22:16:40.735261597Z",
			"created_by": "/bin/sh -c apt-get update && apt-get install -y --no-install-recommends \t\tca-certificates \t\tcurl \t\tnetbase \t\twget \t&& rm -rf /var/lib/apt/lists/*"
		},
		{
			"created": "2018-06-26T22:16:48.022107092Z",
			"created_by": "/bin/sh -c set -ex; \tif ! command -v gpg > /dev/null; then \t\tapt-get update; \t\tapt-get install -y --no-install-recommends \t\t\tgnupg \t\t\tdirmngr \t\t; \t\trm -rf /var/lib/apt/lists/*; \tfi"
		},
		{
			"created": "2018-06-26T22:17:29.585929208Z",
			"created_by": "/bin/sh -c apt-get update && apt-get install -y --no-install-recommends \t\tbzr \t\tgit \t\tmercurial \t\topenssh-client \t\tsubversion \t\t\t\tprocps \t&& rm -rf /var/lib/apt/lists/*"
		},
		{
			"created": "2018-06-27T16:17:08.200176Z",
			"created_by": "/bin/sh -c apt-get update     && apt-get install -y --no-install-recommends         libc6         libgcc1         libgssapi-krb5-2         libicu57         liblttng-ust0         libssl1.0.2         libstdc++6         zlib1g     && rm -rf /var/lib/apt/lists/*"
		},
		{
			"created": "2018-06-27T16:17:08.8406262Z",
			"created_by": "/bin/sh -c #(nop)  ENV DOTNET_SDK_VERSION=2.1.301",
			"empty_layer": true
		},
		{
			"created": "2018-06-27T16:18:04.4654999Z",
			"created_by": "/bin/sh -c curl -SL --output dotnet.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz     && dotnet_sha512='2101df5b1ca8a4a67f239c65080112a69fb2b48c1a121f293bfb18be9928f7cfbf2d38ed720cbf39c9c04734f505c360bb2835fa5f6200e4d763bd77b47027da'     && sha512sum dotnet.tar.gz     && echo \"$dotnet_sha512 dotnet.tar.gz\" | sha512sum -c -     && mkdir -p /usr/share/dotnet     && tar -zxf dotnet.tar.gz -C /usr/share/dotnet     && rm dotnet.tar.gz     && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet"
		},
		{
			"created": "2018-06-27T16:18:05.0279261Z",
			"created_by": "/bin/sh -c #(nop)  ENV ASPNETCORE_URLS=http://+:80 DOTNET_RUNNING_IN_CONTAINER=true DOTNET_USE_POLLING_FILE_WATCHER=true NUGET_XMLDOC_MODE=skip",
			"empty_layer": true
		},
		{
			"created": "2018-06-27T16:18:56.2008874Z",
			"created_by": "/bin/sh -c dotnet help"
		}
	],
	"os": "linux",
	"rootfs": {
		"type": "layers",
		"diff_ids": [
			"sha256:a2e66f6c6f5f248f2f8dcf31a3b569626ef61c6371e157fb0db857152983fc1d",
			"sha256:e14378b596fba6a0e3130a89610fa340c62b76f93fefb394b9243cd34b9449d2",
			"sha256:43701cc7035143dad7edea420be1cc475f38506bf8bfebdfb350916ed4b18c7a",
			"sha256:c30dae2762bdd89451c076c0d6c2f37292d8e27a1de2a47bb747921942312cfd",
			"sha256:f4850b2b67700478bc0f0f3e979cf941f86cefa891a8aa21eacf988b9b0e9c01",
			"sha256:b3188a29c28ba577c17b239633432a159a28859e6fd4631014c8405119c92045",
			"sha256:30b8545cf971222d47806cc702699f5fa66bcbb2959bfff6ad92cb9e907af1e6"
		]
	}
}

One interesting piece of information is that this JSON contains much of the output that was in docker history. Of particular interest is the empty_layer attribute which shows areas where a layer.tar was not created and simply metadata updates occurred.

diff_ids is also interesting from a validation perspective. If you look at the manifest.json file you'll notice a few things:

[
	{
		"Config": "9d32878ab9fe85ddbd2e18230e7fa9fe23a4d9a1231a7eaca6c668ad9ffd4244.json",
		"RepoTags": [
			"microsoft/dotnet:latest"
		],
		"Layers": [
			"9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/layer.tar",
			"ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/layer.tar",
			"ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/layer.tar",
			"a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/layer.tar",
			"4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/layer.tar",
			"7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/layer.tar",
			"58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/layer.tar"
		]
	}
]

Layers are directly associated with the layer.tar files in the layer ID folders. Now if you run a sha256 on each of these files:

$ shasum -a 256 "9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/layer.tar" "ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/layer.tar" "ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/layer.tar" "a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/layer.tar" "4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/layer.tar" "7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/layer.tar" "58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/layer.tar"

a2e66f6c6f5f248f2f8dcf31a3b569626ef61c6371e157fb0db857152983fc1d  9b8967520f4a8a8111390d20d14c6d14d17483fbcfe94810e37e4691ed1c866f/layer.tar
e14378b596fba6a0e3130a89610fa340c62b76f93fefb394b9243cd34b9449d2  ccccb3b1c4d09fcd2532e9528d55a8e7fb9551112e2d7b7222dabdedfd90e20e/layer.tar
43701cc7035143dad7edea420be1cc475f38506bf8bfebdfb350916ed4b18c7a  ff817ba161df73af9764e49ba69a02f5410f1dda2af7b67310987c7445bde8d8/layer.tar
c30dae2762bdd89451c076c0d6c2f37292d8e27a1de2a47bb747921942312cfd  a07df3daa7d55ad115e0fedeca55b7cef92b436219cc5b0e9e86d854eeb001b6/layer.tar
f4850b2b67700478bc0f0f3e979cf941f86cefa891a8aa21eacf988b9b0e9c01  4fca0fc1feef92e7a68dfbdfa41435dee495a2a3fb22db2bfb1123ef741c06fc/layer.tar
b3188a29c28ba577c17b239633432a159a28859e6fd4631014c8405119c92045  7ed7310248364f5e57cd1f115fe416edade44c43e972d7b0aa2861f388e91ad3/layer.tar
30b8545cf971222d47806cc702699f5fa66bcbb2959bfff6ad92cb9e907af1e6  58198826d5338fff62079565c54177e1516da34be73e5ecd674c20f0f290c635/layer.tar

You'll notice that the sha's calculated are in the same order of the diff_ids array. This is a nice way to validate that your layer tars at least match sha-checksums for integrity (which you still assume the manifest.json and layer ID JSON are valid at that point).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment