Set mtime to get more deterministic builds

This commit is contained in:
Utku Demir 2020-06-11 12:44:04 +12:00
parent 560201da66
commit 4e0109f873
No known key found for this signature in database
GPG Key ID: F3F8629C3E0BF60B
2 changed files with 52 additions and 15 deletions

View File

@ -752,8 +752,9 @@ rec {
imageTag="${tag}" imageTag="${tag}"
''} ''}
if [[ "$created" == "now" ]]; then # convert "created" to iso format
created="$(TZ=utc date --iso-8601="seconds")" if [[ "$created" != "now" ]]; then
created="$(date -Iseconds -d "$created")"
fi fi
# Create $maxLayers worth of Docker Layers, one layer per store path # Create $maxLayers worth of Docker Layers, one layer per store path

View File

@ -7,11 +7,12 @@ import hashlib
import tarfile import tarfile
import itertools import itertools
import threading import threading
from datetime import datetime
from collections import namedtuple from collections import namedtuple
# Adds the given store paths to as a tar to the given writable stream. # Adds the given store paths to as a tar to the given writable stream.
def archive_paths_to(obj, paths, add_nix, filter=None): def archive_paths_to(obj, paths, created, add_nix, filter=None):
filter = filter if filter else lambda i: i filter = filter if filter else lambda i: i
# gettarinfo makes the paths relative, this makes them # gettarinfo makes the paths relative, this makes them
@ -20,6 +21,10 @@ def archive_paths_to(obj, paths, add_nix, filter=None):
ti.name = "/" + ti.name ti.name = "/" + ti.name
return ti return ti
def apply_filters(ti):
ti.mtime = int(created.timestamp())
return filter(ti)
def dir(path): def dir(path):
ti = tarfile.TarInfo(path) ti = tarfile.TarInfo(path)
ti.type = tarfile.DIRTYPE ti.type = tarfile.DIRTYPE
@ -27,12 +32,12 @@ def archive_paths_to(obj, paths, add_nix, filter=None):
with tarfile.open(fileobj=obj, mode="w|") as tar: with tarfile.open(fileobj=obj, mode="w|") as tar:
if add_nix: if add_nix:
tar.addfile(dir("/nix")) tar.addfile(apply_filters(dir("/nix")))
tar.addfile(dir("/nix/store")) tar.addfile(apply_filters(dir("/nix/store")))
for path in paths: for path in paths:
ti = tar.gettarinfo(os.path.join("/", path)) ti = tar.gettarinfo(os.path.join("/", path))
tar.addfile(filter(append_root(ti))) tar.addfile(apply_filters(append_root(ti)))
for root, dirs, files in os.walk(path, topdown=True): for root, dirs, files in os.walk(path, topdown=True):
for name in itertools.chain(dirs, files): for name in itertools.chain(dirs, files):
@ -43,7 +48,7 @@ def archive_paths_to(obj, paths, add_nix, filter=None):
if ti.islnk(): if ti.islnk():
ti.type = tarfile.REGTYPE ti.type = tarfile.REGTYPE
ti = filter(ti) ti = apply_filters(ti)
if ti.isfile(): if ti.isfile():
with open(name, "rb") as f: with open(name, "rb") as f:
tar.addfile(ti, f) tar.addfile(ti, f)
@ -72,11 +77,17 @@ LayerInfo = namedtuple("LayerInfo", ["size", "checksum", "path", "paths"])
# Given a list of store paths 'paths', creates a layer add append it # Given a list of store paths 'paths', creates a layer add append it
# to tarfile 'tar'. Returns some a 'LayerInfo' for the layer. # to tarfile 'tar'. Returns some a 'LayerInfo' for the layer.
def add_layer_dir(tar, paths, add_nix=True, filter=None): def add_layer_dir(tar, paths, created, add_nix=True, filter=None):
assert all(i.startswith("/nix/store/") for i in paths) assert all(i.startswith("/nix/store/") for i in paths)
extract_checksum = ExtractChecksum() extract_checksum = ExtractChecksum()
archive_paths_to(extract_checksum, paths, add_nix=add_nix, filter=filter) archive_paths_to(
extract_checksum,
paths,
created=created,
add_nix=add_nix,
filter=filter
)
(checksum, size) = extract_checksum.extract() (checksum, size) = extract_checksum.extract()
path = f"{checksum}/layer.tar" path = f"{checksum}/layer.tar"
@ -86,7 +97,13 @@ def add_layer_dir(tar, paths, add_nix=True, filter=None):
read_fd, write_fd = os.pipe() read_fd, write_fd = os.pipe()
with open(read_fd, "rb") as read, open(write_fd, "wb") as write: with open(read_fd, "rb") as read, open(write_fd, "wb") as write:
def producer(): def producer():
archive_paths_to(write, paths, add_nix=add_nix, filter=filter) archive_paths_to(
write,
paths,
created=created,
add_nix=add_nix,
filter=filter
)
write.close() write.close()
threading.Thread(target=producer).start() threading.Thread(target=producer).start()
tar.addfile(ti, read) tar.addfile(ti, read)
@ -94,11 +111,17 @@ def add_layer_dir(tar, paths, add_nix=True, filter=None):
return LayerInfo(size=size, checksum=checksum, path=path, paths=paths) return LayerInfo(size=size, checksum=checksum, path=path, paths=paths)
def add_customisation_layer(tar, path): def add_customisation_layer(tar, path, created):
def filter(ti): def filter(ti):
ti.name = re.sub("^/nix/store/[^/]*", "", ti.name) ti.name = re.sub("^/nix/store/[^/]*", "", ti.name)
return ti return ti
return add_layer_dir(tar, [path], add_nix=False, filter=filter) return add_layer_dir(
tar,
[path],
created=created,
add_nix=False,
filter=filter
)
# Adds a file to the tarball with given path and contents. # Adds a file to the tarball with given path and contents.
@ -115,6 +138,12 @@ def add_bytes(tar, path, content):
with open(sys.argv[1], "r") as f: with open(sys.argv[1], "r") as f:
conf = json.load(f) conf = json.load(f)
created = (
datetime.now(tz=datetime.timezone.utc)
if conf["created"] == "now"
else datetime.fromisoformat(conf["created"])
)
with tarfile.open(mode="w|", fileobj=sys.stdout.buffer) as tar: with tarfile.open(mode="w|", fileobj=sys.stdout.buffer) as tar:
layers = [] layers = []
for num, store_layer in enumerate(conf["store_layers"]): for num, store_layer in enumerate(conf["store_layers"]):
@ -122,15 +151,22 @@ with tarfile.open(mode="w|", fileobj=sys.stdout.buffer) as tar:
"Creating layer", num, "Creating layer", num,
"from paths:", store_layer, "from paths:", store_layer,
file=sys.stderr) file=sys.stderr)
info = add_layer_dir(tar, store_layer) info = add_layer_dir(tar, store_layer, created=created)
layers.append(info) layers.append(info)
print("Creating the customisation layer...", file=sys.stderr) print("Creating the customisation layer...", file=sys.stderr)
layers.append(add_customisation_layer(tar, conf["customisation_layer"])) layers.append(
add_customisation_layer(
tar,
conf["customisation_layer"],
created=created
)
)
print("Adding manifests...", file=sys.stderr) print("Adding manifests...", file=sys.stderr)
image_json = { image_json = {
"created": conf["created"], "created": datetime.isoformat(created),
"architecture": conf["architecture"], "architecture": conf["architecture"],
"os": "linux", "os": "linux",
"config": conf["config"], "config": conf["config"],