Merge pull request #214438 from agbrooks/master

dockerTools.buildImage: Handle base images w/ duplicate rootfs diffs

authored by Robert Hensing and committed by GitHub 1e383aad 2bac76e4

+64 -2
+53 -1
nixos/tests/docker-tools.nix
··· 1 # this test creates a simple GNU image with docker tools and sees if it executes 2 3 - import ./make-test-python.nix ({ pkgs, ... }: { 4 name = "docker-tools"; 5 meta = with pkgs.lib.maintainers; { 6 maintainers = [ lnl7 roberth ]; ··· 219 assert "abc" in docker.succeed( 220 "docker load --input='${examples.layersUnpackOrder}'", 221 "docker run --rm ${examples.layersUnpackOrder.imageName} cat /layer-order" 222 ) 223 224 with subtest("Ensure environment variables are correctly inherited"):
··· 1 # this test creates a simple GNU image with docker tools and sees if it executes 2 3 + import ./make-test-python.nix ({ pkgs, ... }: 4 + let 5 + # nixpkgs#214434: dockerTools.buildImage fails to unpack base images 6 + # containing duplicate layers when those duplicate tarballs 7 + # appear under the manifest's 'Layers'. Docker can generate images 8 + # like this even though dockerTools does not. 9 + repeatedLayerTestImage = 10 + let 11 + # Rootfs diffs for layers 1 and 2 are identical (and empty) 12 + layer1 = pkgs.dockerTools.buildImage { name = "empty"; }; 13 + layer2 = layer1.overrideAttrs (_: { fromImage = layer1; }); 14 + repeatedRootfsDiffs = pkgs.runCommandNoCC "image-with-links.tar" { 15 + nativeBuildInputs = [pkgs.jq]; 16 + } '' 17 + mkdir contents 18 + tar -xf "${layer2}" -C contents 19 + cd contents 20 + first_rootfs=$(jq -r '.[0].Layers[0]' manifest.json) 21 + second_rootfs=$(jq -r '.[0].Layers[1]' manifest.json) 22 + target_rootfs=$(sha256sum "$first_rootfs" | cut -d' ' -f 1).tar 23 + 24 + # Replace duplicated rootfs diffs with symlinks to one tarball 25 + chmod -R ug+w . 26 + mv "$first_rootfs" "$target_rootfs" 27 + rm "$second_rootfs" 28 + ln -s "../$target_rootfs" "$first_rootfs" 29 + ln -s "../$target_rootfs" "$second_rootfs" 30 + 31 + # Update manifest's layers to use the symlinks' target 32 + cat manifest.json | \ 33 + jq ".[0].Layers[0] = \"$target_rootfs\"" | 34 + jq ".[0].Layers[1] = \"$target_rootfs\"" > manifest.json.new 35 + mv manifest.json.new manifest.json 36 + 37 + tar --sort=name --hard-dereference -cf $out . 38 + ''; 39 + in pkgs.dockerTools.buildImage { 40 + fromImage = repeatedRootfsDiffs; 41 + name = "repeated-layer-test"; 42 + tag = "latest"; 43 + copyToRoot = pkgs.bash; 44 + # A runAsRoot script is required to force previous layers to be unpacked 45 + runAsRoot = '' 46 + echo 'runAsRoot has run.' 47 + ''; 48 + }; 49 + in { 50 name = "docker-tools"; 51 meta = with pkgs.lib.maintainers; { 52 maintainers = [ lnl7 roberth ]; ··· 265 assert "abc" in docker.succeed( 266 "docker load --input='${examples.layersUnpackOrder}'", 267 "docker run --rm ${examples.layersUnpackOrder.imageName} cat /layer-order" 268 + ) 269 + 270 + with subtest("Ensure repeated base layers handled by buildImage"): 271 + docker.succeed( 272 + "docker load --input='${repeatedLayerTestImage}'", 273 + "docker run --rm ${repeatedLayerTestImage.imageName} /bin/bash -c 'exit 0'" 274 ) 275 276 with subtest("Ensure environment variables are correctly inherited"):
+11 -1
pkgs/build-support/docker/default.nix
··· 229 mount /dev/${vmTools.hd} disk 230 cd disk 231 232 if [[ -n "$fromImage" ]]; then 233 echo "Unpacking base image..." 234 mkdir image ··· 245 parentID="$(cat "image/manifest.json" | jq -r '.[0].Config | rtrimstr(".json")')" 246 fi 247 248 - cat ./image/manifest.json | jq -r '.[0].Layers | .[]' > layer-list 249 else 250 touch layer-list 251 fi
··· 229 mount /dev/${vmTools.hd} disk 230 cd disk 231 232 + function dedup() { 233 + declare -A seen 234 + while read ln; do 235 + if [[ -z "''${seen["$ln"]:-}" ]]; then 236 + echo "$ln"; seen["$ln"]=1 237 + fi 238 + done 239 + } 240 + 241 if [[ -n "$fromImage" ]]; then 242 echo "Unpacking base image..." 243 mkdir image ··· 254 parentID="$(cat "image/manifest.json" | jq -r '.[0].Config | rtrimstr(".json")')" 255 fi 256 257 + # In case of repeated layers, unpack only the last occurrence of each 258 + cat ./image/manifest.json | jq -r '.[0].Layers | .[]' | tac | dedup | tac > layer-list 259 else 260 touch layer-list 261 fi