Add history metadata for container builds
This commit adds the history section in contianerconfig. With it 'author', 'created_by' and 'comment' can be customized. In addition 'created' is always included with the image creation date time. 'created_by' entry is set to 'KIWI __version__' by default if nothing is provided. Fixes #852
This commit is contained in:
parent
2fa147dfb9
commit
f0612486dd
@ -52,7 +52,12 @@ class ContainerImageOCI(object):
|
||||
'expose_ports': ['80', '42'],
|
||||
'volumes': ['/var/log', '/tmp'],
|
||||
'environment': {'PATH': '/bin'},
|
||||
'labels': {'name': 'value'}
|
||||
'labels': {'name': 'value'},
|
||||
'history': {
|
||||
'created_by': 'some explanation here',
|
||||
'comment': 'some comment here',
|
||||
'author': 'tux'
|
||||
}
|
||||
}
|
||||
"""
|
||||
def __init__(self, root_dir, custom_args=None):
|
||||
@ -95,6 +100,12 @@ class ContainerImageOCI(object):
|
||||
self.oci_config['entry_subcommand'] = \
|
||||
Defaults.get_default_container_subcommand()
|
||||
|
||||
if 'history' not in self.oci_config:
|
||||
self.oci_config['history'] = {}
|
||||
if 'created_by' not in self.oci_config['history']:
|
||||
self.oci_config['history']['created_by'] = \
|
||||
Defaults.get_default_container_created_by()
|
||||
|
||||
self.oci = OCI(self.oci_config['container_tag'])
|
||||
|
||||
def create(self, filename, base_image):
|
||||
@ -117,7 +128,7 @@ class ContainerImageOCI(object):
|
||||
image_tar = ArchiveTar(base_image)
|
||||
image_tar.extract(self.oci.container_dir)
|
||||
|
||||
self.oci.init_layout(base_image)
|
||||
self.oci.init_layout(True if base_image else False)
|
||||
|
||||
self.oci.unpack(self.oci_root_dir)
|
||||
oci_root = DataSync(
|
||||
@ -133,7 +144,7 @@ class ContainerImageOCI(object):
|
||||
for tag in self.oci_config['additional_tags']:
|
||||
self.oci.add_tag(tag)
|
||||
|
||||
self.oci.set_config(self.oci_config)
|
||||
self.oci.set_config(self.oci_config, True if base_image else False)
|
||||
|
||||
self.oci.garbage_collect()
|
||||
|
||||
|
||||
@ -25,7 +25,10 @@ from pkg_resources import resource_filename
|
||||
|
||||
# project
|
||||
from .path import Path
|
||||
from .version import __githash__
|
||||
from .version import (
|
||||
__githash__,
|
||||
__version__
|
||||
)
|
||||
|
||||
|
||||
class Defaults(object):
|
||||
@ -1173,6 +1176,17 @@ class Defaults(object):
|
||||
"""
|
||||
return ['/bin/bash']
|
||||
|
||||
@classmethod
|
||||
def get_default_container_created_by(self):
|
||||
"""
|
||||
Provides the default 'created by' history entry for containers.
|
||||
|
||||
:return: the specific kiwi version used for the build
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return 'KIWI {0}'.format(__version__)
|
||||
|
||||
@classmethod
|
||||
def set_python_default_encoding_to_utf8(self):
|
||||
"""
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#
|
||||
import os
|
||||
from tempfile import mkdtemp
|
||||
from datetime import datetime
|
||||
|
||||
# project
|
||||
from kiwi.path import Path
|
||||
@ -51,6 +52,9 @@ class OCIBase(object):
|
||||
self.container_name = ':'.join(
|
||||
[self.container_dir, self.container_tag]
|
||||
)
|
||||
self.creation_date = datetime.utcnow().strftime(
|
||||
'%Y-%m-%dT%H:%M:%S+00:00'
|
||||
)
|
||||
|
||||
def init_layout(self, base_image=False):
|
||||
"""
|
||||
@ -81,7 +85,6 @@ class OCIBase(object):
|
||||
Implementation in specialized tool class
|
||||
|
||||
:param string oci_root_dir: root data directory
|
||||
:param string container_name: custom container_dir:tag specifier
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@ -96,7 +99,7 @@ class OCIBase(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_config(self, oci_config):
|
||||
def set_config(self, oci_config, base_image=False):
|
||||
"""
|
||||
Set list of meta data information such as entry_point,
|
||||
maintainer, etc... to the container. The validation of
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
from datetime import datetime
|
||||
|
||||
# project
|
||||
from kiwi.oci_tools.base import OCIBase
|
||||
@ -36,16 +35,10 @@ class OCIUmoci(OCIBase):
|
||||
The import and unpack of the base image is not a
|
||||
responsibility of this class and done beforehead
|
||||
|
||||
:param string base_image: True|False
|
||||
:param bool base_image: True|False
|
||||
"""
|
||||
if base_image:
|
||||
Command.run(
|
||||
[
|
||||
'umoci', 'config', '--image',
|
||||
'{0}:base_layer'.format(self.container_dir),
|
||||
'--tag', self.container_tag
|
||||
]
|
||||
)
|
||||
self.container_name = '{0}:base_layer'.format(self.container_dir)
|
||||
else:
|
||||
Command.run(
|
||||
['umoci', 'init', '--layout', self.container_dir]
|
||||
@ -87,24 +80,30 @@ class OCIUmoci(OCIBase):
|
||||
]
|
||||
)
|
||||
|
||||
def set_config(self, oci_config):
|
||||
def set_config(self, oci_config, base_image=False):
|
||||
"""
|
||||
Set list of meta data information such as entry_point,
|
||||
maintainer, etc... to the container.
|
||||
|
||||
:param list oci_config: meta data list
|
||||
:param bool base_image: True|False
|
||||
"""
|
||||
config_args = self._process_oci_config_to_arguments(oci_config)
|
||||
Command.run(
|
||||
[
|
||||
'umoci', 'config'
|
||||
] + config_args + [
|
||||
'--history.created', self.creation_date,
|
||||
'--image', self.container_name,
|
||||
'--created', datetime.utcnow().strftime(
|
||||
'%Y-%m-%dT%H:%M:%S+00:00'
|
||||
)
|
||||
'--tag', self.container_tag,
|
||||
'--created', self.creation_date
|
||||
]
|
||||
)
|
||||
if base_image:
|
||||
Command.run(['umoci', 'rm', '--image', self.container_name])
|
||||
self.container_name = self.container_name = ':'.join(
|
||||
[self.container_dir, self.container_tag]
|
||||
)
|
||||
|
||||
@classmethod # noqa:C091
|
||||
def _process_oci_config_to_arguments(self, oci_config):
|
||||
@ -168,8 +167,27 @@ class OCIUmoci(OCIBase):
|
||||
name, oci_config['labels'][name]
|
||||
))
|
||||
|
||||
arguments.extend(self._process_oci_history_to_arguments(oci_config))
|
||||
return arguments
|
||||
|
||||
@classmethod
|
||||
def _process_oci_history_to_arguments(self, oci_config):
|
||||
history_args = []
|
||||
if 'history' in oci_config:
|
||||
if 'comment' in oci_config['history']:
|
||||
history_args.append('--history.comment={0}'.format(
|
||||
oci_config['history']['comment']
|
||||
))
|
||||
if 'created_by' in oci_config['history']:
|
||||
history_args.append('--history.created_by={0}'.format(
|
||||
oci_config['history']['created_by']
|
||||
))
|
||||
if 'author' in oci_config['history']:
|
||||
history_args.append('--history.author={0}'.format(
|
||||
oci_config['history']['author']
|
||||
))
|
||||
return history_args
|
||||
|
||||
def garbage_collect(self):
|
||||
"""
|
||||
Cleanup unused data from operations
|
||||
|
||||
@ -2207,7 +2207,8 @@ div {
|
||||
k.expose? &
|
||||
k.volumes? &
|
||||
k.environment? &
|
||||
k.labels?
|
||||
k.labels? &
|
||||
k.history?
|
||||
}
|
||||
}
|
||||
|
||||
@ -2400,6 +2401,32 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
#==========================================
|
||||
# main block: <history>
|
||||
#
|
||||
div {
|
||||
k.history.created_by.attribute =
|
||||
## Specifies the 'created by' history record. By default set to 'KIWI'
|
||||
attribute created_by { text }
|
||||
|
||||
k.history.author.attribute =
|
||||
## Specifies the 'author' history record.
|
||||
attribute author { text }
|
||||
|
||||
k.history.attlist =
|
||||
k.history.created_by.attribute? &
|
||||
k.history.author.attribute?
|
||||
|
||||
k.history =
|
||||
## Provides details about the container history. Includes the
|
||||
## 'created by', 'author' as attributes and its content represents
|
||||
## the 'comment' entry.
|
||||
element history {
|
||||
k.history.attlist,
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
#==========================================
|
||||
# main block: <oemconfig>
|
||||
#
|
||||
|
||||
@ -3427,6 +3427,9 @@ section provides globally useful container information.</a:documentation>
|
||||
<optional>
|
||||
<ref name="k.labels"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="k.history"/>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
@ -3699,6 +3702,42 @@ At least one label must be configured</a:documentation>
|
||||
</element>
|
||||
</define>
|
||||
</div>
|
||||
<!--
|
||||
==========================================
|
||||
main block: <history>
|
||||
|
||||
-->
|
||||
<div>
|
||||
<define name="k.history.created_by.attribute">
|
||||
<attribute name="created_by">
|
||||
<a:documentation>Specifies the 'created by' history record. By default set to 'KIWI'</a:documentation>
|
||||
</attribute>
|
||||
</define>
|
||||
<define name="k.history.author.attribute">
|
||||
<attribute name="author">
|
||||
<a:documentation>Specifies the 'author' history record.</a:documentation>
|
||||
</attribute>
|
||||
</define>
|
||||
<define name="k.history.attlist">
|
||||
<interleave>
|
||||
<optional>
|
||||
<ref name="k.history.created_by.attribute"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="k.history.author.attribute"/>
|
||||
</optional>
|
||||
</interleave>
|
||||
</define>
|
||||
<define name="k.history">
|
||||
<element name="history">
|
||||
<a:documentation>Provides details about the container history. Includes the
|
||||
'created by', 'author' as attributes and its content represents
|
||||
the 'comment' entry.</a:documentation>
|
||||
<ref name="k.history.attlist"/>
|
||||
<text/>
|
||||
</element>
|
||||
</define>
|
||||
</div>
|
||||
<!--
|
||||
==========================================
|
||||
main block: <oemconfig>
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
# kiwi/schema/kiwi_for_generateDS.xsd
|
||||
#
|
||||
# Command line:
|
||||
# /home/ms/Project/kiwi/.tox/3.6/bin/generateDS.py -f --external-encoding="utf-8" --no-dates --no-warnings -o "kiwi/xml_parse.py" kiwi/schema/kiwi_for_generateDS.xsd
|
||||
# /home/david/work/kiwi/.env3/bin/generateDS.py -f --external-encoding="utf-8" --no-dates --no-warnings -o "kiwi/xml_parse.py" kiwi/schema/kiwi_for_generateDS.xsd
|
||||
#
|
||||
# Current working directory (os.getcwd()):
|
||||
# kiwi
|
||||
@ -4865,7 +4865,7 @@ class containerconfig(GeneratedsSuper):
|
||||
useful container information."""
|
||||
subclass = None
|
||||
superclass = None
|
||||
def __init__(self, name=None, tag=None, additionaltags=None, maintainer=None, user=None, workingdir=None, entrypoint=None, subcommand=None, expose=None, volumes=None, environment=None, labels=None):
|
||||
def __init__(self, name=None, tag=None, additionaltags=None, maintainer=None, user=None, workingdir=None, entrypoint=None, subcommand=None, expose=None, volumes=None, environment=None, labels=None, history=None):
|
||||
self.original_tagname_ = None
|
||||
self.name = _cast(None, name)
|
||||
self.tag = _cast(None, tag)
|
||||
@ -4897,6 +4897,10 @@ class containerconfig(GeneratedsSuper):
|
||||
self.labels = []
|
||||
else:
|
||||
self.labels = labels
|
||||
if history is None:
|
||||
self.history = []
|
||||
else:
|
||||
self.history = history
|
||||
def factory(*args_, **kwargs_):
|
||||
if CurrentSubclassModule_ is not None:
|
||||
subclass = getSubclassFromModule_(
|
||||
@ -4938,6 +4942,11 @@ class containerconfig(GeneratedsSuper):
|
||||
def add_labels(self, value): self.labels.append(value)
|
||||
def insert_labels_at(self, index, value): self.labels.insert(index, value)
|
||||
def replace_labels_at(self, index, value): self.labels[index] = value
|
||||
def get_history(self): return self.history
|
||||
def set_history(self, history): self.history = history
|
||||
def add_history(self, value): self.history.append(value)
|
||||
def insert_history_at(self, index, value): self.history.insert(index, value)
|
||||
def replace_history_at(self, index, value): self.history[index] = value
|
||||
def get_name(self): return self.name
|
||||
def set_name(self, name): self.name = name
|
||||
def get_tag(self): return self.tag
|
||||
@ -4957,7 +4966,8 @@ class containerconfig(GeneratedsSuper):
|
||||
self.expose or
|
||||
self.volumes or
|
||||
self.environment or
|
||||
self.labels
|
||||
self.labels or
|
||||
self.history
|
||||
):
|
||||
return True
|
||||
else:
|
||||
@ -5019,6 +5029,8 @@ class containerconfig(GeneratedsSuper):
|
||||
environment_.export(outfile, level, namespace_, name_='environment', pretty_print=pretty_print)
|
||||
for labels_ in self.labels:
|
||||
labels_.export(outfile, level, namespace_, name_='labels', pretty_print=pretty_print)
|
||||
for history_ in self.history:
|
||||
history_.export(outfile, level, namespace_, name_='history', pretty_print=pretty_print)
|
||||
def build(self, node):
|
||||
already_processed = set()
|
||||
self.buildAttributes(node, node.attrib, already_processed)
|
||||
@ -5082,6 +5094,11 @@ class containerconfig(GeneratedsSuper):
|
||||
obj_.build(child_)
|
||||
self.labels.append(obj_)
|
||||
obj_.original_tagname_ = 'labels'
|
||||
elif nodeName_ == 'history':
|
||||
obj_ = history.factory()
|
||||
obj_.build(child_)
|
||||
self.history.append(obj_)
|
||||
obj_.original_tagname_ = 'history'
|
||||
# end class containerconfig
|
||||
|
||||
|
||||
@ -5933,6 +5950,107 @@ class label(GeneratedsSuper):
|
||||
# end class label
|
||||
|
||||
|
||||
class history(GeneratedsSuper):
|
||||
"""Provides details about the container history. Includes the 'created
|
||||
by', 'author' as attributes and its content represents the
|
||||
'comment' entry."""
|
||||
subclass = None
|
||||
superclass = None
|
||||
def __init__(self, created_by=None, author=None, valueOf_=None, mixedclass_=None, content_=None):
|
||||
self.original_tagname_ = None
|
||||
self.created_by = _cast(None, created_by)
|
||||
self.author = _cast(None, author)
|
||||
self.valueOf_ = valueOf_
|
||||
if mixedclass_ is None:
|
||||
self.mixedclass_ = MixedContainer
|
||||
else:
|
||||
self.mixedclass_ = mixedclass_
|
||||
if content_ is None:
|
||||
self.content_ = []
|
||||
else:
|
||||
self.content_ = content_
|
||||
self.valueOf_ = valueOf_
|
||||
def factory(*args_, **kwargs_):
|
||||
if CurrentSubclassModule_ is not None:
|
||||
subclass = getSubclassFromModule_(
|
||||
CurrentSubclassModule_, history)
|
||||
if subclass is not None:
|
||||
return subclass(*args_, **kwargs_)
|
||||
if history.subclass:
|
||||
return history.subclass(*args_, **kwargs_)
|
||||
else:
|
||||
return history(*args_, **kwargs_)
|
||||
factory = staticmethod(factory)
|
||||
def get_created_by(self): return self.created_by
|
||||
def set_created_by(self, created_by): self.created_by = created_by
|
||||
def get_author(self): return self.author
|
||||
def set_author(self, author): self.author = author
|
||||
def get_valueOf_(self): return self.valueOf_
|
||||
def set_valueOf_(self, valueOf_): self.valueOf_ = valueOf_
|
||||
def hasContent_(self):
|
||||
if (
|
||||
(1 if type(self.valueOf_) in [int,float] else self.valueOf_)
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
def export(self, outfile, level, namespace_='', name_='history', namespacedef_='', pretty_print=True):
|
||||
imported_ns_def_ = GenerateDSNamespaceDefs_.get('history')
|
||||
if imported_ns_def_ is not None:
|
||||
namespacedef_ = imported_ns_def_
|
||||
if pretty_print:
|
||||
eol_ = '\n'
|
||||
else:
|
||||
eol_ = ''
|
||||
if self.original_tagname_ is not None:
|
||||
name_ = self.original_tagname_
|
||||
showIndent(outfile, level, pretty_print)
|
||||
outfile.write('<%s%s%s' % (namespace_, name_, namespacedef_ and ' ' + namespacedef_ or '', ))
|
||||
already_processed = set()
|
||||
self.exportAttributes(outfile, level, already_processed, namespace_, name_='history')
|
||||
outfile.write('>')
|
||||
self.exportChildren(outfile, level + 1, namespace_, name_, pretty_print=pretty_print)
|
||||
outfile.write(self.convert_unicode(self.valueOf_))
|
||||
outfile.write('</%s%s>%s' % (namespace_, name_, eol_))
|
||||
def exportAttributes(self, outfile, level, already_processed, namespace_='', name_='history'):
|
||||
if self.created_by is not None and 'created_by' not in already_processed:
|
||||
already_processed.add('created_by')
|
||||
outfile.write(' created_by=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.created_by), input_name='created_by')), ))
|
||||
if self.author is not None and 'author' not in already_processed:
|
||||
already_processed.add('author')
|
||||
outfile.write(' author=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.author), input_name='author')), ))
|
||||
def exportChildren(self, outfile, level, namespace_='', name_='history', fromsubclass_=False, pretty_print=True):
|
||||
pass
|
||||
def build(self, node):
|
||||
already_processed = set()
|
||||
self.buildAttributes(node, node.attrib, already_processed)
|
||||
self.valueOf_ = get_all_text_(node)
|
||||
if node.text is not None:
|
||||
obj_ = self.mixedclass_(MixedContainer.CategoryText,
|
||||
MixedContainer.TypeNone, '', node.text)
|
||||
self.content_.append(obj_)
|
||||
for child in node:
|
||||
nodeName_ = Tag_pattern_.match(child.tag).groups()[-1]
|
||||
self.buildChildren(child, node, nodeName_)
|
||||
return self
|
||||
def buildAttributes(self, node, attrs, already_processed):
|
||||
value = find_attr_value_('created_by', node)
|
||||
if value is not None and 'created_by' not in already_processed:
|
||||
already_processed.add('created_by')
|
||||
self.created_by = value
|
||||
value = find_attr_value_('author', node)
|
||||
if value is not None and 'author' not in already_processed:
|
||||
already_processed.add('author')
|
||||
self.author = value
|
||||
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
|
||||
if not fromsubclass_ and child_.tail is not None:
|
||||
obj_ = self.mixedclass_(MixedContainer.CategoryText,
|
||||
MixedContainer.TypeNone, '', child_.tail)
|
||||
self.content_.append(obj_)
|
||||
pass
|
||||
# end class history
|
||||
|
||||
|
||||
class oemconfig(GeneratedsSuper):
|
||||
"""The oemconfig element specifies the OEM image configuration options
|
||||
which are used to repartition and setup the system disk."""
|
||||
@ -7688,6 +7806,7 @@ __all__ = [
|
||||
"expose",
|
||||
"extension",
|
||||
"file",
|
||||
"history",
|
||||
"ignore",
|
||||
"image",
|
||||
"k_source",
|
||||
|
||||
@ -891,6 +891,9 @@ class XMLState(object):
|
||||
container_config.update(
|
||||
self._match_docker_labels()
|
||||
)
|
||||
container_config.update(
|
||||
self._match_docker_history()
|
||||
)
|
||||
return container_config
|
||||
|
||||
def set_container_config_tag(self, tag):
|
||||
@ -1812,6 +1815,23 @@ class XMLState(object):
|
||||
label.get_value()
|
||||
return container_labels
|
||||
|
||||
def _match_docker_history(self):
|
||||
container_config_section = self.get_build_type_containerconfig_section()
|
||||
container_history = {}
|
||||
if container_config_section:
|
||||
history = container_config_section.get_history()
|
||||
if history:
|
||||
container_history['history'] = {}
|
||||
if history[0].get_created_by():
|
||||
container_history['history']['created_by'] = \
|
||||
history[0].get_created_by()
|
||||
if history[0].get_author():
|
||||
container_history['history']['author'] = \
|
||||
history[0].get_author()
|
||||
container_history['history']['comment'] = \
|
||||
history[0].get_valueOf_()
|
||||
return container_history
|
||||
|
||||
def _solve_profile_dependencies(
|
||||
self, profile, available_profiles, current_profiles
|
||||
):
|
||||
|
||||
@ -128,6 +128,7 @@
|
||||
<label name="somelabel" value="labelvalue"/>
|
||||
<label name="someotherlabel" value="anotherlabelvalue"/>
|
||||
</labels>
|
||||
<history author="history author" created_by="created by text">This is a comment</history>
|
||||
</containerconfig>
|
||||
</type>
|
||||
<type image="iso" mediacheck="true"/>
|
||||
|
||||
@ -6,6 +6,7 @@ import mock
|
||||
from .test_helper import patch_open
|
||||
|
||||
from kiwi.container.oci import ContainerImageOCI
|
||||
from kiwi.version import __version__
|
||||
|
||||
|
||||
class TestContainerImageOCI(object):
|
||||
@ -47,7 +48,8 @@ class TestContainerImageOCI(object):
|
||||
assert container.oci_config == {
|
||||
'container_name': 'kiwi-container',
|
||||
'container_tag': 'latest',
|
||||
'entry_subcommand': ['/bin/bash']
|
||||
'entry_subcommand': ['/bin/bash'],
|
||||
'history': {'created_by': 'KIWI {0}'.format(__version__)}
|
||||
}
|
||||
|
||||
@patch('kiwi.defaults.Defaults.is_buildservice_worker')
|
||||
@ -106,15 +108,16 @@ class TestContainerImageOCI(object):
|
||||
|
||||
self.oci.create('result.tar', None)
|
||||
|
||||
self.oci.oci.init_layout.assert_called_once_with(None)
|
||||
self.oci.oci.init_layout.assert_called_once_with(False)
|
||||
self.oci.oci.unpack.assert_called_once_with('kiwi_oci_root_dir')
|
||||
self.oci.oci.repack.assert_called_once_with('kiwi_oci_root_dir')
|
||||
self.oci.oci.set_config.assert_called_once_with({
|
||||
'container_name': 'foo/bar',
|
||||
'additional_tags': ['current', 'foobar'],
|
||||
'container_tag': 'latest',
|
||||
'entry_subcommand': ['/bin/bash']
|
||||
})
|
||||
'entry_subcommand': ['/bin/bash'],
|
||||
'history': {'created_by': 'KIWI {0}'.format(__version__)}
|
||||
}, False)
|
||||
assert self.oci.oci.add_tag.call_args_list == [
|
||||
call('current'), call('foobar')
|
||||
]
|
||||
@ -164,17 +167,16 @@ class TestContainerImageOCI(object):
|
||||
|
||||
mock_create.assert_called_once_with('kiwi_oci_dir.XXXX/oci_layout')
|
||||
|
||||
self.oci.oci.init_layout.assert_called_once_with(
|
||||
'root_dir/image/image_file'
|
||||
)
|
||||
self.oci.oci.init_layout.assert_called_once_with(True)
|
||||
self.oci.oci.unpack.assert_called_once_with('kiwi_oci_root_dir')
|
||||
self.oci.oci.repack.assert_called_once_with('kiwi_oci_root_dir')
|
||||
self.oci.oci.set_config.assert_called_once_with({
|
||||
'container_name': 'foo/bar',
|
||||
'additional_tags': ['current', 'foobar'],
|
||||
'container_tag': 'latest',
|
||||
'entry_subcommand': ['/bin/bash']
|
||||
})
|
||||
'entry_subcommand': ['/bin/bash'],
|
||||
'history': {'created_by': 'KIWI {0}'.format(__version__)}
|
||||
}, True)
|
||||
assert self.oci.oci.add_tag.call_args_list == [
|
||||
call('current'), call('foobar')
|
||||
]
|
||||
|
||||
@ -6,9 +6,15 @@ from kiwi.oci_tools.umoci import OCIUmoci
|
||||
|
||||
|
||||
class TestOCIBase(object):
|
||||
@patch('kiwi.oci_tools.base.datetime')
|
||||
@patch('kiwi.oci_tools.base.mkdtemp')
|
||||
def setup(self, mock_mkdtemp):
|
||||
def setup(self, mock_mkdtemp, mock_datetime):
|
||||
mock_mkdtemp.return_value = 'tmpdir'
|
||||
strftime = Mock()
|
||||
strftime.strftime = Mock(return_value='current_date')
|
||||
mock_datetime.utcnow = Mock(
|
||||
return_value=strftime
|
||||
)
|
||||
self.oci = OCIUmoci('tag')
|
||||
|
||||
@patch('kiwi.oci_tools.umoci.Command.run')
|
||||
@ -22,12 +28,7 @@ class TestOCIBase(object):
|
||||
@patch('kiwi.oci_tools.umoci.Command.run')
|
||||
def test_init_layout_base_image(self, mock_Command_run):
|
||||
self.oci.init_layout(True)
|
||||
mock_Command_run.assert_called_once_with(
|
||||
[
|
||||
'umoci', 'config', '--image',
|
||||
'tmpdir/oci_layout:base_layer', '--tag', 'tag'
|
||||
]
|
||||
)
|
||||
assert self.oci.container_name == 'tmpdir/oci_layout:base_layer'
|
||||
|
||||
@patch('kiwi.oci_tools.umoci.Command.run')
|
||||
def test_unpack(self, mock_Command_run):
|
||||
@ -54,13 +55,7 @@ class TestOCIBase(object):
|
||||
)
|
||||
|
||||
@patch('kiwi.oci_tools.umoci.Command.run')
|
||||
@patch('kiwi.oci_tools.umoci.datetime')
|
||||
def test_set_config(self, mock_datetime, mock_Command_run):
|
||||
strftime = Mock()
|
||||
strftime.strftime = Mock(return_value='current_date')
|
||||
mock_datetime.utcnow = Mock(
|
||||
return_value=strftime
|
||||
)
|
||||
def test_set_config(self, mock_Command_run):
|
||||
oci_config = {
|
||||
'entry_command': ['/bin/bash', '-x'],
|
||||
'entry_subcommand': ['ls', '-l'],
|
||||
@ -70,7 +65,12 @@ class TestOCIBase(object):
|
||||
'expose_ports': ['80', '42'],
|
||||
'volumes': ['/var/log', '/tmp'],
|
||||
'environment': {'FOO': 'bar', 'PATH': '/bin'},
|
||||
'labels': {'a': 'value', 'b': 'value'}
|
||||
'labels': {'a': 'value', 'b': 'value'},
|
||||
'history': {
|
||||
'author': 'history author',
|
||||
'comment': 'This is a comment',
|
||||
'created_by': 'created by text'
|
||||
}
|
||||
}
|
||||
self.oci.set_config(oci_config)
|
||||
mock_Command_run.assert_called_once_with(
|
||||
@ -82,20 +82,34 @@ class TestOCIBase(object):
|
||||
'--config.exposedports=80', '--config.exposedports=42',
|
||||
'--config.env=FOO=bar', '--config.env=PATH=/bin',
|
||||
'--config.label=a=value', '--config.label=b=value',
|
||||
'--image', 'tmpdir/oci_layout:tag', '--created', 'current_date'
|
||||
'--history.comment=This is a comment',
|
||||
'--history.created_by=created by text',
|
||||
'--history.author=history author',
|
||||
'--history.created', 'current_date',
|
||||
'--image', 'tmpdir/oci_layout:tag', '--tag', 'tag',
|
||||
'--created', 'current_date'
|
||||
]
|
||||
)
|
||||
|
||||
@patch('kiwi.oci_tools.umoci.Command.run')
|
||||
@patch('kiwi.oci_tools.umoci.datetime')
|
||||
def test_set_config_dervied_image(self, mock_Command_run):
|
||||
self.oci.init_layout(True)
|
||||
self.oci.set_config({}, True)
|
||||
assert mock_Command_run.call_args_list == [
|
||||
call([
|
||||
'umoci', 'config',
|
||||
'--history.created', 'current_date',
|
||||
'--image', 'tmpdir/oci_layout:base_layer', '--tag', 'tag',
|
||||
'--created', 'current_date'
|
||||
]),
|
||||
call(['umoci', 'rm', '--image', 'tmpdir/oci_layout:base_layer'])
|
||||
]
|
||||
self.oci.container_name == 'tmpdir/oci_layout:tag'
|
||||
|
||||
@patch('kiwi.oci_tools.umoci.Command.run')
|
||||
def test_set_config_clear_inherited_commands(
|
||||
self, mock_datetime, mock_Command_run
|
||||
self, mock_Command_run
|
||||
):
|
||||
strftime = Mock()
|
||||
strftime.strftime = Mock(return_value='current_date')
|
||||
mock_datetime.utcnow = Mock(
|
||||
return_value=strftime
|
||||
)
|
||||
oci_config = {
|
||||
'entry_command': [],
|
||||
'entry_subcommand': []
|
||||
@ -104,7 +118,8 @@ class TestOCIBase(object):
|
||||
mock_Command_run.assert_called_once_with(
|
||||
[
|
||||
'umoci', 'config', '--clear=config.entrypoint',
|
||||
'--clear=config.cmd', '--image', 'tmpdir/oci_layout:tag',
|
||||
'--clear=config.cmd', '--history.created', 'current_date',
|
||||
'--image', 'tmpdir/oci_layout:tag', '--tag', 'tag',
|
||||
'--created', 'current_date'
|
||||
]
|
||||
)
|
||||
|
||||
@ -620,7 +620,7 @@ class TestXMLState(object):
|
||||
}
|
||||
|
||||
@patch('kiwi.logger.log.warning')
|
||||
def test_add_container_label_no_contianer_image_type(self, mock_log_warn):
|
||||
def test_add_container_label_no_container_image_type(self, mock_log_warn):
|
||||
xml_data = self.description.load()
|
||||
state = XMLState(xml_data, ['vmxFlavour'], 'vmx')
|
||||
state.add_container_config_label('somelabel', 'newlabelvalue')
|
||||
@ -652,7 +652,12 @@ class TestXMLState(object):
|
||||
'user': 'root',
|
||||
'volumes': ['/tmp', '/var/log'],
|
||||
'entry_command': ['/bin/bash', '-x'],
|
||||
'expose_ports': ['80', '8080']
|
||||
'expose_ports': ['80', '8080'],
|
||||
'history': {
|
||||
'author': 'history author',
|
||||
'comment': 'This is a comment',
|
||||
'created_by': 'created by text'
|
||||
}
|
||||
}
|
||||
xml_data = self.description.load()
|
||||
state = XMLState(xml_data, ['vmxFlavour'], 'docker')
|
||||
@ -666,7 +671,7 @@ class TestXMLState(object):
|
||||
'container_tag': 'container_tag',
|
||||
'workingdir': '/root',
|
||||
'user': 'root',
|
||||
'entry_command': []
|
||||
'entry_command': [],
|
||||
}
|
||||
xml_data = self.description.load()
|
||||
state = XMLState(xml_data, ['derivedContainer'], 'docker')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user