@@ -44,7 +44,7 @@ let
4444 else
4545 import <nixpkgs> { inherit config; };
4646
47− inherit (pkgs) dockerTools stdenv buildEnv writeText;
47+ inherit (pkgs) dockerTools stdenv buildEnv writeText runCommand jq lib writeReferencesToFile jshon pigz coreutils findutils moreutils;
4848
4949 project-m36 = pkgs.haskell.packages.${compiler}.project-m36;
5050
@@ -238,7 +238,210 @@ divert 258 DIVERT # Divert pseudo-protocol
238238 '';
239239 };
240240
241− project-m36-docker = pkgs.dockerTools.buildImage {
241+ # following buildImage is copied from nixpkgs-20.03
242+ # to fix the arch always amd64 issue
243+ myarch = platform: {
244+ i686 = "386";
245+ x86_64 = "amd64";
246+ aarch64 = "arm64";
247+ arm = "arm";
248+ armv5tel = "arm";
249+ armv6l = "arm";
250+ armv7l = "arm";
251+ }.${platform.parsed.cpu.name} or (throw "Unsupported system");
252+
253+ MYTARGETARCH = myarch stdenv.targetPlatform;
254+
255+ # 1. extract the base image
256+ # 2. create the layer
257+ # 3. add layer deps to the layer itself, diffing with the base image
258+ # 4. compute the layer id
259+ # 5. put the layer in the image
260+ # 6. repack the image
261+ mybuildImage = with dockerTools; args@{
262+ # Image name.
263+ name,
264+ # Image tag, when null then the nix output hash will be used.
265+ tag ? null,
266+ # Parent image, to append to.
267+ fromImage ? null,
268+ # Name of the parent image; will be read from the image otherwise.
269+ fromImageName ? null,
270+ # Tag of the parent image; will be read from the image otherwise.
271+ fromImageTag ? null,
272+ # Files to put on the image (a nix store path or list of paths).
273+ contents ? null,
274+ # When copying the contents into the image, preserve symlinks to
275+ # directories (see `rsync -K`). Otherwise, transform those symlinks
276+ # into directories.
277+ keepContentsDirlinks ? false,
278+ # Docker config; e.g. what command to run on the container.
279+ config ? null,
280+ # Optional bash script to run on the files prior to fixturizing the layer.
281+ extraCommands ? "", uid ? 0, gid ? 0,
282+ # Optional bash script to run as root on the image when provisioning.
283+ runAsRoot ? null,
284+ # Size of the virtual machine disk to provision when building the image.
285+ diskSize ? 1024,
286+ # Time of creation of the image.
287+ created ? "1970-01-01T00:00:01Z",
288+ }:
289+
290+ let
291+ baseName = baseNameOf name;
292+
293+ # Create a JSON blob of the configuration. Set the date to unix zero.
294+ baseJson = let
295+ pure = writeText "${baseName}-config.json" (builtins.toJSON {
296+ inherit created config;
297+ architecture = MYTARGETARCH;
298+ os = "linux";
299+ });
300+ impure = runCommand "${baseName}-config.json"
301+ { nativeBuildInputs = [ jq ]; }
302+ ''
303+ jq ".created = \"$(TZ=utc date --iso-8601="seconds")\"" ${pure} > $out
304+ '';
305+ in if created == "now" then impure else pure;
306+
307+ layer =
308+ if runAsRoot == null
309+ then mkPureLayer {
310+ name = baseName;
311+ inherit baseJson contents keepContentsDirlinks extraCommands uid gid;
312+ } else mkRootLayer {
313+ name = baseName;
314+ inherit baseJson fromImage fromImageName fromImageTag
315+ contents keepContentsDirlinks runAsRoot diskSize
316+ extraCommands;
317+ };
318+ result = runCommand "docker-image-${baseName}.tar.gz" {
319+ nativeBuildInputs = [ jshon pigz coreutils findutils jq moreutils ];
320+ # Image name and tag must be lowercase
321+ imageName = lib.toLower name;
322+ imageTag = if tag == null then "" else lib.toLower tag;
323+ inherit fromImage baseJson;
324+ layerClosure = writeReferencesToFile layer;
325+ passthru.buildArgs = args;
326+ passthru.layer = layer;
327+ } ''
328+ ${lib.optionalString (tag == null) ''
329+ outName="$(basename "$out")"
330+ outHash=$(echo "$outName" | cut -d - -f 1)
331+ imageTag=$outHash
332+ ''}
333+ # Print tar contents:
334+ # 1: Interpreted as relative to the root directory
335+ # 2: With no trailing slashes on directories
336+ # This is useful for ensuring that the output matches the
337+ # values generated by the "find" command
338+ ls_tar() {
339+ for f in $(tar -tf $1 | xargs realpath -ms --relative-to=.); do
340+ if [[ "$f" != "." ]]; then
341+ echo "/$f"
342+ fi
343+ done
344+ }
345+ mkdir image
346+ touch baseFiles
347+ if [[ -n "$fromImage" ]]; then
348+ echo "Unpacking base image..."
349+ tar -C image -xpf "$fromImage"
350+ cat ./image/manifest.json | jq -r '.[0].Layers | .[]' > layer-list
351+ # Do not import the base image configuration and manifest
352+ chmod a+w image image/*.json
353+ rm -f image/*.json
354+ if [[ -z "$fromImageName" ]]; then
355+ fromImageName=$(jshon -k < image/repositories|head -n1)
356+ fi
357+ if [[ -z "$fromImageTag" ]]; then
358+ fromImageTag=$(jshon -e $fromImageName -k \
359+ < image/repositories|head -n1)
360+ fi
361+ parentID=$(jshon -e $fromImageName -e $fromImageTag -u \
362+ < image/repositories)
363+ for l in image/*/layer.tar; do
364+ ls_tar $l >> baseFiles
365+ done
366+ else
367+ touch layer-list
368+ fi
369+ chmod -R ug+rw image
370+ mkdir temp
371+ cp ${layer}/* temp/
372+ chmod ug+w temp/*
373+ for dep in $(cat $layerClosure); do
374+ find $dep >> layerFiles
375+ done
376+ echo "Adding layer..."
377+ # Record the contents of the tarball with ls_tar.
378+ ls_tar temp/layer.tar >> baseFiles
379+ # Append nix/store directory to the layer so that when the layer is loaded in the
380+ # image /nix/store has read permissions for non-root users.
381+ # nix/store is added only if the layer has /nix/store paths in it.
382+ if [ $(wc -l < $layerClosure) -gt 1 ] && [ $(grep -c -e "^/nix/store$" baseFiles) -eq 0 ]; then
383+ mkdir -p nix/store
384+ chmod -R 555 nix
385+ echo "./nix" >> layerFiles
386+ echo "./nix/store" >> layerFiles
387+ fi
388+ # Get the files in the new layer which were *not* present in
389+ # the old layer, and record them as newFiles.
390+ comm <(sort -n baseFiles|uniq) \
391+ <(sort -n layerFiles|uniq|grep -v ${layer}) -1 -3 > newFiles
392+ # Append the new files to the layer.
393+ tar -rpf temp/layer.tar --hard-dereference --sort=name --mtime="@$SOURCE_DATE_EPOCH" \
394+ --owner=0 --group=0 --no-recursion --files-from newFiles
395+ echo "Adding meta..."
396+ # If we have a parentID, add it to the json metadata.
397+ if [[ -n "$parentID" ]]; then
398+ cat temp/json | jshon -s "$parentID" -i parent > tmpjson
399+ mv tmpjson temp/json
400+ fi
401+ # Take the sha256 sum of the generated json and use it as the layer ID.
402+ # Compute the size and add it to the json under the 'Size' field.
403+ layerID=$(sha256sum temp/json|cut -d ' ' -f 1)
404+ size=$(stat --printf="%s" temp/layer.tar)
405+ cat temp/json | jshon -s "$layerID" -i id -n $size -i Size > tmpjson
406+ mv tmpjson temp/json
407+ # Use the temp folder we've been working on to create a new image.
408+ mv temp image/$layerID
409+ # Add the new layer ID to the end of the layer list
410+ (
411+ cat layer-list
412+ # originally this used `sed -i "1i$layerID" layer-list`, but
413+ # would fail if layer-list was completely empty.
414+ echo "$layerID/layer.tar"
415+ ) | sponge layer-list
416+ # Create image json and image manifest
417+ imageJson=$(cat ${baseJson} | jq ". + {\"rootfs\": {\"diff_ids\": [], \"type\": \"layers\"}}")
418+ manifestJson=$(jq -n "[{\"RepoTags\":[\"$imageName:$imageTag\"]}]")
419+ for layerTar in $(cat ./layer-list); do
420+ layerChecksum=$(sha256sum image/$layerTar | cut -d ' ' -f1)
421+ imageJson=$(echo "$imageJson" | jq ".history |= . + [{\"created\": \"$(jq -r .created ${baseJson})\"}]")
422+ # diff_ids order is from the bottom-most to top-most layer
423+ imageJson=$(echo "$imageJson" | jq ".rootfs.diff_ids |= . + [\"sha256:$layerChecksum\"]")
424+ manifestJson=$(echo "$manifestJson" | jq ".[0].Layers |= . + [\"$layerTar\"]")
425+ done
426+ imageJsonChecksum=$(echo "$imageJson" | sha256sum | cut -d ' ' -f1)
427+ echo "$imageJson" > "image/$imageJsonChecksum.json"
428+ manifestJson=$(echo "$manifestJson" | jq ".[0].Config = \"$imageJsonChecksum.json\"")
429+ echo "$manifestJson" > image/manifest.json
430+ # Store the json under the name image/repositories.
431+ jshon -n object \
432+ -n object -s "$layerID" -i "$imageTag" \
433+ -i "$imageName" > image/repositories
434+ # Make the image read-only.
435+ chmod -R a-w image
436+ echo "Cooking the image..."
437+ tar -C image --hard-dereference --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --xform s:'^./':: -c . | pigz -nT > $out
438+ echo "Finished."
439+ '';
440+
441+ in
442+ result;
443+
444+ project-m36-docker = mybuildImage {
242445 name = "project-m36";
243446 tag = project-m36.version;
244447 created = "now";