############################################################################## # # Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved. # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsibility of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # guarantees and support are strongly adviced to contract a Free Software # Service Company # # This program 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. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from collections import OrderedDict from .librecipe import unwrap, wrap, GenericSlapRecipe import six from zc.buildout import UserError def volatileOptions(options, volatile): def copy(): copy = options_copy() for key in volatile: copy.pop(key, None) return copy options_copy = options.copy options.copy = copy class Recipe(GenericSlapRecipe): """ Early initialization of published parameters. The '-init' option defines parameters that should be published before requesting any partitions, and how they are initialized. Example: [publish-early] recipe = slapos.cookbook:publish-early -init = foo gen-foo:x bar gen-bar:y bar = z [gen-foo] ... [publish] recipe = slapos.cookbook:publish.serialised -extends = publish-early ... Just before the recipe of [gen-foo] is instantiated, 'x' is overridden with the published value 'foo' if it exists. If its __init__ modifies 'x', the new value is published. To prevent [gen-foo] from being accessed too early, 'x' is then removed and the value can only be accessed with ${publish-early:foo}. Init sections are processed in the order of first appearance in the '-init' section, so that a init section can access a value that is generated by a previous one (above, [gen-bar] can access ${publish-early:foo}). Generated values don't end up in the buildout installed file, which is good if they're secret. Note however that buildout won't detect if values change and it may only call update(). ${publish-early:bar} is forced to 'z' (${gen-bar:y} ignored): a line like 'bar = z' is usually rendered conditionally with Jinja2. """ def __init__(self, buildout, name, options): GenericSlapRecipe.__init__(self, buildout, name, options) init = OrderedDict() for line in options['-init'].splitlines(): if line: k, v = line.split() if k not in options: section, v = v.split(':') try: init[section][k] = v except KeyError: init[section] = {k: v} if init: self.slap.initializeConnection(self.server_url, self.key_file, self.cert_file) computer_partition = self.slap.registerComputerPartition( self.computer_id, self.computer_partition_id) published_dict = unwrap(computer_partition.getConnectionParameterDict()) Options = buildout.Options if 'Options' in buildout.__dict__: def revertOptions(): buildout.Options = Options else: def revertOptions(): try: del buildout.Options except AttributeError: pass def newOptions(buildout, section, data): assert section == init_section, (section, init_section) revertOptions() self = buildout.Options(buildout, section, data) self.update(override) return self publish = False publish_dict = {} try: for init_section, init in six.iteritems(init): override = {} for k, v in six.iteritems(init): try: override[v] = published_dict[k] except KeyError: pass buildout.Options = newOptions init_section = buildout[init_section] if buildout.Options is not Options: raise UserError("%s section was already initialized" % init_section) new = {} for k, v in six.iteritems(init): try: options[k] = publish_dict[k] = new[v] = init_section.pop(v) except KeyError: pass if new != override: publish = True finally: revertOptions() if publish: computer_partition.setConnectionDict(wrap(publish_dict)) publish = [k for k in options if k != 'recipe' and not k.startswith('-')] publish += publish_dict publish_dict['-publish'] = ' '.join(publish) volatileOptions(options, list(publish_dict)) install = update = lambda self: None