[Checkins] SVN: zc.recipe.deployment/branches/satchit-sharedconfig-recipe/s Recipe to update monolithic configuration files.

Satchidanand Haridas satchit at zope.com
Wed May 12 14:24:18 EDT 2010


Log message for revision 112263:
  Recipe to update monolithic configuration files.
  
  - A path to the configuration file must be provided.
  
  - The configuration file must comments in the form of "#" at the beginning of the line.
  
  Tests.
   
  

Changed:
  U   zc.recipe.deployment/branches/satchit-sharedconfig-recipe/setup.py
  U   zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/README.txt
  U   zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/__init__.py

-=-
Modified: zc.recipe.deployment/branches/satchit-sharedconfig-recipe/setup.py
===================================================================
--- zc.recipe.deployment/branches/satchit-sharedconfig-recipe/setup.py	2010-05-12 18:20:30 UTC (rev 112262)
+++ zc.recipe.deployment/branches/satchit-sharedconfig-recipe/setup.py	2010-05-12 18:24:18 UTC (rev 112263)
@@ -9,9 +9,11 @@
 deployment = %(name)s:Install
 configuration = %(name)s:Configuration
 crontab = %(name)s:Crontab
+sharedconfig = %(name)s:SharedConfig
 
 [zc.buildout.uninstall]
 default = %(name)s:uninstall
+sharedconfig = %(name)s:uninstall_shared_config
 
 ''' % globals()
 

Modified: zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/README.txt
===================================================================
--- zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/README.txt	2010-05-12 18:20:30 UTC (rev 112262)
+++ zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/README.txt	2010-05-12 18:24:18 UTC (rev 112263)
@@ -564,3 +564,243 @@
 
     >>> os.path.exists(os.path.join(sample_buildout, 'etc/cron.d/bar-cron'))
     False
+
+
+SharedConfig
+============
+
+This recipe can be used to update configuration files that are shared by
+multiple applications. The absolute path of the file must be specified. Also,
+the configuration files must accept comments that start with "#".
+
+Like the configuration recipe, the content to add in the configuration file can
+be provided using the "text" or the "file" option.
+
+First let's create a file that will be used as the shared configuration file.
+
+    >>> open('y.cfg', 'w').write(
+    ... '''Some
+    ... existing
+    ... configuration
+    ... ''')
+
+We now create our buildout configuration and use the "sharedconfig" recipe and
+run buildout.
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = foo y.cfg
+    ...
+    ... [foo]
+    ... recipe = zc.recipe.deployment
+    ... prefix = %s
+    ... user = %s
+    ... etc-user = %s
+    ...
+    ... [y.cfg]
+    ... recipe = zc.recipe.deployment:sharedconfig
+    ... path = y.cfg
+    ... deployment = foo
+    ... text = xxx
+    ...        yyy
+    ...        zzz
+    ... ''' % (sample_buildout, user, user))
+
+    >>> print system(join('bin', 'buildout')), # doctest: +NORMALIZE_WHITESPACE
+    Installing foo.
+    zc.recipe.deployment:
+        Creating 'PREFIX/etc/foo',
+        mode 755, user 'USER', group 'GROUP'
+    zc.recipe.deployment:
+        Creating 'PREFIX/var/log/foo',
+        mode 755, user 'USER', group 'GROUP'
+    zc.recipe.deployment:
+        Creating 'PREFIX/var/run/foo',
+        mode 750, user 'USER', group 'GROUP'
+    zc.recipe.deployment:
+        Updating 'PREFIX/etc/cron.d',
+        mode 755, user 'USER', group 'GROUP'
+    zc.recipe.deployment:
+        Updating 'PREFIX/etc/init.d',
+        mode 755, user 'USER', group 'GROUP'
+    zc.recipe.deployment:
+        Updating 'PREFIX/etc/logrotate.d',
+        mode 755, user 'USER', group 'GROUP'
+    Installing y.cfg.
+
+    >>> print open('y.cfg', 'r').read()
+    Some
+    existing
+    configuration
+    <BLANKLINE>
+    #[foo_y.cfg DO NOT MODIFY LINES FROM HERE#
+    xxx
+    yyy
+    zzz
+    #TILL HERE foo_y.cfg]#
+    <BLANKLINE>
+
+Running buildout again without modifying the configuration leaves the file the
+same.
+
+    >>> print system(join('bin', 'buildout')), # doctest: +NORMALIZE_WHITESPACE
+    Updating foo.
+    Updating y.cfg.
+
+    >>> print open('y.cfg', 'r').read()
+    Some
+    existing
+    configuration
+    <BLANKLINE>
+    #[foo_y.cfg DO NOT MODIFY LINES FROM HERE#
+    xxx
+    yyy
+    zzz
+    #TILL HERE foo_y.cfg]#
+    <BLANKLINE>
+
+If we add some more lines to the file
+
+    >>> open('y.cfg', 'a').write(
+    ... '''Some
+    ... additional
+    ... configuration
+    ... ''')
+
+and run buildout again, but this time after modifying the configuration for
+"y.cfg", the sections will be moved to the end of the file.
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = foo y.cfg
+    ...
+    ... [foo]
+    ... recipe = zc.recipe.deployment
+    ... prefix = %s
+    ... user = %s
+    ... etc-user = %s
+    ...
+    ... [y.cfg]
+    ... recipe = zc.recipe.deployment:sharedconfig
+    ... path = y.cfg
+    ... deployment = foo
+    ... text = 111
+    ...        222
+    ...        333
+    ... ''' % (sample_buildout, user, user))
+
+    >>> print system(join('bin', 'buildout')), # doctest: +NORMALIZE_WHITESPACE
+    Uninstalling y.cfg.
+    Running uninstall recipe.
+    Updating foo.
+    Installing y.cfg.
+
+    >>> print open('y.cfg', 'r').read()
+    Some
+    existing
+    configuration
+    <BLANKLINE>
+    Some
+    additional
+    configuration
+    #[foo_y.cfg DO NOT MODIFY LINES FROM HERE#
+    111
+    222
+    333
+    #TILL HERE foo_y.cfg]#
+    <BLANKLINE>
+
+The text to append to the shared configuration file can also be provided via a
+file.
+
+    >>> write('x.cfg', '''
+    ... [foo]
+    ... a = 1
+    ... b = 2
+    ...
+    ... [log]
+    ... c = 1
+    ... ''')
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = foo y.cfg
+    ...
+    ... [foo]
+    ... recipe = zc.recipe.deployment
+    ... prefix = %s
+    ... user = %s
+    ... etc-user = %s
+    ...
+    ... [y.cfg]
+    ... recipe = zc.recipe.deployment:sharedconfig
+    ... path = %s/etc/z.cfg
+    ... deployment = foo
+    ... file = x.cfg
+    ... ''' % (sample_buildout, user, user, sample_buildout))
+    >>> print system(join('bin', 'buildout')), # doctest: +NORMALIZE_WHITESPACE
+    While:
+      Installing.
+      Getting section y.cfg.
+      Initializing part y.cfg.
+    Error: Path 'PREFIX/etc/z.cfg' does not exist
+
+Oops. The path of the configuration file must exist. Let's create one.
+
+    >>> write(join(sample_buildout, 'etc', 'z.cfg'), '')
+    >>> print system(join('bin', 'buildout')), # doctest: +NORMALIZE_WHITESPACE
+    Uninstalling y.cfg.
+    Running uninstall recipe.
+    Updating foo.
+    Installing y.cfg.
+
+    >>> print open(join(sample_buildout, 'etc', 'z.cfg'), 'r').read()
+    <BLANKLINE>
+    #[foo_y.cfg DO NOT MODIFY LINES FROM HERE#
+    <BLANKLINE>
+    [foo]
+    a = 1
+    b = 2
+    <BLANKLINE>
+    [log]
+    c = 1
+    <BLANKLINE>
+    #TILL HERE foo_y.cfg]#
+    <BLANKLINE>
+
+While uninstalling, only the lines that the recipe installed are removed.
+
+    >>> print system(join('bin', 'buildout')+' buildout:parts='),
+    Uninstalling y.cfg.
+    Running uninstall recipe.
+    Uninstalling foo.
+    Running uninstall recipe.
+    zc.recipe.deployment: Removing 'PREFIX/etc/foo'
+    zc.recipe.deployment: Removing 'PREFIX/etc/cron.d'.
+    zc.recipe.deployment: Removing 'PREFIX/etc/init.d'.
+    zc.recipe.deployment: Removing 'PREFIX/etc/logrotate.d'.
+    zc.recipe.deployment: Removing 'PREFIX/var/log/foo'.
+    zc.recipe.deployment: Removing 'PREFIX/var/run/foo'.
+
+But the files are not deleted.
+
+    >>> os.path.exists('y.cfg')
+    True
+
+    >>> print open('y.cfg', 'r').read()
+    Some
+    existing
+    configuration
+    <BLANKLINE>
+    Some
+    additional
+    configuration
+
+    >>> os.path.exists(join(sample_buildout, 'etc', 'z.cfg'))
+    True
+
+    >>> print open(join(sample_buildout, 'etc', 'z.cfg'), 'r').read()
+    <BLANKLINE>

Modified: zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/__init__.py
===================================================================
--- zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/__init__.py	2010-05-12 18:20:30 UTC (rev 112262)
+++ zc.recipe.deployment/branches/satchit-sharedconfig-recipe/src/zc/recipe/deployment/__init__.py	2010-05-12 18:24:18 UTC (rev 112263)
@@ -162,3 +162,59 @@
 
     update = install
 
+
+begin_marker = '#[%s DO NOT MODIFY LINES FROM HERE#'
+end_marker = '#TILL HERE %s]#'
+
+class SharedConfig:
+
+    def __init__(self, buildout, name, options):
+        self.options = options
+        deployment = options.get('deployment')
+        options['entry_name'] = '%s_%s' % (buildout[deployment]['name'], name)
+        if not os.path.exists(options['path']):
+            raise zc.buildout.UserError(
+                "Path '%s' does not exist" % options['path'])
+        options['location'] = options['path']
+
+    def install(self):
+        options = self.options
+        mode = options.get('mode', '')
+        if 'file' in options:
+            if 'text' in options:
+                raise zc.buildout.UserError(
+                    "Cannot specify both file and text options")
+            text = open(options['file'], 'r'+mode).read()
+        else:
+            text = options['text']
+        open(options['location'], 'a'+mode).write(
+            self._wrap_with_comments(options['entry_name'], text))
+        return ()
+
+    def _wrap_with_comments(self, entry_name, text):
+        return '\n%s\n%s\n%s\n' % (
+            begin_marker % entry_name, text, end_marker % entry_name)
+
+    def update(self):
+        pass
+
+def uninstall_shared_config(name, options):
+    old_config = open(options['location'], 'r').read()
+    new_config = []
+    block_start = False
+    for line in old_config.splitlines():
+        if line.startswith('#[%s' % options['entry_name']):
+            block_start = True
+            continue
+        elif line.endswith('%s]#' % options['entry_name']):
+            block_start = False
+            continue
+        else:
+            if block_start:
+                continue
+            else:
+                new_config.append(line)
+
+    mode = options.get('mode', '')
+    open(options['location'], 'w'+mode).write('\n'.join(new_config))
+



More information about the checkins mailing list