simplify consume_vendordata, move exclusion, consume_vendordata per instance

this simplifies consume_vendordata a bit, changes consume_vendordata to
be by default "PER_INSTANCE" (like userdata).

The other thing we do here is move the exlusion to be handled when walking the
data.  The benefit of doing it then is that we can exclude part-handlers.

Without the ability to exlude part handlers, exclusion is basically useless
(since part-handlers could accomplish anything they wanted).
This commit is contained in:
Scott Moser 2014-01-16 16:57:21 -05:00
parent 5dd23c78de
commit e6db6cb0e5
2 changed files with 52 additions and 82 deletions

View File

@ -187,6 +187,10 @@ def _escape_string(text):
def walker_callback(data, filename, payload, headers): def walker_callback(data, filename, payload, headers):
content_type = headers['Content-Type'] content_type = headers['Content-Type']
if content_type in data.get('excluded'):
LOG.debug('content_type "%s" is excluded', content_type)
return
if content_type in PART_CONTENT_TYPES: if content_type in PART_CONTENT_TYPES:
walker_handle_handler(data, content_type, filename, payload) walker_handle_handler(data, content_type, filename, payload)
return return

View File

@ -338,56 +338,40 @@ class Init(object):
processed_vd = "%s" % (self.datasource.get_vendordata()) processed_vd = "%s" % (self.datasource.get_vendordata())
util.write_file(self._get_ipath('vendordata'), processed_vd, 0600) util.write_file(self._get_ipath('vendordata'), processed_vd, 0600)
def _get_default_handlers(self, user_data=False, vendor_data=False, def _default_handlers(self, opts=None):
excluded=None): if opts is None:
opts = { opts = {}
opts.update({
'paths': self.paths, 'paths': self.paths,
'datasource': self.datasource, 'datasource': self.datasource,
} })
def conditional_get(cls, mod):
cls_name = cls.__name__.split('.')[-1]
_mod = getattr(cls, mod)
if not excluded:
return _mod(**opts)
if cls_name not in excluded:
_mod = getattr(cls, mod)
return _mod(**opts)
# TODO(harlowja) Hmmm, should we dynamically import these?? # TODO(harlowja) Hmmm, should we dynamically import these??
def_handlers = [ def_handlers = [
conditional_get(bh_part, 'BootHookPartHandler'), cc_part.CloudConfigPartHandler(**opts),
conditional_get(up_part, 'UpstartJobPartHandler'), ss_part.ShellScriptPartHandler(**opts),
bh_part.BootHookPartHandler(**opts),
up_part.UpstartJobPartHandler(**opts),
] ]
return def_handlers
# Add in the shell script part handler
if user_data:
def_handlers.extend([
conditional_get(cc_part, 'CloudConfigPartHandler'),
conditional_get(ss_part, 'ShellScriptPartHandler')])
# This changes the path for the vendor script execution
if vendor_data:
opts['script_path'] = "vendor_scripts"
opts['cloud_config_path'] = "vendor_cloud_config"
def_handlers.extend([
conditional_get(cc_part, 'CloudConfigPartHandler'),
conditional_get(ss_part, 'ShellScriptPartHandler')])
return [x for x in def_handlers if x is not None]
def _default_userdata_handlers(self): def _default_userdata_handlers(self):
return self._get_default_handlers(user_data=True) return self._default_handlers()
def _default_vendordata_handlers(self, excluded=None): def _default_vendordata_handlers(self):
return self._get_default_handlers(vendor_data=True, excluded=excluded) return self._default_handlers(
opts={'script_path': 'vendor_scripts',
'cloud_config_path': 'vendor_cloud_config'})
def _do_handlers(self, data_msg, c_handlers_list, frequency): def _do_handlers(self, data_msg, c_handlers_list, frequency,
excluded=None):
""" """
Generalized handlers suitable for use with either vendordata Generalized handlers suitable for use with either vendordata
or userdata or userdata
""" """
if excluded is None:
excluded = []
cdir = self.paths.get_cpath("handlers") cdir = self.paths.get_cpath("handlers")
idir = self._get_ipath("handlers") idir = self._get_ipath("handlers")
@ -450,7 +434,7 @@ class Init(object):
handlers.call_begin(mod, data, frequency) handlers.call_begin(mod, data, frequency)
c_handlers.initialized.append(mod) c_handlers.initialized.append(mod)
def walk_handlers(): def walk_handlers(excluded):
# Walk the user data # Walk the user data
part_data = { part_data = {
'handlers': c_handlers, 'handlers': c_handlers,
@ -463,9 +447,9 @@ class Init(object):
# to help write there contents to files with numbered # to help write there contents to files with numbered
# names... # names...
'handlercount': 0, 'handlercount': 0,
'excluded': excluded,
} }
handlers.walk(data_msg, handlers.walker_callback, handlers.walk(data_msg, handlers.walker_callback, data=part_data)
data=part_data)
def finalize_handlers(): def finalize_handlers():
# Give callbacks opportunity to finalize # Give callbacks opportunity to finalize
@ -482,7 +466,7 @@ class Init(object):
try: try:
init_handlers() init_handlers()
walk_handlers() walk_handlers(excluded)
finally: finally:
finalize_handlers() finalize_handlers()
@ -503,67 +487,49 @@ class Init(object):
# objects before the load of the userdata happened, # objects before the load of the userdata happened,
# this is expected. # this is expected.
def _consume_vendordata(self, frequency=PER_ALWAYS): def _consume_vendordata(self, frequency=PER_INSTANCE):
""" """
Consume the vendordata and run the part handlers on it Consume the vendordata and run the part handlers on it
""" """
if not self.datasource.has_vendordata(): # User-data should have been consumed first.
LOG.info("datasource did not provide vendor data") # So we merge the other available cloud-configs (everything except
# vendor provided), and check whether or not we should consume
# vendor data at all. That gives user or system a chance to override.
if not self.datasource.get_vendordata_raw():
LOG.debug("no vendordata from datasource")
return return
# User-data should have been consumed first. If it has, then we can
# read it and simply parse it. This means that the datasource can
# define if the vendordata can be consumed too....i.e this method
# gives us a lot of flexibility.
_cc_merger = helpers.ConfigMerger(paths=self._paths, _cc_merger = helpers.ConfigMerger(paths=self._paths,
datasource=self.datasource, datasource=self.datasource,
additional_fns=[], additional_fns=[],
base_cfg=self.cfg, base_cfg=self.cfg,
include_vendor=False) include_vendor=False)
_cc = _cc_merger.cfg vdcfg = _cc_merger.cfg.get('vendor_data', {})
if not self.datasource.consume_vendordata(): if not isinstance(vdcfg, dict):
if not isinstance(_cc, dict): vdcfg = {'enabled': False}
LOG.info(("userdata does explicitly allow vendordata " LOG.warn("invalid 'vendor_data' setting. resetting to: %s", vdcfg)
"consumption"))
if not util.is_true(vdcfg.get('enabled')):
LOG.debug("vendordata consumption is disabled.")
return return
if 'vendor_data' not in _cc:
LOG.info(("no 'vendor_data' directive found in the"
"conf files. Skipping consumption of vendordata"))
return
# This allows for the datasource to signal explicit conditions when
# when the user has opted in to user-data
if self.datasource.consume_vendordata():
LOG.info(("datasource has indicated that vendordata that user"
" opted-in via another channel"))
vdc = _cc.get('vendor_data')
no_handlers = None
if isinstance(vdc, dict):
enabled = vdc.get('enabled') enabled = vdc.get('enabled')
no_handlers = vdc.get('no_run') no_handlers = vdc.get('disabled_handlers', None)
if enabled is None: LOG.debug("vendor data will be consumed. disabled_handlers=%s",
LOG.info("vendordata will not be consumed: user has not opted-in") no_handlers)
return
elif util.is_false(enabled):
LOG.info("user has requested NO vendordata consumption")
return
LOG.info("vendor data will be consumed")
# Ensure vendordata source fetched before activation (just incase) # Ensure vendordata source fetched before activation (just incase)
vendor_data_msg = self.datasource.get_vendordata(True) vendor_data_msg = self.datasource.get_vendordata()
# This keeps track of all the active handlers, while excluding what the # This keeps track of all the active handlers, while excluding what the
# users doesn't want run, i.e. boot_hook, cloud_config, shell_script # users doesn't want run, i.e. boot_hook, cloud_config, shell_script
c_handlers_list = self._default_vendordata_handlers( c_handlers_list = self._default_vendordata_handlers()
excluded=no_handlers)
# Run the handlers # Run the handlers
self._do_handlers(vendor_data_msg, c_handlers_list, frequency) self._do_handlers(vendor_data_msg, c_handlers_list, frequency,
excluded=no_handlers)
def _consume_userdata(self, frequency=PER_INSTANCE): def _consume_userdata(self, frequency=PER_INSTANCE):
""" """
@ -574,7 +540,7 @@ class Init(object):
user_data_msg = self.datasource.get_userdata(True) user_data_msg = self.datasource.get_userdata(True)
# This keeps track of all the active handlers # This keeps track of all the active handlers
c_handlers_list = self._default_userdata_handlers() c_handlers_list = self._default_handlers()
# Run the handlers # Run the handlers
self._do_handlers(user_data_msg, c_handlers_list, frequency) self._do_handlers(user_data_msg, c_handlers_list, frequency)