diff --git a/docs/configuration_file.md b/docs/configuration_file.md
index 3ea34af..5876c9a 100644
--- a/docs/configuration_file.md
+++ b/docs/configuration_file.md
@@ -110,3 +110,53 @@ Some options are also mandatory.
* `memory_limit` (*optional*, `string`) — memory limit to apply to build (for more info, see [documentation for resources](https://github.com/projectatomic/osbs-client/blob/master/docs/resource.md)
* `storage_limit` (*optional*, `string`) — storage limit to apply to build (for more info, see [documentation for resources](https://github.com/projectatomic/osbs-client/blob/master/docs/resource.md)
+
+
+## Build JSON Templates
+
+In the `build_json_dir` there must be `prod.json` and `prod_inner.json` which
+defines the [OpenShift Build](https://docs.openshift.org/latest/dev_guide/builds.html)
+specification that will be used to enable the specific
+[atomic-reactor](https://github.com/projectatomic/atomic-reactor) plugins that
+should be enabled and the config values for each.
+
+There is also a third file that is optional that can exist along side the
+previous two in `build_json_dir` which is `prod_customize.json` and it will
+provide the ability to set site-specific customizations such as removing,
+plugins, adding plugins, or overriding arguments passed to existing plugins.
+
+The syntax of `prod_customize.json` is as follows:
+
+```json
+{
+ "disable_plugins": [
+ {
+ "plugin_type": "foo_type",
+ "plugin_name": "foo_name"
+ },
+ {
+ "plugin_type": "bar_type",
+ "plugin_name": "bar_name"
+ }
+ ],
+
+ "enable_plugins": [
+ {
+ "plugin_type": "foo_type",
+ "plugin_name": "foo_name",
+ "plugin_args": {
+ "foo_arg1": "foo_value1",
+ "foo_arg2": "foo_value2"
+ }
+ }
+ ]
+}
+```
+
+Such that:
+
+* `disable_plugins` will define a list of lists that define the plugin type of the plugin that is to be removed (`prebuild_plugins`, `prepublish_plugins`, `postbuild_plugins`, `exit_plugins`) and the name of the plugin.
+
+* `enable_plugins` is used to add plugins or modify already enabled plugins by overriding args passed to the plugin, these must be defined as key-value pairs as illustrated above. It should be noted that plugins added here will be executed at the end of the list of plugins in that particular `plugin_type` (`prebuild_plugins`, `prepublish_plugins`, `postbuild_plugins`, `exit_plugins`).
+
+
diff --git a/inputs/prod_customize.json b/inputs/prod_customize.json
new file mode 100644
index 0000000..e874211
--- /dev/null
+++ b/inputs/prod_customize.json
@@ -0,0 +1,7 @@
+{
+ "disable_plugins": [
+ ],
+
+ "enable_plugins": [
+ ]
+}
diff --git a/osbs/build/build_request.py b/osbs/build/build_request.py
index a890d0b..aeceeaa 100644
--- a/osbs/build/build_request.py
+++ b/osbs/build/build_request.py
@@ -24,7 +24,7 @@ except ImportError:
from osbs.build.manipulate import DockJsonManipulator
from osbs.build.spec import BuildSpec
-from osbs.constants import SECRETS_PATH, DEFAULT_OUTER_TEMPLATE, DEFAULT_INNER_TEMPLATE
+from osbs.constants import SECRETS_PATH, DEFAULT_OUTER_TEMPLATE, DEFAULT_INNER_TEMPLATE, DEFAULT_CUSTOMIZE_CONF
from osbs.exceptions import OsbsException, OsbsValidationException
from osbs.utils import looks_like_git_hash, git_repo_humanish_part_from_uri
@@ -46,6 +46,7 @@ class BuildRequest(object):
self.build_json = None # rendered template
self._template = None # template loaded from filesystem
self._inner_template = None # dock json
+ self._customize_conf = None # site customize conf for _inner_template
self._dj = None
self._resource_limits = None
self._openshift_required_version = parse_version('1.0.6')
@@ -136,6 +137,20 @@ class BuildRequest(object):
return self._inner_template
@property
+ def customize_conf(self):
+ if self._customize_conf is None:
+ path = os.path.join(self.build_json_store, DEFAULT_CUSTOMIZE_CONF)
+ logger.debug("loading customize conf from path %s", path)
+ try:
+ with open(path, "r") as fp:
+ self._customize_conf= json.load(fp)
+ except IOError:
+ # File not found, which is perfectly fine. Set to empty string
+ self._customize_conf = {}
+
+ return self._customize_conf
+
+ @property
def dj(self):
if self._dj is None:
self._dj = DockJsonManipulator(self.template, self.inner_template)
@@ -649,10 +664,60 @@ class BuildRequest(object):
self.dj.dock_json_set_arg('postbuild_plugins', 'import_image',
'insecure_registry', True)
+ def render_customizations(self):
+ """
+ Customize prod_inner for site specific customizations
+ """
+
+ disable_plugins = self.customize_conf.get('disable_plugins', [])
+ if not disable_plugins:
+ logger.debug("No site-specific plugins to disable")
+ else:
+ for plugin_dict in disable_plugins:
+ try:
+ self.dj.remove_plugin(
+ plugin_dict['plugin_type'],
+ plugin_dict['plugin_name']
+ )
+ logger.debug(
+ "site-specific plugin disabled -> Type:{0} Name:{1}".format(
+ plugin_dict['plugin_type'],
+ plugin_dict['plugin_name']
+ )
+ )
+ except KeyError:
+ # Malformed config
+ logger.debug("Invalid custom configuration found for disable_plugins")
+
+ enable_plugins = self.customize_conf.get('enable_plugins', [])
+ if not enable_plugins:
+ logger.debug("No site-specific plugins to enable")
+ else:
+ for plugin_dict in enable_plugins:
+ try:
+ self.dj.add_plugin(
+ plugin_dict['plugin_type'],
+ plugin_dict['plugin_name'],
+ plugin_dict['plugin_args']
+ )
+ logger.debug(
+ "site-specific plugin enabled -> Type:{0} Name:{1} Args: {2}".format(
+ plugin_dict['plugin_type'],
+ plugin_dict['plugin_name'],
+ plugin_dict['plugin_args']
+ )
+ )
+ except KeyError:
+ # Malformed config
+ logger.debug("Invalid custom configuration found for enable_plugins")
+
+
def render(self, validate=True):
if validate:
self.spec.validate()
+ self.render_customizations()
+
# !IMPORTANT! can't be too long: https://github.com/openshift/origin/issues/733
self.template['metadata']['name'] = self.spec.name.value
self.render_resource_limits()
diff --git a/osbs/build/manipulate.py b/osbs/build/manipulate.py
index a6b64cf..a0fd1b8 100644
--- a/osbs/build/manipulate.py
+++ b/osbs/build/manipulate.py
@@ -50,6 +50,21 @@ class DockJsonManipulator(object):
self.dock_json[plugin_type].remove(p)
break
+ def add_plugin(self, plugin_type, plugin_name, args_dict):
+ """
+ if config has plugin, override it, else add it
+ """
+
+ plugin_modified = False
+
+ for plugin in self.dock_json[plugin_type]:
+ if plugin['name'] == plugin_name:
+ plugin['args'] = args_dict
+ plugin_modified = True
+
+ if not plugin_modified:
+ self.dock_json[plugin_type].append({"name": plugin_name, "args": args_dict})
+
def dock_json_has_plugin_conf(self, plugin_type, plugin_name):
"""
Check whether a plugin is configured.
diff --git a/osbs/constants.py b/osbs/constants.py
index 649626c..282f002 100644
--- a/osbs/constants.py
+++ b/osbs/constants.py
@@ -18,6 +18,7 @@ DEFAULT_CONFIGURATION_FILE = "/etc/osbs.conf"
DEFAULT_CONFIGURATION_SECTION = "default"
DEFAULT_OUTER_TEMPLATE = "prod.json"
DEFAULT_INNER_TEMPLATE = "prod_inner.json"
+DEFAULT_CUSTOMIZE_CONF = "prod_customize.json"
GENERAL_CONFIGURATION_SECTION = "general"
POD_FINISHED_STATES = ["failed", "succeeded"]
POD_FAILED_STATES = ["failed"]
diff --git a/tests/build/test_build_request.py b/tests/build/test_build_request.py
index add97d3..96c642e 100644
--- a/tests/build/test_build_request.py
+++ b/tests/build/test_build_request.py
@@ -1348,3 +1348,97 @@ class TestBuildRequest(object):
pull_base_image_plugin = get_plugin(
plugins, 'prebuild_plugins', 'pull_base_image')
assert pull_base_image_plugin is not None
+
+ def test_render_prod_custom_site_plugin_enable(self):
+ """
+ Test to make sure that when we attempt to enable a plugin, it is
+ actually enabled in the JSON for the build_request after running
+ build_request.render()
+ """
+
+ plugin_type = "exit_plugins"
+ plugin_name = "testing_exit_plugin"
+ plugin_args = {"foo": "bar"}
+
+ build_request = BuildRequest(INPUTS_PATH)
+ build_request.customize_conf['enable_plugins'].append(
+ {
+ "plugin_type": plugin_type,
+ "plugin_name": plugin_name,
+ "plugin_args": plugin_args
+ }
+ )
+ kwargs = get_sample_prod_params()
+ build_request.set_params(**kwargs)
+ build_request.render()
+
+ assert {
+ "name": plugin_name,
+ "args": plugin_args
+ } in build_request.dj.dock_json[plugin_type]
+
+ def test_render_prod_custom_site_plugin_disable(self):
+ """
+ Test to make sure that when we attempt to disable a plugin, it is
+ actually disabled in the JSON for the build_request after running
+ build_request.render()
+ """
+
+ plugin_type = "postbuild_plugins"
+ plugin_name = "compress"
+
+ build_request = BuildRequest(INPUTS_PATH)
+ build_request.customize_conf['disable_plugins'].append(
+ {
+ "plugin_type": plugin_type,
+ "plugin_name": plugin_name
+ }
+ )
+ kwargs = get_sample_prod_params()
+ build_request.set_params(**kwargs)
+ build_request.render()
+
+ for plugin in build_request.dj.dock_json[plugin_type]:
+ if plugin['name'] == plugin_name:
+ assert False
+
+ def test_render_prod_custom_site_plugin_override(self):
+ """
+ Test to make sure that when we attempt to override a plugin's args,
+ they are actually overridden in the JSON for the build_request
+ after running build_request.render()
+ """
+
+ plugin_type = "postbuild_plugins"
+ plugin_name = "compress"
+ plugin_args = {"foo": "bar"}
+
+ kwargs = get_sample_prod_params()
+
+ unmodified_build_request = BuildRequest(INPUTS_PATH)
+ unmodified_build_request.set_params(**kwargs)
+ unmodified_build_request.render()
+
+ for plugin_dict in unmodified_build_request.dj.dock_json[plugin_type]:
+ if plugin_dict['name'] == plugin_name:
+ plugin_index = unmodified_build_request.dj.dock_json[plugin_type].index(plugin_dict)
+
+ build_request = BuildRequest(INPUTS_PATH)
+ build_request.customize_conf['enable_plugins'].append(
+ {
+ "plugin_type": plugin_type,
+ "plugin_name": plugin_name,
+ "plugin_args": plugin_args
+ }
+ )
+ build_request.set_params(**kwargs)
+ build_request.render()
+
+
+ assert {
+ "name": plugin_name,
+ "args": plugin_args
+ } in build_request.dj.dock_json[plugin_type]
+
+ assert unmodified_build_request.dj.dock_json[plugin_type][plugin_index]['name'] == plugin_name
+ assert build_request.dj.dock_json[plugin_type][plugin_index]['name'] == plugin_name