dockerTools.streamLayeredImage: Store the customisation layer as a tarball

This fixes as issue described here[1], where permissions set by 'extraCommands'
were ignored by Nix.

[1] https://github.com/NixOS/nixpkgs/pull/91084#issuecomment-669834938
This commit is contained in:
Utku Demir
2020-09-04 16:53:23 +12:00
parent 0ea35cf25c
commit ae82f81bfa
4 changed files with 73 additions and 85 deletions
@@ -33,7 +33,6 @@ function does all this.
import io
import os
import re
import sys
import json
import hashlib
@@ -45,21 +44,14 @@ from datetime import datetime, timezone
from collections import namedtuple
def archive_paths_to(obj, paths, mtime, add_nix, filter=None):
def archive_paths_to(obj, paths, mtime):
"""
Writes the given store paths as a tar file to the given stream.
obj: Stream to write to. Should have a 'write' method.
paths: List of store paths.
add_nix: Whether /nix and /nix/store directories should be
prepended to the archive.
filter: An optional transformation to be applied to TarInfo
objects. Should take a single TarInfo object and return
another one. Defaults to identity.
"""
filter = filter if filter else lambda i: i
# gettarinfo makes the paths relative, this makes them
# absolute again
def append_root(ti):
@@ -72,7 +64,7 @@ def archive_paths_to(obj, paths, mtime, add_nix, filter=None):
ti.gid = 0
ti.uname = "root"
ti.gname = "root"
return filter(ti)
return ti
def nix_root(ti):
ti.mode = 0o0555 # r-xr-xr-x
@@ -85,11 +77,9 @@ def archive_paths_to(obj, paths, mtime, add_nix, filter=None):
with tarfile.open(fileobj=obj, mode="w|") as tar:
# To be consistent with the docker utilities, we need to have
# these directories first when building layer tarballs. But
# we don't need them on the customisation layer.
if add_nix:
tar.addfile(apply_filters(nix_root(dir("/nix"))))
tar.addfile(apply_filters(nix_root(dir("/nix/store"))))
# these directories first when building layer tarballs.
tar.addfile(apply_filters(nix_root(dir("/nix"))))
tar.addfile(apply_filters(nix_root(dir("/nix/store"))))
for path in paths:
path = pathlib.Path(path)
@@ -136,7 +126,7 @@ class ExtractChecksum:
LayerInfo = namedtuple("LayerInfo", ["size", "checksum", "path", "paths"])
def add_layer_dir(tar, paths, mtime, add_nix=True, filter=None):
def add_layer_dir(tar, paths, mtime):
"""
Appends given store paths to a TarFile object as a new layer.
@@ -144,11 +134,6 @@ def add_layer_dir(tar, paths, mtime, add_nix=True, filter=None):
paths: List of store paths.
mtime: 'mtime' of the added files and the layer tarball.
Should be an integer representing a POSIX time.
add_nix: Whether /nix and /nix/store directories should be
added to a layer.
filter: An optional transformation to be applied to TarInfo
objects inside the layer. Should take a single TarInfo
object and return another one. Defaults to identity.
Returns: A 'LayerInfo' object containing some metadata of
the layer added.
@@ -164,8 +149,6 @@ def add_layer_dir(tar, paths, mtime, add_nix=True, filter=None):
extract_checksum,
paths,
mtime=mtime,
add_nix=add_nix,
filter=filter
)
(checksum, size) = extract_checksum.extract()
@@ -182,8 +165,6 @@ def add_layer_dir(tar, paths, mtime, add_nix=True, filter=None):
write,
paths,
mtime=mtime,
add_nix=add_nix,
filter=filter
)
write.close()
@@ -199,29 +180,38 @@ def add_layer_dir(tar, paths, mtime, add_nix=True, filter=None):
return LayerInfo(size=size, checksum=checksum, path=path, paths=paths)
def add_customisation_layer(tar, path, mtime):
def add_customisation_layer(target_tar, customisation_layer, mtime):
"""
Adds the contents of the store path as a new layer. This is different
than the 'add_layer_dir' function defaults in the sense that the contents
of a single store path will be added to the root of the layer. eg (without
the /nix/store prefix).
Adds the customisation layer as a new layer. This is layer is structured
differently; given store path has the 'layer.tar' and corresponding
sha256sum ready.
tar: 'tarfile.TarFile' object for the new layer to be added to.
path: A store path.
mtime: 'mtime' of the added files and the layer tarball. Should be an
integer representing a POSIX time.
customisation_layer: Path containing the layer archive.
mtime: 'mtime' of the added layer tarball.
"""
def filter(ti):
ti.name = re.sub("^/nix/store/[^/]*", "", ti.name)
return ti
return add_layer_dir(
tar,
[path],
mtime=mtime,
add_nix=False,
filter=filter
)
checksum_path = os.path.join(customisation_layer, "checksum")
with open(checksum_path) as f:
checksum = f.read().strip()
assert len(checksum) == 64, f"Invalid sha256 at ${checksum_path}."
layer_path = os.path.join(customisation_layer, "layer.tar")
path = f"{checksum}/layer.tar"
tarinfo = target_tar.gettarinfo(layer_path)
tarinfo.name = path
tarinfo.mtime = mtime
with open(layer_path, "rb") as f:
target_tar.addfile(tarinfo, f)
return LayerInfo(
size=None,
checksum=checksum,
path=path,
paths=[customisation_layer]
)
def add_bytes(tar, path, content, mtime):