Commit fdfc1509 authored by Alain Takoudjou's avatar Alain Takoudjou

random-recipe: add option create-once to prevent storage file deletion by buildout

It sometimes happend that buildout unistall/install the section with this recipe,
which remore storage_path file and recreate it. In most of cases this is not wanted
because the recipe is used to generate password, so that password will change when this
To prevent buildout to remove storage_path, I introduced a new option 'create-once'
which, when set to True (the default value) will not let buildout play with storage_path file
by returning None in install recipe. Also install won't remove the file if the size is not 0.

/reviewed-on nexedi/slapos!129
parent 9f8227e7
......@@ -92,6 +92,9 @@ class Password(object):
- storage-path: plain-text persistent storage for password,
that can only be accessed by the user
(default: ${buildout:parts-directory}/${:_buildout_section_name_})
- create-once: boolean value which set if storage-path won't be modified
as soon the file is created with the password (not empty).
(default: True)
If storage-path is empty, the recipe does not save the password, which is
fine it is saved by other means, e.g. using the publish-early recipe.
......@@ -99,6 +102,8 @@ class Password(object):
def __init__(self, buildout, name, options):
options_get = options.get
self.create_once = options.get('create-once', 'True').lower() \
in GenericBaseRecipe.TRUE_VALUES
self.storage_path = options['storage-path']
except KeyError:
......@@ -112,9 +117,9 @@ class Password(object):
except IOError as e:
if e.errno != errno.ENOENT:
self.update = self.install
if not passwd:
passwd = self.generatePassword(int(options_get('bytes', '8')))
self.update = self.install
self.passwd = passwd
# Password must not go into .installed file, for 2 reasons:
# security of course but also to prevent buildout to always reinstall.
......@@ -126,17 +131,23 @@ class Password(object):
def install(self):
if self.storage_path:
# The following 2 lines are just an optimization to avoid recreating
# the file with the same content.
if self.create_once and os.stat(self.storage_path).st_size:
except OSError as e:
if e.errno != errno.ENOENT:
fd =,
os.O_CREAT | os.O_EXCL | os.O_WRONLY | os.O_TRUNC, 0600)
os.write(fd, self.passwd)
return self.storage_path
if not self.create_once:
return self.storage_path
def update(self):
return ()
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment