Merge pull request #179801 from hercules-ci/fix-footgun-dockerTools-buildImage-contents

dockerTools.buildImage: Add copyToRoot to replace contents, explain usage

authored by Robert Hensing and committed by GitHub 613e7686 5cbfadba

+128 -40
+12 -3
doc/builders/images/dockertools.section.md
··· 20 fromImageName = null; 21 fromImageTag = "latest"; 22 23 - contents = pkgs.redis; 24 runAsRoot = '' 25 #!${pkgs.runtimeShell} 26 mkdir -p /data ··· 46 47 - `fromImageTag` can be used to further specify the tag of the base image within the repository, in case an image contains multiple tags. By default it's `null`, in which case `buildImage` will peek the first tag available for the base image. 48 49 - - `contents` is a derivation that will be copied in the new layer of the resulting image. This can be similarly seen as `ADD contents/ /` in a `Dockerfile`. By default it's `null`. 50 51 - `runAsRoot` is a bash script that will run as root in an environment that overlays the existing layers of the base image with the new resulting layer, including the previously copied `contents` derivation. This can be similarly seen as `RUN ...` in a `Dockerfile`. 52 ··· 81 name = "hello"; 82 tag = "latest"; 83 created = "now"; 84 - contents = pkgs.hello; 85 86 config.Cmd = [ "/bin/hello" ]; 87 }
··· 20 fromImageName = null; 21 fromImageTag = "latest"; 22 23 + copyToRoot = pkgs.buildEnv { 24 + name = "image-root"; 25 + paths = [ pkgs.redis ]; 26 + pathsToLink = [ "/bin" ]; 27 + }; 28 + 29 runAsRoot = '' 30 #!${pkgs.runtimeShell} 31 mkdir -p /data ··· 51 52 - `fromImageTag` can be used to further specify the tag of the base image within the repository, in case an image contains multiple tags. By default it's `null`, in which case `buildImage` will peek the first tag available for the base image. 53 54 + - `copyToRoot` is a derivation that will be copied in the new layer of the resulting image. This can be similarly seen as `ADD contents/ /` in a `Dockerfile`. By default it's `null`. 55 56 - `runAsRoot` is a bash script that will run as root in an environment that overlays the existing layers of the base image with the new resulting layer, including the previously copied `contents` derivation. This can be similarly seen as `RUN ...` in a `Dockerfile`. 57 ··· 86 name = "hello"; 87 tag = "latest"; 88 created = "now"; 89 + copyToRoot = pkgs.buildEnv { 90 + name = "image-root"; 91 + paths = [ pkgs.hello ]; 92 + pathsToLink = [ "/bin" ]; 93 + }; 94 95 config.Cmd = [ "/bin/hello" ]; 96 }
+9
nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
··· 305 </listitem> 306 <listitem> 307 <para> 308 memtest86+ was updated from 5.00-coreboot-002 to 6.00-beta2. 309 It is now the upstream version from https://www.memtest.org/, 310 as coreboot’s fork is no longer available.
··· 305 </listitem> 306 <listitem> 307 <para> 308 + <literal>dockerTools.buildImage</literal> deprecates the 309 + misunderstood <literal>contents</literal> parameter, in favor 310 + of <literal>copyToRoot</literal>. Use 311 + <literal>copyToRoot = buildEnv { ... };</literal> or similar 312 + if you intend to add packages to <literal>/bin</literal>. 313 + </para> 314 + </listitem> 315 + <listitem> 316 + <para> 317 memtest86+ was updated from 5.00-coreboot-002 to 6.00-beta2. 318 It is now the upstream version from https://www.memtest.org/, 319 as coreboot’s fork is no longer available.
+3
nixos/doc/manual/release-notes/rl-2211.section.md
··· 114 115 - Matrix Synapse now requires entries in the `state_group_edges` table to be unique, in order to prevent accidentally introducing duplicate information (for example, because a database backup was restored multiple times). If your Synapse database already has duplicate rows in this table, this could fail with an error and require manual remediation. 116 117 - memtest86+ was updated from 5.00-coreboot-002 to 6.00-beta2. It is now the upstream version from https://www.memtest.org/, as coreboot's fork is no longer available. 118 119 - Add udev rules for the Teensy family of microcontrollers.
··· 114 115 - Matrix Synapse now requires entries in the `state_group_edges` table to be unique, in order to prevent accidentally introducing duplicate information (for example, because a database backup was restored multiple times). If your Synapse database already has duplicate rows in this table, this could fail with an error and require manual remediation. 116 117 + - `dockerTools.buildImage` deprecates the misunderstood `contents` parameter, in favor of `copyToRoot`. 118 + Use `copyToRoot = buildEnv { ... };` or similar if you intend to add packages to `/bin`. 119 + 120 - memtest86+ was updated from 5.00-coreboot-002 to 6.00-beta2. It is now the upstream version from https://www.memtest.org/, as coreboot's fork is no longer available. 121 122 - Add udev rules for the Teensy family of microcontrollers.
+5 -1
nixos/tests/docker-tools-cross.nix
··· 24 hello1 = remoteCrossPkgs.dockerTools.buildImage { 25 name = "hello1"; 26 tag = "latest"; 27 - contents = remoteCrossPkgs.hello; 28 }; 29 30 hello2 = remoteCrossPkgs.dockerTools.buildLayeredImage {
··· 24 hello1 = remoteCrossPkgs.dockerTools.buildImage { 25 name = "hello1"; 26 tag = "latest"; 27 + copyToRoot = remoteCrossPkgs.buildEnv { 28 + name = "image-root"; 29 + pathsToLink = [ "/bin" ]; 30 + paths = [ remoteCrossPkgs.hello ]; 31 + }; 32 }; 33 34 hello2 = remoteCrossPkgs.dockerTools.buildLayeredImage {
+27 -11
pkgs/build-support/docker/default.nix
··· 332 , # JSON containing configuration and metadata for this layer. 333 baseJson 334 , # Files to add to the layer. 335 - contents ? null 336 , # When copying the contents into the image, preserve symlinks to 337 # directories (see `rsync -K`). Otherwise, transform those symlinks 338 # into directories. ··· 344 }: 345 runCommand "docker-layer-${name}" 346 { 347 - inherit baseJson contents extraCommands; 348 nativeBuildInputs = [ jshon rsync tarsum ]; 349 } 350 '' ··· 390 , # Script to run as root. Bash. 391 runAsRoot 392 , # Files to add to the layer. If null, an empty layer will be created. 393 - contents ? null 394 , # When copying the contents into the image, preserve symlinks to 395 # directories (see `rsync -K`). Otherwise, transform those symlinks 396 # into directories. ··· 418 419 inherit fromImage fromImageName fromImageTag diskSize; 420 421 - preMount = lib.optionalString (contents != null && contents != [ ]) '' 422 echo "Adding contents..." 423 - for item in ${escapeShellArgs (map (c: "${c}") (toList contents))}; do 424 echo "Adding $item..." 425 rsync -a${if keepContentsDirlinks then "K" else "k"} --chown=0:0 $item/ layer/ 426 done ··· 500 , # Tag of the parent image; will be read from the image otherwise. 501 fromImageTag ? null 502 , # Files to put on the image (a nix store path or list of paths). 503 - contents ? null 504 , # When copying the contents into the image, preserve symlinks to 505 # directories (see `rsync -K`). Otherwise, transform those symlinks 506 # into directories. ··· 517 diskSize ? 1024 518 , # Time of creation of the image. 519 created ? "1970-01-01T00:00:01Z" 520 , 521 }: 522 523 let 524 baseName = baseNameOf name; 525 526 # Create a JSON blob of the configuration. Set the date to unix zero. ··· 545 mkPureLayer 546 { 547 name = baseName; 548 - inherit baseJson contents keepContentsDirlinks extraCommands uid gid; 549 } else 550 mkRootLayer { 551 name = baseName; 552 inherit baseJson fromImage fromImageName fromImageTag 553 - contents keepContentsDirlinks runAsRoot diskSize 554 extraCommands; 555 }; 556 result = runCommand "docker-image-${baseName}.tar.gz" 557 { ··· 715 ''; 716 717 in 718 - result; 719 720 # Merge the tarballs of images built with buildImage into a single 721 # tarball that contains all images. Running `docker load` on the resulting ··· 776 # contents. The main purpose is to be able to use nix commands in 777 # the container. 778 # Be careful since this doesn't work well with multilayer. 779 - buildImageWithNixDb = args@{ contents ? null, extraCommands ? "", ... }: ( 780 buildImage (args // { 781 - extraCommands = (mkDbExtraCommand contents) + extraCommands; 782 }) 783 ); 784 785 buildLayeredImageWithNixDb = args@{ contents ? null, extraCommands ? "", ... }: ( 786 buildLayeredImage (args // { 787 extraCommands = (mkDbExtraCommand contents) + extraCommands;
··· 332 , # JSON containing configuration and metadata for this layer. 333 baseJson 334 , # Files to add to the layer. 335 + copyToRoot ? null 336 , # When copying the contents into the image, preserve symlinks to 337 # directories (see `rsync -K`). Otherwise, transform those symlinks 338 # into directories. ··· 344 }: 345 runCommand "docker-layer-${name}" 346 { 347 + inherit baseJson extraCommands; 348 + contents = copyToRoot; 349 nativeBuildInputs = [ jshon rsync tarsum ]; 350 } 351 '' ··· 391 , # Script to run as root. Bash. 392 runAsRoot 393 , # Files to add to the layer. If null, an empty layer will be created. 394 + # To add packages to /bin, use `buildEnv` or similar. 395 + copyToRoot ? null 396 , # When copying the contents into the image, preserve symlinks to 397 # directories (see `rsync -K`). Otherwise, transform those symlinks 398 # into directories. ··· 420 421 inherit fromImage fromImageName fromImageTag diskSize; 422 423 + preMount = lib.optionalString (copyToRoot != null && copyToRoot != [ ]) '' 424 echo "Adding contents..." 425 + for item in ${escapeShellArgs (map (c: "${c}") (toList copyToRoot))}; do 426 echo "Adding $item..." 427 rsync -a${if keepContentsDirlinks then "K" else "k"} --chown=0:0 $item/ layer/ 428 done ··· 502 , # Tag of the parent image; will be read from the image otherwise. 503 fromImageTag ? null 504 , # Files to put on the image (a nix store path or list of paths). 505 + copyToRoot ? null 506 , # When copying the contents into the image, preserve symlinks to 507 # directories (see `rsync -K`). Otherwise, transform those symlinks 508 # into directories. ··· 519 diskSize ? 1024 520 , # Time of creation of the image. 521 created ? "1970-01-01T00:00:01Z" 522 + , # Deprecated. 523 + contents ? null 524 , 525 }: 526 527 let 528 + checked = 529 + lib.warnIf (contents != null) 530 + "in docker image ${name}: The contents parameter is deprecated. Change to copyToRoot if the contents are designed to be copied to the root filesystem, such as when you use `buildEnv` or similar between contents and your packages. Use copyToRoot = buildEnv { ... }; or similar if you intend to add packages to /bin." 531 + lib.throwIf (contents != null && copyToRoot != null) "in docker image ${name}: You can not specify both contents and copyToRoot." 532 + ; 533 + 534 + rootContents = if copyToRoot == null then contents else copyToRoot; 535 + 536 baseName = baseNameOf name; 537 538 # Create a JSON blob of the configuration. Set the date to unix zero. ··· 557 mkPureLayer 558 { 559 name = baseName; 560 + inherit baseJson keepContentsDirlinks extraCommands uid gid; 561 + copyToRoot = rootContents; 562 } else 563 mkRootLayer { 564 name = baseName; 565 inherit baseJson fromImage fromImageName fromImageTag 566 + keepContentsDirlinks runAsRoot diskSize 567 extraCommands; 568 + copyToRoot = rootContents; 569 }; 570 result = runCommand "docker-image-${baseName}.tar.gz" 571 { ··· 729 ''; 730 731 in 732 + checked result; 733 734 # Merge the tarballs of images built with buildImage into a single 735 # tarball that contains all images. Running `docker load` on the resulting ··· 790 # contents. The main purpose is to be able to use nix commands in 791 # the container. 792 # Be careful since this doesn't work well with multilayer. 793 + # TODO: add the dependencies of the config json. 794 + buildImageWithNixDb = args@{ copyToRoot ? contents, contents ? null, extraCommands ? "", ... }: ( 795 buildImage (args // { 796 + extraCommands = (mkDbExtraCommand copyToRoot) + extraCommands; 797 }) 798 ); 799 800 + # TODO: add the dependencies of the config json. 801 buildLayeredImageWithNixDb = args@{ contents ? null, extraCommands ? "", ... }: ( 802 buildLayeredImage (args // { 803 extraCommands = (mkDbExtraCommand contents) + extraCommands;
+72 -25
pkgs/build-support/docker/examples.nix
··· 24 bash = buildImage { 25 name = "bash"; 26 tag = "latest"; 27 - contents = pkgs.bashInteractive; 28 }; 29 30 # 2. service example, layered on another image ··· 36 fromImage = bash; 37 # fromImage = debian; 38 39 - contents = pkgs.redis; 40 runAsRoot = '' 41 mkdir -p /data 42 ''; ··· 118 # 5. example of multiple contents, emacs and vi happily coexisting 119 editors = buildImage { 120 name = "editors"; 121 - contents = [ 122 - pkgs.coreutils 123 - pkgs.bash 124 - pkgs.emacs 125 - pkgs.vim 126 - pkgs.nano 127 - ]; 128 }; 129 130 # 6. nix example to play with the container nix store ··· 132 nix = buildImageWithNixDb { 133 name = "nix"; 134 tag = "latest"; 135 - contents = [ 136 - # nix-store uses cat program to display results as specified by 137 - # the image env variable NIX_PAGER. 138 - pkgs.coreutils 139 - pkgs.nix 140 - pkgs.bash 141 - ]; 142 config = { 143 Env = [ 144 "NIX_PAGER=cat" ··· 155 name = "onTopOfPulledImage"; 156 tag = "latest"; 157 fromImage = nixFromDockerHub; 158 - contents = [ pkgs.hello ]; 159 }; 160 161 # 8. regression test for erroneous use of eval and string expansion. ··· 163 runAsRootExtraCommands = pkgs.dockerTools.buildImage { 164 name = "runAsRootExtraCommands"; 165 tag = "latest"; 166 - contents = [ pkgs.coreutils ]; 167 # The parens here are to create problematic bash to embed and eval. In case 168 # this is *embedded* into the script (with nix expansion) the initial quotes 169 # will close the string and the following parens are unexpected ··· 176 unstableDate = pkgs.dockerTools.buildImage { 177 name = "unstable-date"; 178 tag = "latest"; 179 - contents = [ pkgs.coreutils ]; 180 created = "now"; 181 }; 182 ··· 265 name = "l3"; 266 fromImage = l2; 267 tag = "latest"; 268 - contents = [ pkgs.coreutils ]; 269 extraCommands = '' 270 mkdir -p tmp 271 echo layer3 > tmp/layer3 ··· 290 name = "child"; 291 fromImage = environmentVariablesParent; 292 tag = "latest"; 293 - contents = [ pkgs.coreutils ]; 294 config = { 295 Env = [ 296 "FROM_CHILD=true" ··· 424 name = "layers-unpack-order-${layerName}"; 425 tag = "latest"; 426 fromImage = parent; 427 - contents = [ pkgs.coreutils ]; 428 runAsRoot = '' 429 #!${pkgs.runtimeShell} 430 echo -n "${layerName}" >> /layer-order ··· 441 # buildImage without explicit tag 442 bashNoTag = pkgs.dockerTools.buildImage { 443 name = "bash-no-tag"; 444 - contents = pkgs.bashInteractive; 445 }; 446 447 # buildLayeredImage without explicit tag ··· 501 in crossPkgs.dockerTools.buildImage { 502 name = "hello-cross"; 503 tag = "latest"; 504 - contents = crossPkgs.hello; 505 }; 506 507 # layered image where a store path is itself a symlink ··· 643 build-image-with-path = buildImage { 644 name = "build-image-with-path"; 645 tag = "latest"; 646 - contents = [ pkgs.bashInteractive ./test-dummy ]; 647 }; 648 649 layered-image-with-path = pkgs.dockerTools.streamLayeredImage {
··· 24 bash = buildImage { 25 name = "bash"; 26 tag = "latest"; 27 + copyToRoot = pkgs.buildEnv { 28 + name = "image-root"; 29 + paths = [ pkgs.bashInteractive ]; 30 + pathsToLink = [ "/bin" ]; 31 + }; 32 }; 33 34 # 2. service example, layered on another image ··· 40 fromImage = bash; 41 # fromImage = debian; 42 43 + copyToRoot = pkgs.buildEnv { 44 + name = "image-root"; 45 + paths = [ pkgs.redis ]; 46 + pathsToLink = [ "/bin" ]; 47 + }; 48 + 49 runAsRoot = '' 50 mkdir -p /data 51 ''; ··· 127 # 5. example of multiple contents, emacs and vi happily coexisting 128 editors = buildImage { 129 name = "editors"; 130 + copyToRoot = pkgs.buildEnv { 131 + name = "image-root"; 132 + pathsToLink = [ "/bin" ]; 133 + paths = [ 134 + pkgs.coreutils 135 + pkgs.bash 136 + pkgs.emacs 137 + pkgs.vim 138 + pkgs.nano 139 + ]; 140 + }; 141 }; 142 143 # 6. nix example to play with the container nix store ··· 145 nix = buildImageWithNixDb { 146 name = "nix"; 147 tag = "latest"; 148 + copyToRoot = pkgs.buildEnv { 149 + name = "image-root"; 150 + pathsToLink = [ "/bin" ]; 151 + paths = [ 152 + # nix-store uses cat program to display results as specified by 153 + # the image env variable NIX_PAGER. 154 + pkgs.coreutils 155 + pkgs.nix 156 + pkgs.bash 157 + ]; 158 + }; 159 config = { 160 Env = [ 161 "NIX_PAGER=cat" ··· 172 name = "onTopOfPulledImage"; 173 tag = "latest"; 174 fromImage = nixFromDockerHub; 175 + copyToRoot = pkgs.buildEnv { 176 + name = "image-root"; 177 + pathsToLink = [ "/bin" ]; 178 + paths = [ pkgs.hello ]; 179 + }; 180 }; 181 182 # 8. regression test for erroneous use of eval and string expansion. ··· 184 runAsRootExtraCommands = pkgs.dockerTools.buildImage { 185 name = "runAsRootExtraCommands"; 186 tag = "latest"; 187 + copyToRoot = pkgs.buildEnv { 188 + name = "image-root"; 189 + pathsToLink = [ "/bin" ]; 190 + paths = [ pkgs.coreutils ]; 191 + }; 192 # The parens here are to create problematic bash to embed and eval. In case 193 # this is *embedded* into the script (with nix expansion) the initial quotes 194 # will close the string and the following parens are unexpected ··· 201 unstableDate = pkgs.dockerTools.buildImage { 202 name = "unstable-date"; 203 tag = "latest"; 204 + copyToRoot = pkgs.buildEnv { 205 + name = "image-root"; 206 + pathsToLink = [ "/bin" ]; 207 + paths = [ pkgs.coreutils ]; 208 + }; 209 created = "now"; 210 }; 211 ··· 294 name = "l3"; 295 fromImage = l2; 296 tag = "latest"; 297 + copyToRoot = pkgs.buildEnv { 298 + name = "image-root"; 299 + pathsToLink = [ "/bin" ]; 300 + paths = [ pkgs.coreutils ]; 301 + }; 302 extraCommands = '' 303 mkdir -p tmp 304 echo layer3 > tmp/layer3 ··· 323 name = "child"; 324 fromImage = environmentVariablesParent; 325 tag = "latest"; 326 + copyToRoot = pkgs.buildEnv { 327 + name = "image-root"; 328 + pathsToLink = [ "/bin" ]; 329 + paths = [ pkgs.coreutils ]; 330 + }; 331 config = { 332 Env = [ 333 "FROM_CHILD=true" ··· 461 name = "layers-unpack-order-${layerName}"; 462 tag = "latest"; 463 fromImage = parent; 464 + copyToRoot = pkgs.buildEnv { 465 + name = "image-root"; 466 + pathsToLink = [ "/bin" ]; 467 + paths = [ pkgs.coreutils ]; 468 + }; 469 runAsRoot = '' 470 #!${pkgs.runtimeShell} 471 echo -n "${layerName}" >> /layer-order ··· 482 # buildImage without explicit tag 483 bashNoTag = pkgs.dockerTools.buildImage { 484 name = "bash-no-tag"; 485 + # Not recommended. Use `buildEnv` between copy and packages to avoid file duplication. 486 + copyToRoot = pkgs.bashInteractive; 487 }; 488 489 # buildLayeredImage without explicit tag ··· 543 in crossPkgs.dockerTools.buildImage { 544 name = "hello-cross"; 545 tag = "latest"; 546 + copyToRoot = pkgs.buildEnv { 547 + name = "image-root"; 548 + pathsToLink = [ "/bin" ]; 549 + paths = [ crossPkgs.hello ]; 550 + }; 551 }; 552 553 # layered image where a store path is itself a symlink ··· 689 build-image-with-path = buildImage { 690 name = "build-image-with-path"; 691 tag = "latest"; 692 + # Not recommended. Use `buildEnv` between copy and packages to avoid file duplication. 693 + copyToRoot = [ pkgs.bashInteractive ./test-dummy ]; 694 }; 695 696 layered-image-with-path = pkgs.dockerTools.streamLayeredImage {