Source code for kiwi.storage.subformat.vagrant_base

# Copyright (c) 2019 SUSE Linux GmbH.  All rights reserved.
#
# This file is part of kiwi.
#
# kiwi is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kiwi is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kiwi.  If not, see <http://www.gnu.org/licenses/>
#
import json
import os.path

from kiwi.utils.temporary import Temporary
from typing import (
    Dict, Optional, List
)

from kiwi import xml_parse
from kiwi.system.result import Result
from kiwi.storage.subformat.base import DiskFormatBase
from kiwi.storage.subformat.template.vagrant_config import VagrantConfigTemplate
from kiwi.command import Command

from kiwi.exceptions import (
    KiwiFormatSetupError
)


[docs] class DiskFormatVagrantBase(DiskFormatBase): """ Base class for creating vagrant boxes. The documentation of the vagrant box format can be found here: https://www.vagrantup.com/docs/boxes/format.html In a nutshell, a vagrant box is a tar, tar.gz or zip archive of the following: 1. ``metadata.json``: A json file that contains the name of the provider and arbitrary additional data (that vagrant doesn't care about). 2. ``Vagrantfile``: A Vagrantfile which defines the boxes' MAC address. It can be also used to define other settings of the box, e.g. the method via which the ``/vagrant/`` directory is shared. This file is either automatically generated by KIWI or we use a file that has been provided by the user (depends on the setting in `vagrantconfig.embebbed_vagrantfile`) 3. The actual virtual disk image: this is provider specific and vagrant simply forwards it to your virtual machine provider. Required methods/variables that child classes must implement: * :meth:`vagrant_post_init` post initializing method that has to specify the vagrant provider name in :attr:`provider` and the box name in :attr:`image_format`. Note: new providers also needs to be specified in the schema and the box name needs to be registered to :func:`kiwi.defaults.Defaults.get_disk_format_types` * :meth:`create_box_img` Optional methods: * :meth:`get_additional_metadata` * :meth:`get_additional_vagrant_config_settings` """
[docs] def post_init(self, custom_args: Dict['str', xml_parse.vagrantconfig]): """ vagrant disk format post initialization method store vagrantconfig information provided via custom_args :param dict custom_args: Contains instance of xml_parse::vagrantconfig .. code:: python {'vagrantconfig': object} """ if not custom_args or 'vagrantconfig' not in custom_args: raise KiwiFormatSetupError( 'object init requires custom_args hash with a vagrantconfig' ) if not custom_args['vagrantconfig']: raise KiwiFormatSetupError( 'no vagrantconfig provided' ) self.vagrantconfig = custom_args['vagrantconfig'] self.vagrant_post_init()
[docs] def vagrant_post_init(self) -> None: """ Vagrant provider specific post initialization method Setup vagrant provider and box name. This information must be set by the specialized provider class implementation to make the this base class methods effective """ self.provider: Optional[str] = None
[docs] def create_box_img(self, temp_image_dir: str) -> List[str]: """ Provider specific image creation step: this function creates the actual box image. It must be implemented by a child class. """ raise NotImplementedError
[docs] def create_image_format(self) -> None: """ Create a vagrant box for any provider. This includes: * creation of box metadata.json * creation of box Vagrantfile (either from scratch or by using the user provided Vagrantfile) * creation of result format tarball from the files created above """ if not self.image_format or not self.provider: raise NotImplementedError( 'vagrant_post_init: Missing provider and/or box name setup' ) temp_image_dir = Temporary(prefix='kiwi_vagrant_box.').new_dir() box_img_files = self.create_box_img(temp_image_dir.name) metadata_json = os.path.join(temp_image_dir.name, 'metadata.json') with open(metadata_json, 'w') as meta: meta.write(self._create_box_metadata()) vagrantfile = os.path.join(temp_image_dir.name, 'Vagrantfile') with open(vagrantfile, 'w') as vagrant: # autogenerate a Vagrantfile: if not self.vagrantconfig.get_embedded_vagrantfile(): embedded_vagrantfile = self._create_box_vagrantconfig() # user provided a Vagrantfile else: with open( os.path.join( self.xml_state.xml_data.description_dir, self.vagrantconfig.get_embedded_vagrantfile() ), 'r') as users_vagrantfile: embedded_vagrantfile = users_vagrantfile.read(-1) vagrant.write(embedded_vagrantfile) Command.run( [ 'tar', '-C', temp_image_dir.name, '-czf', self.get_target_file_path_for_format( self.image_format ), os.path.basename(metadata_json), os.path.basename(vagrantfile) ] + [ os.path.basename(box_img_file) for box_img_file in box_img_files ] )
[docs] def store_to_result(self, result: Result) -> None: """ Store result file of the vagrant format conversion into the provided result instance. In this case compression is unwanted because the box is already created as a compressed tarball :param object result: Instance of Result """ if not self.image_format: raise NotImplementedError( 'vagrant_post_init: Missing box name setup' ) result.add( key='disk_format_image', filename=self.get_target_file_path_for_format( self.image_format ), use_for_bundle=True, compress=False, shasum=True )
[docs] def get_additional_metadata(self) -> Dict: """ Provide :meth:`create_image_format` with additional metadata that will be included in ``metadata.json``. The default implementation returns an empty dictionary. :return: A dictionary that is serializable to JSON :rtype: dict """ return {}
[docs] def get_additional_vagrant_config_settings(self) -> str: """ Supply additional configuration settings for vagrant to be included in the resulting box. This function can be used by child classes to customize the behavior for different providers: the supplied configuration settings get forwarded to :func:`VagrantConfigTemplate.get_template` as the parameter ``custom_settings`` and included in the ``Vagrantfile``. The default implementation returns nothing. :return: additional vagrant settings :rtype: str """ return ''
def _create_box_metadata(self): metadata = self.get_additional_metadata() or {} metadata['provider'] = self.provider return json.dumps( metadata, sort_keys=True, indent=2, separators=(',', ': ') ) def _create_box_vagrantconfig(self): template = VagrantConfigTemplate() return template.get_template( custom_settings=self.get_additional_vagrant_config_settings() )