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