[PATCH] add mechanism for general purpose parameter passing to Image Factory

Ian McLeod imcleod at redhat.com
Wed Oct 28 03:30:58 UTC 2015


We originally added semi-generic parameter passing for OVA support and
then overloaded it for Vagrant support, before breaking that back out
as specific image types.

This change adds a fairly generic mechanism for parameter passthrough
to plugins and the base builder that does not require patching the
builder and CLI each time a new class of parameter is added to Factory.

The immediate reason for doing this is to expose the Docker plugin's
ability to set custom labels, environments and commands, but it is
likely to be useful for other things in the future.
---
 builder/kojid | 39 ++++++++++++++++++++++++++++++++++-----
 cli/koji      | 15 ++++++++++++++-
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/builder/kojid b/builder/kojid
index 4ea93f2..ad427cd 100755
--- a/builder/kojid
+++ b/builder/kojid
@@ -3178,6 +3178,25 @@ class BaseImageTask(OzImageTask):
             raise koji.ApplianceError('Image status is %s: %s' %
                 (status, details))
 
+    def _mergeFactoryParams(self, img_opts, fixed_params):
+        """
+        Merge any KV pairs passed in via --factory-parameter CLI options
+        into an existing dictionary that will eventually be passed to
+        factory build commands.  This allows a fairly generic mechanism
+        for parameter passthrough to plugins and the base builder that does
+        not require patching the builder and CLI each time.
+
+        @args:
+            img_opts - an existing dict with any pre-existing parameters
+            fixed_params - list of parameters that must not be overriden
+        @returns:
+            nothing - dict is modified in place
+        """
+        if self.opts.get('factory_parameter'):
+            for kvp in self.opts.get('factory_parameter'):
+                if kvp[0] not in fixed_params:
+                    img_opts[kvp[0]] = kvp[1]
+
     def _buildBase(self, template, params, wait=True):
         """
         Build a base image using ImageFactory. This is a "raw" image.
@@ -3194,7 +3213,10 @@ class BaseImageTask(OzImageTask):
         #       may need to still upload ozlog and remove the log handler
         self.logger.info('dispatching a baseimg builder')
         self.logger.debug('templates: %s' % template)
-        self.logger.debug('params: %s' % params)
+        self.logger.debug('pre-merge params: %s' % params)
+        # We enforce various things related to the ks file - do not allow override
+        self._mergeFactoryParams(params, [ 'install_script' ])
+        self.logger.debug('post-merge params: %s' % params)
         base = self.bd.builder_for_base_image(template, parameters=params)
         if wait:
             base.base_thread.join()
@@ -3245,19 +3267,22 @@ class BaseImageTask(OzImageTask):
         if format == 'vagrant-virtualbox':
             format = 'vsphere-ova'
             img_opts['vsphere_ova_format'] = 'vagrant-virtualbox'
+            fixed_params = [ 'vsphere_ova_format' ]
         if format == 'vagrant-libvirt':
             format = 'rhevm-ova'
             img_opts['rhevm_ova_format'] = 'vagrant-libvirt'
+            fixed_params = [ 'rhevm_ova_format' ]
         if format == 'vagrant-vmware-fusion':
             format = 'vsphere-ova'
             img_opts['vsphere_ova_format'] = 'vagrant-vmware-fusion'
             # The initial disk image transform for VMWare Fusion/Workstation requires a "standard" VMDK
             # not the stream oriented format used for VirtualBox or regular VMWare OVAs
             img_opts['vsphere_vmdk_format'] = 'standard'
+            fixed_params = [ 'vsphere_ova_format', 'vsphere_vmdk_format' ]
         targ = self._do_target_image(self.base_img.base_image.identifier,
-            format.replace('-ova', ''), img_opts=img_opts)
+            format.replace('-ova', ''), img_opts=img_opts, fixed_params=fixed_params)
         targ2 = self._do_target_image(targ.target_image.identifier, 'OVA',
-            img_opts=img_opts)
+            img_opts=img_opts, fixed_params=fixed_params)
         return {'image': targ2.target_image.data}
 
     def _buildDocker(self, format):
@@ -3275,7 +3300,7 @@ class BaseImageTask(OzImageTask):
             'docker', img_opts=img_opts)
         return {'image': targ.target_image.data}
 
-    def _do_target_image(self, base_id, image_type, img_opts={}):
+    def _do_target_image(self, base_id, image_type, img_opts={}, fixed_params=[]):
         """
         A generic method for building what ImageFactory calls "target images".
         These are images based on a raw disk that was built before using the
@@ -3287,6 +3312,8 @@ class BaseImageTask(OzImageTask):
                 uses this to figure out what plugin to run
             img_opts - a dict of additional options that specific to the target
                 type we pass in via image_type
+            fixed_params - a list of parameter keys that should not be overriden
+                           by the --factory-parameter CLI
         @returns:
             A Builder() object from ImageFactory that contains information
             about the image building include state and progress.
@@ -3294,7 +3321,9 @@ class BaseImageTask(OzImageTask):
         # TODO: test the failure case where IF itself throws an exception
         #       ungracefully (missing a plugin for example)
         #       may need to still upload ozlog and remove the log handler
-        self.logger.debug('img_opts: %s' % img_opts)
+        self.logger.debug('img_opts_pre_merge: %s' % img_opts)
+        self._mergeFactoryParams(img_opts, fixed_params)
+        self.logger.debug('img_opts_post_merge: %s' % img_opts)
         target = self.bd.builder_for_target_image(image_type,
             image_id=base_id, template=None, parameters=img_opts)
         target.target_thread.join()
diff --git a/cli/koji b/cli/koji
index 64c5356..cbfa2d0 100755
--- a/cli/koji
+++ b/cli/koji
@@ -5306,6 +5306,11 @@ def handle_image_build(options, session, args):
     parser.add_option("--ova-option", action="append",
         help=_("Override a value in the OVA description XML. Provide a value " +
                "in a name=value format, such as 'ovf_memory_mb=6144'"))
+    parser.add_option("--factory-parameter", nargs=2, action="append",
+        help=_("Pass a parameter to Image Factory. The results are highly specific " +
+               "to the image format being created. This is a two argument parameter " +
+               "that can be specified an arbitrary number of times. For example: "
+               "--factory-parameter docker_cmd '[ \"/bin/echo Hello World\" ]'"))
     parser.add_option("--release", help=_("Forcibly set the release field"))
     parser.add_option("--repo", action="append",
         help=_("Specify a repo that will override the repo used to install " +
@@ -5355,6 +5360,14 @@ def handle_image_build(options, session, args):
             for k, v in config.items(section):
                 task_options.ova_option.append('%s=%s' % (k, v))
 
+        # as do factory-parameters
+        section = 'factory-parameters'
+        if config.has_section(section):
+            task_options.factory_parameter = [ ]
+            for k, v in config.items(section):
+                # We do this, rather than a dict, to match what argparse spits out
+                task_options.factory_parameter.append( (k, v) )
+
     else:
         if len(args) < 5:
             parser.error(_("At least five arguments are required: a name, " +
@@ -5483,7 +5496,7 @@ def _build_image_oz(options, task_opts, session, args):
     hub_opts = {}
     for opt in ('ksurl', 'ksversion', 'kickstart', 'scratch', 'repo',
                 'release', 'skip_tag', 'specfile', 'distro', 'format',
-                'disk_size', 'ova_option'):
+                'disk_size', 'ova_option', 'factory_parameter'):
         val = getattr(task_opts, opt, None)
         if val is not None:
             hub_opts[opt] = val
-- 
2.1.0



More information about the buildsys mailing list