at 21.11-pre 16 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, ... }: { 4 name = "docker-tools"; 5 meta = with pkgs.lib.maintainers; { 6 maintainers = [ lnl7 roberth ]; 7 }; 8 9 nodes = { 10 docker = { ... }: { 11 virtualisation = { 12 diskSize = 2048; 13 docker.enable = true; 14 }; 15 }; 16 }; 17 18 testScript = with pkgs.dockerTools; '' 19 unix_time_second1 = "1970-01-01T00:00:01Z" 20 21 docker.wait_for_unit("sockets.target") 22 23 with subtest("Ensure Docker images use a stable date by default"): 24 docker.succeed( 25 "docker load --input='${examples.bash}'" 26 ) 27 assert unix_time_second1 in docker.succeed( 28 "docker inspect ${examples.bash.imageName} " 29 + "| ${pkgs.jq}/bin/jq -r .[].Created", 30 ) 31 32 docker.succeed("docker run --rm ${examples.bash.imageName} bash --version") 33 # Check imageTag attribute matches image 34 docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.bash.imageTag}'") 35 docker.succeed("docker rmi ${examples.bash.imageName}") 36 37 # The remaining combinations 38 with subtest("Ensure imageTag attribute matches image"): 39 docker.succeed( 40 "docker load --input='${examples.bashNoTag}'" 41 ) 42 docker.succeed( 43 "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTag.imageTag}'" 44 ) 45 docker.succeed("docker rmi ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag}") 46 47 docker.succeed( 48 "docker load --input='${examples.bashNoTagLayered}'" 49 ) 50 docker.succeed( 51 "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTagLayered.imageTag}'" 52 ) 53 docker.succeed("docker rmi ${examples.bashNoTagLayered.imageName}:${examples.bashNoTagLayered.imageTag}") 54 55 docker.succeed( 56 "${examples.bashNoTagStreamLayered} | docker load" 57 ) 58 docker.succeed( 59 "docker images --format '{{.Tag}}' | grep -F '${examples.bashNoTagStreamLayered.imageTag}'" 60 ) 61 docker.succeed( 62 "docker rmi ${examples.bashNoTagStreamLayered.imageName}:${examples.bashNoTagStreamLayered.imageTag}" 63 ) 64 65 docker.succeed( 66 "docker load --input='${examples.nixLayered}'" 67 ) 68 docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.nixLayered.imageTag}'") 69 docker.succeed("docker rmi ${examples.nixLayered.imageName}") 70 71 72 with subtest( 73 "Check if the nix store is correctly initialized by listing " 74 "dependencies of the installed Nix binary" 75 ): 76 docker.succeed( 77 "docker load --input='${examples.nix}'", 78 "docker run --rm ${examples.nix.imageName} nix-store -qR ${pkgs.nix}", 79 "docker rmi ${examples.nix.imageName}", 80 ) 81 82 with subtest( 83 "Ensure (layered) nix store has correct permissions " 84 "and that the container starts when its process does not have uid 0" 85 ): 86 docker.succeed( 87 "docker load --input='${examples.bashLayeredWithUser}'", 88 "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 555 == $(stat --format=%a /nix) && test 555 == $(stat --format=%a /nix/store)'", 89 "docker rmi ${examples.bashLayeredWithUser.imageName}", 90 ) 91 92 with subtest("The nix binary symlinks are intact"): 93 docker.succeed( 94 "docker load --input='${examples.nix}'", 95 "docker run --rm ${examples.nix.imageName} ${pkgs.bash}/bin/bash -c 'test nix == $(readlink ${pkgs.nix}/bin/nix-daemon)'", 96 "docker rmi ${examples.nix.imageName}", 97 ) 98 99 with subtest("The nix binary symlinks are intact when the image is layered"): 100 docker.succeed( 101 "docker load --input='${examples.nixLayered}'", 102 "docker run --rm ${examples.nixLayered.imageName} ${pkgs.bash}/bin/bash -c 'test nix == $(readlink ${pkgs.nix}/bin/nix-daemon)'", 103 "docker rmi ${examples.nixLayered.imageName}", 104 ) 105 106 with subtest("The pullImage tool works"): 107 docker.succeed( 108 "docker load --input='${examples.nixFromDockerHub}'", 109 "docker run --rm nix:2.2.1 nix-store --version", 110 "docker rmi nix:2.2.1", 111 ) 112 113 with subtest("runAsRoot and entry point work"): 114 docker.succeed( 115 "docker load --input='${examples.nginx}'", 116 "docker run --name nginx -d -p 8000:80 ${examples.nginx.imageName}", 117 ) 118 docker.wait_until_succeeds("curl -f http://localhost:8000/") 119 docker.succeed( 120 "docker rm --force nginx", 121 "docker rmi '${examples.nginx.imageName}'", 122 ) 123 124 with subtest("A pulled image can be used as base image"): 125 docker.succeed( 126 "docker load --input='${examples.onTopOfPulledImage}'", 127 "docker run --rm ontopofpulledimage hello", 128 "docker rmi ontopofpulledimage", 129 ) 130 131 with subtest("Regression test for issue #34779"): 132 docker.succeed( 133 "docker load --input='${examples.runAsRootExtraCommands}'", 134 "docker run --rm runasrootextracommands cat extraCommands", 135 "docker run --rm runasrootextracommands cat runAsRoot", 136 "docker rmi '${examples.runAsRootExtraCommands.imageName}'", 137 ) 138 139 with subtest("Ensure Docker images can use an unstable date"): 140 docker.succeed( 141 "docker load --input='${examples.unstableDate}'" 142 ) 143 assert unix_time_second1 not in docker.succeed( 144 "docker inspect ${examples.unstableDate.imageName} " 145 + "| ${pkgs.jq}/bin/jq -r .[].Created" 146 ) 147 148 with subtest("Ensure Layered Docker images can use an unstable date"): 149 docker.succeed( 150 "docker load --input='${examples.unstableDateLayered}'" 151 ) 152 assert unix_time_second1 not in docker.succeed( 153 "docker inspect ${examples.unstableDateLayered.imageName} " 154 + "| ${pkgs.jq}/bin/jq -r .[].Created" 155 ) 156 157 with subtest("Ensure Layered Docker images work"): 158 docker.succeed( 159 "docker load --input='${examples.layered-image}'", 160 "docker run --rm ${examples.layered-image.imageName}", 161 "docker run --rm ${examples.layered-image.imageName} cat extraCommands", 162 ) 163 164 with subtest("Ensure images built on top of layered Docker images work"): 165 docker.succeed( 166 "docker load --input='${examples.layered-on-top}'", 167 "docker run --rm ${examples.layered-on-top.imageName}", 168 ) 169 170 with subtest("Ensure layered images built on top of layered Docker images work"): 171 docker.succeed( 172 "docker load --input='${examples.layered-on-top-layered}'", 173 "docker run --rm ${examples.layered-on-top-layered.imageName}", 174 ) 175 176 177 def set_of_layers(image_name): 178 return set( 179 docker.succeed( 180 f"docker inspect {image_name} " 181 + "| ${pkgs.jq}/bin/jq -r '.[] | .RootFS.Layers | .[]'" 182 ).split() 183 ) 184 185 186 with subtest("Ensure layers are shared between images"): 187 docker.succeed( 188 "docker load --input='${examples.another-layered-image}'" 189 ) 190 layers1 = set_of_layers("${examples.layered-image.imageName}") 191 layers2 = set_of_layers("${examples.another-layered-image.imageName}") 192 assert bool(layers1 & layers2) 193 194 with subtest("Ensure order of layers is correct"): 195 docker.succeed( 196 "docker load --input='${examples.layersOrder}'" 197 ) 198 199 for index in 1, 2, 3: 200 assert f"layer{index}" in docker.succeed( 201 f"docker run --rm ${examples.layersOrder.imageName} cat /tmp/layer{index}" 202 ) 203 204 with subtest("Ensure environment variables are correctly inherited"): 205 docker.succeed( 206 "docker load --input='${examples.environmentVariables}'" 207 ) 208 out = docker.succeed("docker run --rm ${examples.environmentVariables.imageName} env") 209 env = out.splitlines() 210 assert "FROM_PARENT=true" in env, "envvars from the parent should be preserved" 211 assert "FROM_CHILD=true" in env, "envvars from the child should be preserved" 212 assert "LAST_LAYER=child" in env, "envvars from the child should take priority" 213 214 with subtest("Ensure environment variables of layered images are correctly inherited"): 215 docker.succeed( 216 "docker load --input='${examples.environmentVariablesLayered}'" 217 ) 218 out = docker.succeed("docker run --rm ${examples.environmentVariablesLayered.imageName} env") 219 env = out.splitlines() 220 assert "FROM_PARENT=true" in env, "envvars from the parent should be preserved" 221 assert "FROM_CHILD=true" in env, "envvars from the child should be preserved" 222 assert "LAST_LAYER=child" in env, "envvars from the child should take priority" 223 224 with subtest( 225 "Ensure inherited environment variables of layered images are correctly resolved" 226 ): 227 # Read environment variables as stored in image config 228 config = docker.succeed( 229 "tar -xOf ${examples.environmentVariablesLayered} manifest.json | ${pkgs.jq}/bin/jq -r .[].Config" 230 ).strip() 231 out = docker.succeed( 232 f"tar -xOf ${examples.environmentVariablesLayered} {config} | ${pkgs.jq}/bin/jq -r '.config.Env | .[]'" 233 ) 234 env = out.splitlines() 235 assert ( 236 sum(entry.startswith("LAST_LAYER") for entry in env) == 1 237 ), "envvars overridden by child should be unique" 238 239 with subtest("Ensure image with only 2 layers can be loaded"): 240 docker.succeed( 241 "docker load --input='${examples.two-layered-image}'" 242 ) 243 244 with subtest( 245 "Ensure the bulk layer doesn't miss store paths (regression test for #78744)" 246 ): 247 docker.succeed( 248 "docker load --input='${pkgs.dockerTools.examples.bulk-layer}'", 249 # Ensure the two output paths (ls and hello) are in the layer 250 "docker run bulk-layer ls /bin/hello", 251 ) 252 253 with subtest( 254 "Ensure the bulk layer with a base image respects the number of maxLayers" 255 ): 256 docker.succeed( 257 "docker load --input='${pkgs.dockerTools.examples.layered-bulk-layer}'", 258 # Ensure the image runs correctly 259 "docker run layered-bulk-layer ls /bin/hello", 260 ) 261 262 # Ensure the image has the correct number of layers 263 assert len(set_of_layers("layered-bulk-layer")) == 4 264 265 with subtest("Ensure correct behavior when no store is needed"): 266 # This check tests that buildLayeredImage can build images that don't need a store. 267 docker.succeed( 268 "docker load --input='${pkgs.dockerTools.examples.no-store-paths}'" 269 ) 270 271 # This check may be loosened to allow an *empty* store rather than *no* store. 272 docker.succeed("docker run --rm no-store-paths ls /") 273 docker.fail("docker run --rm no-store-paths ls /nix/store") 274 275 with subtest("Ensure buildLayeredImage does not change store path contents."): 276 docker.succeed( 277 "docker load --input='${pkgs.dockerTools.examples.filesInStore}'", 278 "docker run --rm file-in-store nix-store --verify --check-contents", 279 "docker run --rm file-in-store |& grep 'some data'", 280 ) 281 282 with subtest("Ensure cross compiled image can be loaded and has correct arch."): 283 docker.succeed( 284 "docker load --input='${pkgs.dockerTools.examples.cross}'", 285 ) 286 assert ( 287 docker.succeed( 288 "docker inspect ${pkgs.dockerTools.examples.cross.imageName} " 289 + "| ${pkgs.jq}/bin/jq -r .[].Architecture" 290 ).strip() 291 == "${if pkgs.system == "aarch64-linux" then "amd64" else "arm64"}" 292 ) 293 294 with subtest("buildLayeredImage doesn't dereference /nix/store symlink layers"): 295 docker.succeed( 296 "docker load --input='${examples.layeredStoreSymlink}'", 297 "docker run --rm ${examples.layeredStoreSymlink.imageName} bash -c 'test -L ${examples.layeredStoreSymlink.passthru.symlink}'", 298 "docker rmi ${examples.layeredStoreSymlink.imageName}", 299 ) 300 301 with subtest("buildImage supports registry/ prefix in image name"): 302 docker.succeed( 303 "docker load --input='${examples.prefixedImage}'" 304 ) 305 docker.succeed( 306 "docker images --format '{{.Repository}}' | grep -F '${examples.prefixedImage.imageName}'" 307 ) 308 309 with subtest("buildLayeredImage supports registry/ prefix in image name"): 310 docker.succeed( 311 "docker load --input='${examples.prefixedLayeredImage}'" 312 ) 313 docker.succeed( 314 "docker images --format '{{.Repository}}' | grep -F '${examples.prefixedLayeredImage.imageName}'" 315 ) 316 317 with subtest("buildLayeredImage supports running chown with fakeRootCommands"): 318 docker.succeed( 319 "docker load --input='${examples.layeredImageWithFakeRootCommands}'" 320 ) 321 docker.succeed( 322 "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} sh -c 'stat -c '%u' /home/jane | grep -E ^1000$'" 323 ) 324 325 with subtest("Ensure docker load on merged images loads all of the constituent images"): 326 docker.succeed( 327 "docker load --input='${examples.mergedBashAndRedis}'" 328 ) 329 docker.succeed( 330 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.bash.imageName}-${examples.bash.imageTag}'" 331 ) 332 docker.succeed( 333 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.redis.imageName}-${examples.redis.imageTag}'" 334 ) 335 docker.succeed("docker run --rm ${examples.bash.imageName} bash --version") 336 docker.succeed("docker run --rm ${examples.redis.imageName} redis-cli --version") 337 docker.succeed("docker rmi ${examples.bash.imageName}") 338 docker.succeed("docker rmi ${examples.redis.imageName}") 339 340 with subtest( 341 "Ensure docker load on merged images loads all of the constituent images (missing tags)" 342 ): 343 docker.succeed( 344 "docker load --input='${examples.mergedBashNoTagAndRedis}'" 345 ) 346 docker.succeed( 347 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.bashNoTag.imageName}-${examples.bashNoTag.imageTag}'" 348 ) 349 docker.succeed( 350 "docker images --format '{{.Repository}}-{{.Tag}}' | grep -F '${examples.redis.imageName}-${examples.redis.imageTag}'" 351 ) 352 # we need to explicitly specify the generated tag here 353 docker.succeed( 354 "docker run --rm ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag} bash --version" 355 ) 356 docker.succeed("docker run --rm ${examples.redis.imageName} redis-cli --version") 357 docker.succeed("docker rmi ${examples.bashNoTag.imageName}:${examples.bashNoTag.imageTag}") 358 docker.succeed("docker rmi ${examples.redis.imageName}") 359 360 with subtest("mergeImages preserves owners of the original images"): 361 docker.succeed( 362 "docker load --input='${examples.mergedBashFakeRoot}'" 363 ) 364 docker.succeed( 365 "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} sh -c 'stat -c '%u' /home/jane | grep -E ^1000$'" 366 ) 367 ''; 368})