diff --git a/setup.py b/setup.py index 30ff9f6860f8a36a2205242a201f22d42b300901..02d80ab86113ef90b28719bb91555fed7c016c99 100755 --- a/setup.py +++ b/setup.py @@ -150,6 +150,7 @@ setup(name=name, 'postgres.export = slapos.recipe.postgres.backup:ExportRecipe', 'postgres.import = slapos.recipe.postgres.backup:ImportRecipe', 'proactive = slapos.recipe.proactive:Recipe', + 'promise.plugin= slapos.recipe.promise_plugin:Recipe', 'publish = slapos.recipe.publish:Recipe', 'publish.serialised = slapos.recipe.publish:Serialised', 'publish-early = slapos.recipe.publish_early:Recipe', diff --git a/slapos/recipe/promise_plugin.py b/slapos/recipe/promise_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..0df9a392227e009976aa268d7fe0f3639992cb7f --- /dev/null +++ b/slapos/recipe/promise_plugin.py @@ -0,0 +1,135 @@ +############################################################################## +# +# Copyright (c) 2018 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. +# +############################################################################## +import logging, os, sys +import zc.buildout.easy_install +from slapos.recipe.librecipe import GenericBaseRecipe + +script_template = '''# This script is auto generated by slapgrid, do not edit! +import sys +sys.path[0:0] = [ + %(path)s +] + +extra_config_dict = { + %(config)s +} + +# We want to cleanup all imported modules from slapos namespace, because +# they will conflict with slapos.core. +# In fact as slapos.grid is already imported, the promise cannot reimport +# his own slapos.grid from an updated sys.path. Then all module imports which +# are not in slapos.core will fail. +# +# call reload(slapos) only solve a part of problem because not all modules +# will be reloaded, and some new modules won't be added. +# The solution is to delete all cached 'slapos' modules as well as all cached +# 'pkg_resources' modules which is responsible of namespace declaration. +# They will be re-imported again using the updated sys.path +for module in sys.modules.keys(): + if 'slapos' in module or 'pkg_resources' in module: + del sys.modules[module] + +import slapos.grid.promise + +%(content)s +''' + +class Recipe(GenericBaseRecipe): + + _WORKING_SET_CACHE_NAME = "slapos.cookbook_pplugin_ws_cache" + + def __init__(self, buildout, name, options): + buildout_section = buildout['buildout'] + options['eggs-directory'] = buildout_section['eggs-directory'] + options['develop-eggs-directory'] = buildout_section['develop-eggs-directory'] + super(Recipe, self).__init__(buildout, name, options) + + def _get_cache_storage(self): + """Return a mapping where to store generated working sets. + from https://github.com/buildout/buildout/blob/master/zc.recipe.egg_/src/zc/recipe/egg/egg.py#L170 + """ + cache_storage = getattr( + self.buildout, + self._WORKING_SET_CACHE_NAME, + None + ) + if cache_storage is None: + cache_storage = {} + setattr( + self.buildout, + self._WORKING_SET_CACHE_NAME, + cache_storage + ) + return cache_storage + + def install(self): + develop_eggs_dir = self.options['develop-eggs-directory'] + eggs_dir = self.options['eggs-directory'] + egg_list = [ + egg.strip() + for egg in self.options['eggs'].split('\n') + if egg.strip() + ] + + cache_storage = self._get_cache_storage() + cache_key = ( + tuple(egg_list), + eggs_dir, + develop_eggs_dir, + ) + if cache_key not in cache_storage: + working_set = zc.buildout.easy_install.working_set( + egg_list, + [develop_eggs_dir, eggs_dir] + ) + cache_storage[cache_key] = working_set + else: + working_set = cache_storage[cache_key] + + content = self.options['content'].strip() + output = self.options['output'] + mode = self.options.get('mode', '0600') + path_list_string = "" + for dist in working_set: + path_list_string += ' "%s",\n' % dist.location + + content_string = '\n'.join([line.lstrip() for line in content.split('\n')]) + config_string = "" + for key in self.options: + if key.startswith('config-'): + config_string += " '%s': '%s',\n" % (key[7:], self.options[key]) + + option_dict = dict(path=path_list_string.strip(), + content=content_string, + config=config_string.strip()) + with open(output, 'w') as f: + f.write(script_template % option_dict) + + os.chmod(output, int(mode, 8)) + return (output,) + + update = install