at master 26 kB view raw
1# this test creates a simple GNU image with docker tools and sees if it executes 2 3{ pkgs, ... }: 4let 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 (_: { 14 fromImage = layer1; 15 }); 16 repeatedRootfsDiffs = 17 pkgs.runCommand "image-with-links.tar" 18 { 19 nativeBuildInputs = [ pkgs.jq ]; 20 } 21 '' 22 mkdir contents 23 tar -xf "${layer2}" -C contents 24 cd contents 25 first_rootfs=$(jq -r '.[0].Layers[0]' manifest.json) 26 second_rootfs=$(jq -r '.[0].Layers[1]' manifest.json) 27 target_rootfs=$(sha256sum "$first_rootfs" | cut -d' ' -f 1).tar 28 29 # Replace duplicated rootfs diffs with symlinks to one tarball 30 chmod -R ug+w . 31 mv "$first_rootfs" "$target_rootfs" 32 rm "$second_rootfs" 33 ln -s "../$target_rootfs" "$first_rootfs" 34 ln -s "../$target_rootfs" "$second_rootfs" 35 36 # Update manifest's layers to use the symlinks' target 37 cat manifest.json | \ 38 jq ".[0].Layers[0] = \"$target_rootfs\"" | 39 jq ".[0].Layers[1] = \"$target_rootfs\"" > manifest.json.new 40 mv manifest.json.new manifest.json 41 42 tar --sort=name --hard-dereference -cf $out . 43 ''; 44 in 45 pkgs.dockerTools.buildImage { 46 fromImage = repeatedRootfsDiffs; 47 name = "repeated-layer-test"; 48 tag = "latest"; 49 copyToRoot = pkgs.bash; 50 # A runAsRoot script is required to force previous layers to be unpacked 51 runAsRoot = '' 52 echo 'runAsRoot has run.' 53 ''; 54 }; 55 56 chownTestImage = pkgs.dockerTools.streamLayeredImage { 57 name = "chown-test"; 58 tag = "latest"; 59 enableFakechroot = true; 60 fakeRootCommands = '' 61 touch /testfile 62 chown 12345:12345 /testfile 63 ''; 64 config.Cmd = [ 65 "${pkgs.coreutils}/bin/stat" 66 "-c" 67 "%u:%g" 68 "/testfile" 69 ]; 70 }; 71 72 nonRootTestImage = pkgs.dockerTools.streamLayeredImage { 73 name = "non-root-test"; 74 tag = "latest"; 75 uid = 1000; 76 gid = 1000; 77 uname = "user"; 78 gname = "user"; 79 config = { 80 User = "user"; 81 Cmd = [ 82 "${pkgs.coreutils}/bin/stat" 83 "-c" 84 "%u:%g" 85 "${pkgs.coreutils}/bin/stat" 86 ]; 87 }; 88 }; 89in 90{ 91 name = "docker-tools"; 92 meta = with pkgs.lib.maintainers; { 93 maintainers = [ 94 lnl7 95 roberth 96 ]; 97 }; 98 99 nodes = { 100 docker = 101 { ... }: 102 { 103 virtualisation = { 104 diskSize = 3072; 105 docker.enable = true; 106 }; 107 }; 108 }; 109 110 testScript = with pkgs.dockerTools; '' 111 unix_time_second1 = "1970-01-01T00:00:01Z" 112 113 docker.wait_for_unit("sockets.target") 114 115 with subtest("includeStorePath"): 116 with subtest("assumption"): 117 docker.succeed("${examples.helloOnRoot} | docker load") 118 docker.succeed("docker run --rm hello | grep -i hello") 119 docker.succeed("docker image rm hello:latest") 120 121 with subtest("includeStorePath = false; breaks example"): 122 docker.succeed("${examples.helloOnRootNoStore} | docker load") 123 docker.fail("docker run --rm hello | grep -i hello") 124 docker.succeed("docker image rm hello:latest") 125 with subtest("includeStorePath = false; breaks example (fakechroot)"): 126 docker.succeed("${examples.helloOnRootNoStoreFakechroot} | docker load") 127 docker.fail("docker run --rm hello | grep -i hello") 128 docker.succeed("docker image rm hello:latest") 129 130 with subtest("Ensure ZERO paths are added to the store"): 131 docker.fail("${examples.helloOnRootNoStore} | ${pkgs.crane}/bin/crane export - - | tar t | grep 'nix/store/'") 132 with subtest("Ensure ZERO paths are added to the store (fakechroot)"): 133 docker.fail("${examples.helloOnRootNoStoreFakechroot} | ${pkgs.crane}/bin/crane export - - | tar t | grep 'nix/store/'") 134 135 with subtest("includeStorePath = false; works with mounted store"): 136 docker.succeed("${examples.helloOnRootNoStore} | docker load") 137 docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello") 138 docker.succeed("docker image rm hello:latest") 139 with subtest("includeStorePath = false; works with mounted store (fakechroot)"): 140 docker.succeed("${examples.helloOnRootNoStoreFakechroot} | docker load") 141 docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello") 142 docker.succeed("docker image rm hello:latest") 143 144 with subtest("Ensure Docker images use a stable date by default"): 145 docker.succeed( 146 "docker load --input='${examples.bash}'" 147 ) 148 assert unix_time_second1 in docker.succeed( 149 "docker inspect ${examples.bash.imageName} " 150 + "| ${pkgs.jq}/bin/jq -r .[].Created", 151 ) 152 153 docker.succeed("docker run --rm ${examples.bash.imageName} bash --version") 154 # Check imageTag attribute matches image 155 docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.bash.imageTag}'") 156 docker.succeed("docker rmi ${examples.bash.imageName}") 157 158 # The remaining combinations 159 with subtest("Ensure imageTag attribute matches image"): 160 docker.succeed( 161 "docker load --input='${examples.bashNoTag}'" 162 ) 163 docker.succeed( 164 "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTag.imageTag}'" 165 ) 166 docker.succeed("docker rmi ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag}") 167 168 docker.succeed( 169 "docker load --input='${examples.bashNoTagLayered}'" 170 ) 171 docker.succeed( 172 "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTagLayered.imageTag}'" 173 ) 174 docker.succeed("docker rmi ${examples.bashNoTagLayered.imageName}:${examples.bashNoTagLayered.imageTag}") 175 176 docker.succeed( 177 "${examples.bashNoTagStreamLayered} | docker load" 178 ) 179 docker.succeed( 180 "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTagStreamLayered.imageTag}'" 181 ) 182 docker.succeed( 183 "docker rmi ${examples.bashNoTagStreamLayered.imageName}:${examples.bashNoTagStreamLayered.imageTag}" 184 ) 185 186 docker.succeed( 187 "docker load --input='${examples.nixLayered}'" 188 ) 189 docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.nixLayered.imageTag}'") 190 docker.succeed("docker rmi ${examples.nixLayered.imageName}") 191 192 with subtest("Check that images with alternative compression schemas load"): 193 docker.succeed( 194 "docker load --input='${examples.bashZstdCompressed}'", 195 "docker rmi ${examples.bashZstdCompressed.imageName}", 196 ) 197 docker.succeed( 198 "docker load --input='${examples.bashUncompressed}'", 199 "docker rmi ${examples.bashUncompressed.imageName}", 200 ) 201 docker.succeed( 202 "docker load --input='${examples.bashLayeredUncompressed}'", 203 "docker rmi ${examples.bashLayeredUncompressed.imageName}", 204 ) 205 docker.succeed( 206 "docker load --input='${examples.bashLayeredZstdCompressed}'", 207 "docker rmi ${examples.bashLayeredZstdCompressed.imageName}", 208 ) 209 210 with subtest( 211 "Check if the nix store is correctly initialized by listing " 212 "dependencies of the installed Nix binary" 213 ): 214 docker.succeed( 215 "docker load --input='${examples.nix}'", 216 "docker run --rm ${examples.nix.imageName} nix-store -qR ${pkgs.nix}", 217 "docker rmi ${examples.nix.imageName}", 218 ) 219 220 with subtest( 221 "Ensure (layered) nix store has correct permissions " 222 "and that the container starts when its process does not have uid 0" 223 ): 224 docker.succeed( 225 "docker load --input='${examples.bashLayeredWithUser}'", 226 "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 755 == $(stat --format=%a /nix) && test 755 == $(stat --format=%a /nix/store)'", 227 "docker rmi ${examples.bashLayeredWithUser.imageName}", 228 ) 229 230 with subtest("The nix binary symlinks are intact"): 231 docker.succeed( 232 "docker load --input='${examples.nix}'", 233 "docker run --rm ${examples.nix.imageName} ${pkgs.bash}/bin/bash -c 'test nix == $(readlink ${pkgs.nix}/bin/nix-daemon)'", 234 "docker rmi ${examples.nix.imageName}", 235 ) 236 237 with subtest("The nix binary symlinks are intact when the image is layered"): 238 docker.succeed( 239 "docker load --input='${examples.nixLayered}'", 240 "docker run --rm ${examples.nixLayered.imageName} ${pkgs.bash}/bin/bash -c 'test nix == $(readlink ${pkgs.nix}/bin/nix-daemon)'", 241 "docker rmi ${examples.nixLayered.imageName}", 242 ) 243 244 with subtest("The pullImage tool works"): 245 docker.succeed( 246 "docker load --input='${examples.testNixFromDockerHub}'", 247 "docker run --rm nix:2.2.1 nix-store --version", 248 "docker rmi nix:2.2.1", 249 ) 250 251 with subtest("runAsRoot and entry point work"): 252 docker.succeed( 253 "docker load --input='${examples.nginx}'", 254 "docker run --name nginx -d -p 8000:80 ${examples.nginx.imageName}", 255 ) 256 docker.wait_until_succeeds("curl -f http://localhost:8000/") 257 docker.succeed( 258 "docker rm --force nginx", 259 "docker rmi '${examples.nginx.imageName}'", 260 ) 261 262 with subtest("A pulled image can be used as base image"): 263 docker.succeed( 264 "docker load --input='${examples.onTopOfPulledImage}'", 265 "docker run --rm ontopofpulledimage hello", 266 "docker rmi ontopofpulledimage", 267 ) 268 269 with subtest("Regression test for issue #34779"): 270 docker.succeed( 271 "docker load --input='${examples.runAsRootExtraCommands}'", 272 "docker run --rm runasrootextracommands cat extraCommands", 273 "docker run --rm runasrootextracommands cat runAsRoot", 274 "docker rmi '${examples.runAsRootExtraCommands.imageName}'", 275 ) 276 277 with subtest("Ensure Docker images can use an unstable date"): 278 docker.succeed( 279 "docker load --input='${examples.unstableDate}'" 280 ) 281 assert unix_time_second1 not in docker.succeed( 282 "docker inspect ${examples.unstableDate.imageName} " 283 + "| ${pkgs.jq}/bin/jq -r .[].Created" 284 ) 285 286 with subtest("Ensure Layered Docker images can use an unstable date"): 287 docker.succeed( 288 "docker load --input='${examples.unstableDateLayered}'" 289 ) 290 assert unix_time_second1 not in docker.succeed( 291 "docker inspect ${examples.unstableDateLayered.imageName} " 292 + "| ${pkgs.jq}/bin/jq -r .[].Created" 293 ) 294 295 with subtest("Ensure Layered Docker images work"): 296 docker.succeed( 297 "docker load --input='${examples.layered-image}'", 298 "docker run --rm ${examples.layered-image.imageName}", 299 "docker run --rm ${examples.layered-image.imageName} cat extraCommands", 300 ) 301 302 with subtest("Ensure images built on top of layered Docker images work"): 303 docker.succeed( 304 "docker load --input='${examples.layered-on-top}'", 305 "docker run --rm ${examples.layered-on-top.imageName}", 306 ) 307 308 with subtest("Ensure layered images built on top of layered Docker images work"): 309 docker.succeed( 310 "docker load --input='${examples.layered-on-top-layered}'", 311 "docker run --rm ${examples.layered-on-top-layered.imageName}", 312 ) 313 314 315 def set_of_layers(image_name): 316 return set( 317 docker.succeed( 318 f"docker inspect {image_name} " 319 + "| ${pkgs.jq}/bin/jq -r '.[] | .RootFS.Layers | .[]'" 320 ).split() 321 ) 322 323 324 with subtest("Ensure layers are shared between images"): 325 docker.succeed( 326 "docker load --input='${examples.another-layered-image}'" 327 ) 328 layers1 = set_of_layers("${examples.layered-image.imageName}") 329 layers2 = set_of_layers("${examples.another-layered-image.imageName}") 330 assert bool(layers1 & layers2) 331 332 with subtest("Ensure order of layers is correct"): 333 docker.succeed( 334 "docker load --input='${examples.layersOrder}'" 335 ) 336 337 for index in 1, 2, 3: 338 assert f"layer{index}" in docker.succeed( 339 f"docker run --rm ${examples.layersOrder.imageName} cat /tmp/layer{index}" 340 ) 341 342 with subtest("Ensure layers unpacked in correct order before runAsRoot runs"): 343 assert "abc" in docker.succeed( 344 "docker load --input='${examples.layersUnpackOrder}'", 345 "docker run --rm ${examples.layersUnpackOrder.imageName} cat /layer-order" 346 ) 347 348 with subtest("Ensure repeated base layers handled by buildImage"): 349 docker.succeed( 350 "docker load --input='${repeatedLayerTestImage}'", 351 "docker run --rm ${repeatedLayerTestImage.imageName} /bin/bash -c 'exit 0'" 352 ) 353 354 with subtest("Ensure environment variables are correctly inherited"): 355 docker.succeed( 356 "docker load --input='${examples.environmentVariables}'" 357 ) 358 out = docker.succeed("docker run --rm ${examples.environmentVariables.imageName} env") 359 env = out.splitlines() 360 assert "FROM_PARENT=true" in env, "envvars from the parent should be preserved" 361 assert "FROM_CHILD=true" in env, "envvars from the child should be preserved" 362 assert "LAST_LAYER=child" in env, "envvars from the child should take priority" 363 364 with subtest("Ensure environment variables of layered images are correctly inherited"): 365 docker.succeed( 366 "docker load --input='${examples.environmentVariablesLayered}'" 367 ) 368 out = docker.succeed("docker run --rm ${examples.environmentVariablesLayered.imageName} env") 369 env = out.splitlines() 370 assert "FROM_PARENT=true" in env, "envvars from the parent should be preserved" 371 assert "FROM_CHILD=true" in env, "envvars from the child should be preserved" 372 assert "LAST_LAYER=child" in env, "envvars from the child should take priority" 373 374 with subtest( 375 "Ensure inherited environment variables of layered images are correctly resolved" 376 ): 377 # Read environment variables as stored in image config 378 config = docker.succeed( 379 "tar -xOf ${examples.environmentVariablesLayered} manifest.json | ${pkgs.jq}/bin/jq -r .[].Config" 380 ).strip() 381 out = docker.succeed( 382 f"tar -xOf ${examples.environmentVariablesLayered} {config} | ${pkgs.jq}/bin/jq -r '.config.Env | .[]'" 383 ) 384 env = out.splitlines() 385 assert ( 386 sum(entry.startswith("LAST_LAYER") for entry in env) == 1 387 ), "envvars overridden by child should be unique" 388 389 with subtest("Ensure image with only 2 layers can be loaded"): 390 docker.succeed( 391 "docker load --input='${examples.two-layered-image}'" 392 ) 393 394 with subtest( 395 "Ensure the bulk layer doesn't miss store paths (regression test for #78744)" 396 ): 397 docker.succeed( 398 "docker load --input='${pkgs.dockerTools.examples.bulk-layer}'", 399 # Ensure the two output paths (ls and hello) are in the layer 400 "docker run bulk-layer ls /bin/hello", 401 ) 402 403 with subtest( 404 "Ensure the bulk layer with a base image respects the number of maxLayers" 405 ): 406 docker.succeed( 407 "docker load --input='${pkgs.dockerTools.examples.layered-bulk-layer}'", 408 # Ensure the image runs correctly 409 "docker run layered-bulk-layer ls /bin/hello", 410 ) 411 412 # Ensure the image has the correct number of layers 413 assert len(set_of_layers("layered-bulk-layer")) == 4 414 415 with subtest("Ensure only minimal paths are added to the store"): 416 # TODO: make an example that has no store paths, for example by making 417 # busybox non-self-referential. 418 419 # This check tests that buildLayeredImage can build images that don't need a store. 420 docker.succeed( 421 "docker load --input='${pkgs.dockerTools.examples.no-store-paths}'" 422 ) 423 424 docker.succeed("docker run --rm no-store-paths ls / >/dev/console") 425 426 # If busybox isn't self-referential, we need this line 427 # docker.fail("docker run --rm no-store-paths ls /nix/store >/dev/console") 428 # However, it currently is self-referential, so we check that it is the 429 # only store path. 430 docker.succeed("diff <(docker run --rm no-store-paths ls /nix/store) <(basename ${pkgs.pkgsStatic.busybox}) >/dev/console") 431 432 with subtest("Ensure buildLayeredImage does not change store path contents."): 433 docker.succeed( 434 "docker load --input='${pkgs.dockerTools.examples.filesInStore}'", 435 "docker run --rm file-in-store nix-store --verify --check-contents", 436 "docker run --rm file-in-store |& grep 'some data'", 437 ) 438 439 with subtest("Ensure cross compiled image can be loaded and has correct arch."): 440 docker.succeed( 441 "docker load --input='${pkgs.dockerTools.examples.cross}'", 442 ) 443 assert ( 444 docker.succeed( 445 "docker inspect ${pkgs.dockerTools.examples.cross.imageName} " 446 + "| ${pkgs.jq}/bin/jq -r .[].Architecture" 447 ).strip() 448 == "${if pkgs.stdenv.hostPlatform.system == "aarch64-linux" then "amd64" else "arm64"}" 449 ) 450 451 with subtest("buildLayeredImage doesn't dereference /nix/store symlink layers"): 452 docker.succeed( 453 "docker load --input='${examples.layeredStoreSymlink}'", 454 "docker run --rm ${examples.layeredStoreSymlink.imageName} bash -c 'test -L ${examples.layeredStoreSymlink.passthru.symlink}'", 455 "docker rmi ${examples.layeredStoreSymlink.imageName}", 456 ) 457 458 with subtest("buildImage supports registry/ prefix in image name"): 459 docker.succeed( 460 "docker load --input='${examples.prefixedImage}'" 461 ) 462 docker.succeed( 463 "docker images --format '{{.Repository}}' | grep -F '${examples.prefixedImage.imageName}'" 464 ) 465 466 with subtest("buildLayeredImage supports registry/ prefix in image name"): 467 docker.succeed( 468 "docker load --input='${examples.prefixedLayeredImage}'" 469 ) 470 docker.succeed( 471 "docker images --format '{{.Repository}}' | grep -F '${examples.prefixedLayeredImage.imageName}'" 472 ) 473 474 with subtest("buildLayeredImage supports running chown with fakeRootCommands"): 475 docker.succeed( 476 "docker load --input='${examples.layeredImageWithFakeRootCommands}'" 477 ) 478 docker.succeed( 479 "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} sh -c 'stat -c '%u' /home/alice | grep -E ^1000$'" 480 ) 481 482 with subtest("Ensure docker load on merged images loads all of the constituent images"): 483 docker.succeed( 484 "docker load --input='${examples.mergedBashAndRedis}'" 485 ) 486 docker.succeed( 487 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.bash.imageName}-${examples.bash.imageTag}'" 488 ) 489 docker.succeed( 490 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.redis.imageName}-${examples.redis.imageTag}'" 491 ) 492 docker.succeed("docker run --rm ${examples.bash.imageName} bash --version") 493 docker.succeed("docker run --rm ${examples.redis.imageName} redis-cli --version") 494 docker.succeed("docker rmi ${examples.bash.imageName}") 495 docker.succeed("docker rmi ${examples.redis.imageName}") 496 497 with subtest( 498 "Ensure docker load on merged images loads all of the constituent images (missing tags)" 499 ): 500 docker.succeed( 501 "docker load --input='${examples.mergedBashNoTagAndRedis}'" 502 ) 503 docker.succeed( 504 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.bashNoTag.imageName}-${examples.bashNoTag.imageTag}'" 505 ) 506 docker.succeed( 507 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.redis.imageName}-${examples.redis.imageTag}'" 508 ) 509 # we need to explicitly specify the generated tag here 510 docker.succeed( 511 "docker run --rm ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag} bash --version" 512 ) 513 docker.succeed("docker run --rm ${examples.redis.imageName} redis-cli --version") 514 docker.succeed("docker rmi ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag}") 515 docker.succeed("docker rmi ${examples.redis.imageName}") 516 517 with subtest("mergeImages preserves owners of the original images"): 518 docker.succeed( 519 "docker load --input='${examples.mergedBashFakeRoot}'" 520 ) 521 docker.succeed( 522 "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} sh -c 'stat -c '%u' /home/alice | grep -E ^1000$'" 523 ) 524 525 with subtest("The image contains store paths referenced by the fakeRootCommands output"): 526 docker.succeed( 527 "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} /hello/bin/layeredImageWithFakeRootCommands-hello" 528 ) 529 530 with subtest("mergeImage correctly deals with varying compression schemas in inputs"): 531 docker.succeed("docker load --input='${examples.mergeVaryingCompressor}'") 532 533 for sub_image, tag in [ 534 ("${examples.redis.imageName}", "${examples.redis.imageTag}"), 535 ("${examples.bashUncompressed.imageName}", "${examples.bashUncompressed.imageTag}"), 536 ("${examples.bashZstdCompressed.imageName}", "${examples.bashZstdCompressed.imageTag}"), 537 ]: 538 docker.succeed(f"docker images --format '{{{{.Repository}}}}-{{{{.Tag}}}}' | grep -F '{sub_image}-{tag}'") 539 docker.succeed(f"docker rmi {sub_image}") 540 541 542 with subtest("exportImage produces a valid tarball"): 543 docker.succeed( 544 "tar -tf ${examples.exportBash} | grep '\./bin/bash' > /dev/null" 545 ) 546 547 with subtest("layered image fakeRootCommands with fakechroot works"): 548 docker.succeed("${examples.imageViaFakeChroot} | docker load") 549 docker.succeed("docker run --rm image-via-fake-chroot | grep -i hello") 550 docker.succeed("docker image rm image-via-fake-chroot:latest") 551 552 with subtest("Ensure bare paths in contents are loaded correctly"): 553 docker.succeed( 554 "docker load --input='${examples.build-image-with-path}'", 555 "docker run --rm build-image-with-path bash -c '[[ -e /hello.txt ]]'", 556 "docker rmi build-image-with-path", 557 ) 558 docker.succeed( 559 "${examples.layered-image-with-path} | docker load", 560 "docker run --rm layered-image-with-path bash -c '[[ -e /hello.txt ]]'", 561 "docker rmi layered-image-with-path", 562 ) 563 564 with subtest("Ensure correct architecture is present in manifests."): 565 docker.succeed(""" 566 docker load --input='${examples.build-image-with-architecture}' 567 docker inspect build-image-with-architecture \ 568 | ${pkgs.jq}/bin/jq -er '.[] | select(.Architecture=="arm64").Architecture' 569 docker rmi build-image-with-architecture 570 """) 571 docker.succeed(""" 572 ${examples.layered-image-with-architecture} | docker load 573 docker inspect layered-image-with-architecture \ 574 | ${pkgs.jq}/bin/jq -er '.[] | select(.Architecture=="arm64").Architecture' 575 docker rmi layered-image-with-architecture 576 """) 577 578 with subtest("etc"): 579 docker.succeed("${examples.etc} | docker load") 580 docker.succeed("docker run --rm etc | grep localhost") 581 docker.succeed("docker image rm etc:latest") 582 583 with subtest("image-with-certs"): 584 docker.succeed("<${examples.image-with-certs} docker load") 585 docker.succeed("docker run --rm image-with-certs:latest test -r /etc/ssl/certs/ca-bundle.crt") 586 docker.succeed("docker run --rm image-with-certs:latest test -r /etc/ssl/certs/ca-certificates.crt") 587 docker.succeed("docker run --rm image-with-certs:latest test -r /etc/pki/tls/certs/ca-bundle.crt") 588 docker.succeed("docker image rm image-with-certs:latest") 589 590 with subtest("streamLayeredImage: chown is persistent in fakeRootCommands"): 591 docker.succeed( 592 "${chownTestImage} | docker load", 593 "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)" 594 ) 595 596 with subtest("streamLayeredImage: with non-root user"): 597 docker.succeed( 598 "${nonRootTestImage} | docker load", 599 "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)" 600 ) 601 ''; 602}