Make ACI generation consistent across calls

This will ensure that no matter which signature we write for the generated ACI, it is correct for that image.
This commit is contained in:
Joseph Schorr 2016-05-23 19:52:17 -04:00
parent f02d295dd8
commit 4ec3a6c231
5 changed files with 60 additions and 16 deletions

View file

@ -1,5 +1,6 @@
import json
import re
import calendar
from uuid import uuid4
@ -17,16 +18,21 @@ class ACIImage(TarImageFormatter):
def stream_generator(self, namespace, repository, tag, synthetic_image_id,
layer_json, get_image_iterator, get_layer_iterator, get_image_json):
image_mtime = 0
created = next(get_image_iterator()).created
if created is not None:
image_mtime = calendar.timegm(created.utctimetuple())
# ACI Format (.tar):
# manifest - The JSON manifest
# rootfs - The root file system
# Yield the manifest.
yield self.tar_file('manifest', self._build_manifest(namespace, repository, tag, layer_json,
synthetic_image_id))
manifest = self._build_manifest(namespace, repository, tag, layer_json, synthetic_image_id)
yield self.tar_file('manifest', manifest, mtime=image_mtime)
# Yield the merged layer dtaa.
yield self.tar_folder('rootfs')
yield self.tar_folder('rootfs', mtime=image_mtime)
layer_merger = StreamLayerMerger(get_layer_iterator, path_prefix='rootfs/')
for entry in layer_merger.get_generator():
@ -187,7 +193,6 @@ class ACIImage(TarImageFormatter):
env_vars.append(pieces)
manifest = {
"acKind": "ImageManifest",
"acVersion": "0.6.1",

View file

@ -6,6 +6,7 @@ from formats.tarimageformatter import TarImageFormatter
import copy
import json
import math
import calendar
class FileEstimationException(Exception):
""" Exception raised by build_docker_load_stream if the estimated size of the layer TAR
@ -28,6 +29,11 @@ class SquashedDockerImage(TarImageFormatter):
def stream_generator(self, namespace, repository, tag, synthetic_image_id,
layer_json, get_image_iterator, get_layer_iterator, get_image_json):
image_mtime = 0
created = next(get_image_iterator()).created
if created is not None:
image_mtime = calendar.timegm(created.utctimetuple())
# Docker import V1 Format (.tar):
# repositories - JSON file containing a repo -> tag -> image map
@ -45,17 +51,17 @@ class SquashedDockerImage(TarImageFormatter):
repositories = {}
repositories[hostname + '/' + namespace + '/' + repository] = synthetic_layer_info
yield self.tar_file('repositories', json.dumps(repositories))
yield self.tar_file('repositories', json.dumps(repositories), mtime=image_mtime)
# Yield the image ID folder.
yield self.tar_folder(synthetic_image_id)
yield self.tar_folder(synthetic_image_id, mtime=image_mtime)
# Yield the JSON layer data.
layer_json = SquashedDockerImage._build_layer_json(layer_json, synthetic_image_id)
yield self.tar_file(synthetic_image_id + '/json', json.dumps(layer_json))
yield self.tar_file(synthetic_image_id + '/json', json.dumps(layer_json), mtime=image_mtime)
# Yield the VERSION file.
yield self.tar_file(synthetic_image_id + '/VERSION', '1.0')
yield self.tar_file(synthetic_image_id + '/VERSION', '1.0', mtime=image_mtime)
# Yield the merged layer data's header.
estimated_file_size = 0
@ -72,7 +78,8 @@ class SquashedDockerImage(TarImageFormatter):
# Make sure the estimated file size is an integer number of bytes.
estimated_file_size = int(math.ceil(estimated_file_size))
yield self.tar_file_header(synthetic_image_id + '/layer.tar', estimated_file_size)
yield self.tar_file_header(synthetic_image_id + '/layer.tar', estimated_file_size,
mtime=image_mtime)
# Yield the contents of the merged layer.
yielded_size = 0

View file

@ -18,10 +18,10 @@ class TarImageFormatter(object):
layer_json, get_image_iterator, get_layer_iterator, get_image_json):
raise NotImplementedError
def tar_file(self, name, contents):
def tar_file(self, name, contents, mtime=None):
""" Returns the TAR binary representation for a file with the given name and file contents. """
length = len(contents)
tar_data = self.tar_file_header(name, length)
tar_data = self.tar_file_header(name, length, mtime=mtime)
tar_data += contents
tar_data += self.tar_file_padding(length)
return tar_data
@ -33,17 +33,24 @@ class TarImageFormatter(object):
return ''
def tar_file_header(self, name, file_size):
def tar_file_header(self, name, file_size, mtime=None):
""" Returns TAR file header data for a file with the given name and size. """
info = tarfile.TarInfo(name=name)
info.type = tarfile.REGTYPE
info.size = file_size
if mtime is not None:
info.mtime = mtime
return info.tobuf()
def tar_folder(self, name):
def tar_folder(self, name, mtime=None):
""" Returns TAR file header data for a folder with the given name. """
info = tarfile.TarInfo(name=name)
info.type = tarfile.DIRTYPE
if mtime is not None:
info.mtime = mtime
# allow the directory to be readable by non-root users
info.mode = 0755
return info.tobuf()