Merge lp:~jbaudoux/anybox.recipe.openerp/20130908_relative_paths into lp:anybox.recipe.openerp

Proposed by Jacques-Etienne Baudoux
Status: Needs review
Proposed branch: lp:~jbaudoux/anybox.recipe.openerp/20130908_relative_paths
Merge into: lp:anybox.recipe.openerp
Diff against target: 140 lines (+51/-13)
2 files modified
anybox/recipe/openerp/base.py (+9/-0)
anybox/recipe/openerp/server.py (+42/-13)
To merge this branch: bzr merge lp:~jbaudoux/anybox.recipe.openerp/20130908_relative_paths
Reviewer Review Type Date Requested Status
Anybox Pending
Review via email: mp+184473@code.launchpad.net

Description of the change

Improvement to be compliant with buildout option "relative_paths = true"

To post a comment you must log in.
423. By Jacques-Etienne Baudoux

Fix format string

Revision history for this message
Georges Racinet (gracinet) wrote :

Really interesting if I understand it correctly, this the forever delayed "TODO investigate these options" ! Thank you for the contribution.

I'd need to read the details of _relativitize(), and especially understand why the one from zc.buildout.easy_install is not enough, but one thing is for sure : this will need dedicated unit tests and documentation : if OpenERP buildouts can be made relocatable, then the doc must state it explicitely, along with limitations (simply pointing out that it behaves exactly as zc.recipe.egg in that respect, it that is the case).

Not yet decided whether this can go to 1.7 or trunk. There's nothing that seems to be so dangerous (a breakage would not stay unnoticed for long).

Revision history for this message
Georges Racinet (gracinet) wrote :

inclusion in trunk could be done faster, if that matters to you.
Also, with the new Sphinx documentation, that seems to be the most sensible choice for a new feature (even if we should have supported this from the beginning). Mention of relocability can be done in the non-specific parts of doc/configuration.rst, I guess.

Revision history for this message
Jacques-Etienne Baudoux (jbaudoux) wrote :

It allows me to deploy in a chroot subdirectory.
It's now compliant with the relative_paths option of buildout for the python scripts.
For your information, it's, however, still missing some hack to take care the the addons path in the openerp.cfg. I didn't found a nice way to solve that. For now, I'm solving it by creating a symling this: mybuildoutdirfullpath/chroot/mybuildoutdirfullpath -> /

Revision history for this message
Georges Racinet (gracinet) wrote :

OpenERP has a tendency to interpret paths relative to what if feels to be its home directory, namely the parent dir of the 'openerp' package.

In our case that's self.openerp_dir, so maybe you could work out something with os.path.relpath based on it ?

I don't know if that's true for addons, but it is for log files : bin/start_openerp --log-file=my.log will put the log file at <buildout_dir>/parts/<server part location>/my.log

One of the many reasons we wanted to normalize stuff in upgrade scripts (easy to forget to write down in maintainance procedures).

Revision history for this message
Jacques-Etienne Baudoux (jbaudoux) wrote :

As the openerp.cfg is generated by importing openerp.tools.config from buildout, and the path is generated by openerp with os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
I don't see anyway to have relative path here.
And as having full path in openerp.cfg is not blocking, so I'm gonna leave it like this.

Nevertheless, for python, relative path is required for the python module loader if you deploy in chroot jail. So, I suggest you merge this MP.

Revision history for this message
Georges Racinet (gracinet) wrote :

> As the openerp.cfg is generated by importing openerp.tools.config from
> buildout, and the path is generated by openerp with os.path.abspath(os.path.ex
> panduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
> I don't see anyway to have relative path here.

This I don't really understand: that'd be the initial generation, but the recipe overwrites openerp.cfg afterwards by writing directly to it with a ConfigParser instance, IIRC

> And as having full path in openerp.cfg is not blocking, so I'm gonna leave it
> like this.
>
> Nevertheless, for python, relative path is required for the python module
> loader if you deploy in chroot jail. So, I suggest you merge this MP.

I see, for the chroot use case, you *need* paths to be relative.
But for simple relocability without rerunning buildout, it'd be neat to have a full relative setup.
I'll take a look at the merge this afternoon. Meanwhile, if you have opportunity to add tests in anybox/recipe/openerp/test_server.py or test_base.py, that'd be great (tests are needed anyway).

Revision history for this message
Georges Racinet (gracinet) wrote :

Took a look at the merge today (sorry, I'm late) and a few more question arise:

 - the invocation of Session in the generated interpreter now uses the buildout dir (to read the expected VERSION file)
 - is there a specific reason not to pass a relative path in the scripts generation, too ?

That makes too much questionning for merging before 1.8.0, I'll take a look at these again rigth after the release of 1.8.0 (should be first thing tomorrow morning CEST).

Revision history for this message
Georges Racinet (gracinet) wrote :

But I'm confident that it can make it before 1.8.1

Revision history for this message
Jacques-Etienne Baudoux (jbaudoux) wrote :

> This I don't really understand: that'd be the initial generation, but the recipe overwrites openerp.cfg afterwards by writing directly to it with a ConfigParser instance, IIRC
ok, I see what you mean. This has to be investigated a bit more.

> - is there a specific reason not to pass a relative path in the scripts generation, too ?
Of what scripts are you talking? All the python scripts in bin seems to be with relative path

Anyway, thanks for the time you spend reviewing this. I need to look at your latest developments

Revision history for this message
Jacques-Etienne Baudoux (jbaudoux) wrote :

Hi Georges,

I'm coming back on this MP as I just found some time to investigate that openerp.cfg addons_path that is not relative to the root of the chroot jail.

So, it's coming from server.py:
|- def _create_default_config(self):
|| """Have OpenERP generate its default config file.
|| """
|| self.options.setdefault('options.admin_passwd', '')
|| if self.major_version <= (6, 0):
|| # root-path not available as command-line option
|| os.chdir(join(self.openerp_dir, 'bin'))
|| subprocess.check_call([self.script_path, '--stop-after-init', '-s',
|| ])
|| else:
|| sys.path.extend([self.openerp_dir])
|| sys.path.extend([egg.location for egg in self.ws])
|| from openerp.tools.config import configmanager
|| configmanager(self.config_path).save()

I see 2 options:

1/ no improvement to this MP. Only python scripts are relative. However, each item in the addons path is prefixed by the path of the jail.
addons_path = /home/jbaudoux/chroot/opt/server/openerp/addons,....
and, inside the jail, workaround by creating a symlink /home/jbaudoux/chroot -> /

2/ don't use
|| from openerp.tools.config import configmanager
|| configmanager(self.config_path).save()
but run the jail start script like it was previously
|| subprocess.check_call([self.script_path, '--stop-after-init', '-s',
|| ])
where self.script_path has to be replaced by a new buildout variable specifying the script to run
This also has the advantage that if buildout is not run by the user that will run openerp, the openerp.cfg file gets the right ownership which is currently not the case.

What do you prefer? Option 1 is working fine (so I keep this MP as it is) but if you'd like to have option 2 implemented, I can make a MP to this MP.

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Hi Jacques Etienne,

thank you for your work! I'm suggesting some changes to streamline the preparation of running the buildout in a jail root here: https://code.launchpad.net/~therp-nl/anybox.recipe.openerp/jbaudoux-relative_paths_explicit_jailroot_support/+merge/205099. This includes mangling of the addons paths.

Revision history for this message
Jacques-Etienne Baudoux (jbaudoux) wrote :

This MP is now obsolete and replaced by:
https://code.launchpad.net/~jbaudoux/anybox.recipe.openerp/1.8.5_relative_paths_jailroot/+merge/237238

Keeping this MP for history until latest gets merged.

Unmerged revisions

423. By Jacques-Etienne Baudoux

Fix format string

422. By Jacques-Etienne Baudoux

pep8

421. By Jacques-Etienne Baudoux

Support buildout option "relative_paths = true"

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'anybox/recipe/openerp/base.py'
--- anybox/recipe/openerp/base.py 2013-09-05 22:45:15 +0000
+++ anybox/recipe/openerp/base.py 2013-09-08 17:19:36 +0000
@@ -111,6 +111,15 @@
111 self.clear_retry = clear_retry == 'true'111 self.clear_retry = clear_retry == 'true'
112112
113 # same as in zc.recipe.eggs113 # same as in zc.recipe.eggs
114 relative_paths = options.get(
115 'relative-paths',
116 buildout['buildout'].get('relative-paths', 'false')
117 )
118 if relative_paths == 'true':
119 self._relative_paths = self.buildout_dir
120 else:
121 self._relative_paths = ''
122 assert relative_paths == 'false'
114 self.extra_paths = [123 self.extra_paths = [
115 join(self.buildout_dir, p.strip())124 join(self.buildout_dir, p.strip())
116 for p in self.options.get('extra-paths', '').split(os.linesep)125 for p in self.options.get('extra-paths', '').split(os.linesep)
117126
=== modified file 'anybox/recipe/openerp/server.py'
--- anybox/recipe/openerp/server.py 2013-09-07 11:43:18 +0000
+++ anybox/recipe/openerp/server.py 2013-09-08 17:19:36 +0000
@@ -13,6 +13,21 @@
13SERVER_COMMA_LIST_OPTIONS = ('log_handler', )13SERVER_COMMA_LIST_OPTIONS = ('log_handler', )
1414
1515
16def _relative_path(common, path):
17 """Copied from easy_install"""
18 r = []
19 while 1:
20 dirname, basename = os.path.split(path)
21 r.append(basename)
22 if dirname == common:
23 break
24 if dirname == path:
25 raise AssertionError("dirname of %s is the same" % dirname)
26 path = dirname
27 r.reverse()
28 return os.path.join(*r)
29
30
16class ServerRecipe(BaseRecipe):31class ServerRecipe(BaseRecipe):
17 """Recipe for server install and config32 """Recipe for server install and config
18 """33 """
@@ -266,15 +281,28 @@
266 desc = self.openerp_scripts[name] = dict(entry=entry)281 desc = self.openerp_scripts[name] = dict(entry=entry)
267 return name, desc282 return name, desc
268283
284 def _relativitize(self, path):
285 """Inspired from easy_install"""
286 if self._relative_paths:
287 eggs = os.path.normcase(os.path.abspath(
288 self.b_options['eggs-directory']))
289 common = os.path.dirname(os.path.commonprefix([path, eggs]))
290 if (common == self._relative_paths or
291 common.startswith(os.path.join(self._relative_paths, ''))
292 ):
293 return "join(base, %r)" % _relative_path(common, path)
294 return path
295
269 def _register_main_startup_script(self, qualified_name):296 def _register_main_startup_script(self, qualified_name):
270 """Register main startup script, usually ``start_openerp`` for install.297 """Register main startup script, usually ``start_openerp`` for install.
271 """298 """
272 desc = self._get_or_create_script('openerp_starter',299 desc = self._get_or_create_script('openerp_starter',
273 name=qualified_name)[1]300 name=qualified_name)[1]
274 desc.update(301 desc.update(
275 arguments='%r, %r, version=%r' % (self._get_server_command(),302 arguments='%s, %s, version=%r' % (
276 self.config_path,303 self._relativitize(self._get_server_command()),
277 self.major_version),304 self._relativitize(self.config_path),
305 self.major_version),
278 )306 )
279307
280 startup_delay = float(self.options.get('startup_delay', 0))308 startup_delay = float(self.options.get('startup_delay', 0))
@@ -305,9 +333,9 @@
305 "from anybox.recipe.openerp import devtools",333 "from anybox.recipe.openerp import devtools",
306 "devtools.load(for_tests=True)",334 "devtools.load(for_tests=True)",
307 "")),335 "")),
308 arguments='%r, %r, version=%r, just_test=True' % (336 arguments='%s, %s, version=%r, just_test=True' % (
309 self._get_server_command(),337 self._relativitize(self._get_server_command()),
310 self.config_path,338 self._relativitize(self.config_path),
311 self.major_version),339 self.major_version),
312 )340 )
313341
@@ -340,7 +368,8 @@
340 # to resort on hacking sys.argv368 # to resort on hacking sys.argv
341 desc['initialization'] = (369 desc['initialization'] = (
342 "from sys import argv; argv[1:] = ['%s', '-c', '%s.conf.py']" % (370 "from sys import argv; argv[1:] = ['%s', '-c', '%s.conf.py']" % (
343 gunicorn_entry_point, join(self.etc, qualified_name)))371 gunicorn_entry_point,
372 self._relativitize(join(self.etc, qualified_name))))
344373
345 def _register_openerp_command(self, qualified_name):374 def _register_openerp_command(self, qualified_name):
346 """Register https://launchpad.net/openerp-command for install.375 """Register https://launchpad.net/openerp-command for install.
@@ -403,7 +432,9 @@
403 desc = self._get_or_create_script('openerp_cron_worker',432 desc = self._get_or_create_script('openerp_cron_worker',
404 name=qualified_name)[1]433 name=qualified_name)[1]
405 desc.update(entry='openerp_cron_worker',434 desc.update(entry='openerp_cron_worker',
406 arguments='%r, %r' % (script_src, self.config_path),435 arguments='%s, %s' % (
436 self._relativitize(script_src),
437 self._relativitize(self.config_path)),
407 initialization='',438 initialization='',
408 )439 )
409440
@@ -418,7 +449,7 @@
418 initialization = os.linesep.join((449 initialization = os.linesep.join((
419 "",450 "",
420 "from anybox.recipe.openerp.startup import Session",451 "from anybox.recipe.openerp.startup import Session",
421 "session = Session(%r)" % self.config_path,452 "session = Session(%s)" % self._relativitize(self.config_path),
422 "if len(sys.argv) <= 1:",453 "if len(sys.argv) <= 1:",
423 " print('To start the OpenERP working session, just do:')",454 " print('To start the OpenERP working session, just do:')",
424 " print(' session.open(db=DATABASE_NAME)')",455 " print(' session.open(db=DATABASE_NAME)')",
@@ -441,8 +472,7 @@
441 initialization=initialization,472 initialization=initialization,
442 arguments=self.options.get('arguments', ''),473 arguments=self.options.get('arguments', ''),
443 extra_paths=self.extra_paths,474 extra_paths=self.extra_paths,
444 # TODO investigate these options:475 relative_paths=self._relative_paths,
445 # relative_paths=self._relative_paths,
446 )476 )
447477
448 def _install_openerp_scripts(self):478 def _install_openerp_scripts(self):
@@ -476,9 +506,8 @@
476 interpreter='',506 interpreter='',
477 initialization=initialization,507 initialization=initialization,
478 arguments=desc.get('arguments', ''),508 arguments=desc.get('arguments', ''),
479 # TODO investigate these options:
480 extra_paths=self.extra_paths,509 extra_paths=self.extra_paths,
481 # relative_paths=self._relative_paths,510 relative_paths=self._relative_paths,
482 )511 )
483 self.openerp_installed.append(join(self.bin_dir, script_name))512 self.openerp_installed.append(join(self.bin_dir, script_name))
484513

Subscribers

People subscribed via source and target branches