[Checkins] SVN: z3c.recipe.ldap/trunk/ slapd instance tests passing. Need to finish slapadd.

Ross Patterson me at rpatterson.net
Sun Dec 9 08:55:30 EST 2007


Log message for revision 82210:
  slapd instance tests passing.  Need to finish slapadd.
  

Changed:
  U   z3c.recipe.ldap/trunk/setup.py
  U   z3c.recipe.ldap/trunk/z3c/recipe/ldap/__init__.py
  U   z3c.recipe.ldap/trunk/z3c/recipe/ldap/docs/README.txt
  A   z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapadd.py
  A   z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapd.py
  U   z3c.recipe.ldap/trunk/z3c/recipe/ldap/tests/test_docs.py

-=-
Modified: z3c.recipe.ldap/trunk/setup.py
===================================================================
--- z3c.recipe.ldap/trunk/setup.py	2007-12-09 05:22:30 UTC (rev 82209)
+++ z3c.recipe.ldap/trunk/setup.py	2007-12-09 13:55:29 UTC (rev 82210)
@@ -14,10 +14,9 @@
 
 long_description = open(README).read() + '\n\n' 
 
-entry_point = 'z3c.recipe.ldap:Recipe'
+entry_points = {"zc.buildout": ["default = z3c.recipe.ldap:Slapd",
+                                "slapadd = z3c.recipe.ldap:Slapadd"]}
 
-entry_points = {"zc.buildout": ["default = %s" % entry_point]}
-
 setup(name='z3c.recipe.ldap',
       version=version,
       description="Deploy an OpenLDAP serve in a zc.buildout",
@@ -37,8 +36,8 @@
       include_package_data=True,
       zip_safe=True,
       install_requires=['setuptools',
-                        'zope.testing',
-                        'zc.buildout'
+                        'zc.buildout',
+                        'zc.recipe.egg'
                         # -*- Extra requirements: -*-
                         ],
       entry_points=entry_points,

Modified: z3c.recipe.ldap/trunk/z3c/recipe/ldap/__init__.py
===================================================================
--- z3c.recipe.ldap/trunk/z3c/recipe/ldap/__init__.py	2007-12-09 05:22:30 UTC (rev 82209)
+++ z3c.recipe.ldap/trunk/z3c/recipe/ldap/__init__.py	2007-12-09 13:55:29 UTC (rev 82210)
@@ -1,19 +1,4 @@
-# -*- coding: utf-8 -*-
-"""Recipe ldap"""
+"""z3c.recipe.ldap"""
 
-class Recipe(object):
-    """This recipe is used by zc.buildout"""
-
-    def __init__(self, buildout, name, options):
-        self.name, self.options = name, options
-
-    def install(self):
-        """installer"""
-        # XXX do the job here
-        # returns installed files
-        return tuple()
-
-    def update(self):
-        """updater"""
-        pass
-
+from slapd import Slapd
+from slapadd import Slapadd

Modified: z3c.recipe.ldap/trunk/z3c/recipe/ldap/docs/README.txt
===================================================================
--- z3c.recipe.ldap/trunk/z3c/recipe/ldap/docs/README.txt	2007-12-09 05:22:30 UTC (rev 82209)
+++ z3c.recipe.ldap/trunk/z3c/recipe/ldap/docs/README.txt	2007-12-09 13:55:29 UTC (rev 82210)
@@ -17,3 +17,198 @@
 How to use z3c.recipe.ldap ?
 ============================
 
+-------------------------
+Installing slapd instance
+-------------------------
+
+The default recipe in z3c.recipe.ldap can be used to deploy a slapd
+LDAP server in the buildout.  Options in the slapd part not used by
+the recipe itself will be used to create and populate a slapd.conf
+file.
+
+The only required option is the suffix argupent.  Specifying the
+suffix with a dc requires that the "dc" LDAP attribute type
+configuration.  Write a buildout.cfg with a suffix and including the
+attribute type configuration.  Also specify that the server should use
+a socket instead of a network port::
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = slapd
+    ...
+    ... [slapd]
+    ... recipe = z3c.recipe.ldap
+    ... use-socket = True
+    ... include =
+    ...     foo.schema
+    ...     bar.conf
+    ... suffix = "dc=localhost"
+    ... """)
+
+Create the files to be included::
+
+    >>> write(sample_buildout, 'foo.schema',
+    ... """
+    ... attributetype ( 0.9.2342.19200300.100.1.25
+    ... 	NAME ( 'dc' 'domainComponent' )
+    ... 	DESC 'RFC1274/2247: domain component'
+    ... 	EQUALITY caseIgnoreIA5Match
+    ... 	SUBSTR caseIgnoreIA5SubstringsMatch
+    ... 	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+    ... """)
+    >>> write(sample_buildout, 'bar.conf', '\n')
+
+Run the buildout::
+
+    >>> print system(buildout),
+    Installing slapd.
+    Generated script '/sample-buildout/bin/slapd'.
+
+The configuration file is created in the part by default.  Note that
+keys that can be specified multiple times in slapd.conf, such as
+include, will be constitued from multiple line separated values when
+present.  Also note that keys that contain file paths in slapd.conf,
+such as include, will be expanded from the buildout directory::
+
+    >>> ls(sample_buildout, 'parts', 'slapd')
+    -  slapd.conf
+    >>> cat(sample_buildout, 'parts', 'slapd', 'slapd.conf')
+    include	/sample-buildout/foo.schema
+    include	/sample-buildout/bar.conf...
+
+An empty directory is created for the LDAP database::
+
+    >>> ls(sample_buildout, 'var')
+    d  slapd
+    >>> ls(sample_buildout, 'var', 'slapd')
+
+A script is also created for starting and stopping the slapd server::
+
+    >>> ls(sample_buildout, 'bin')
+    -  buildout
+    -  slapd
+
+Start the slapd server::
+
+    >>> bin = join(sample_buildout, 'bin', 'slapd')
+    >>> print system(bin+' start'),
+
+On first run, the LDAP database is created::
+
+    >>> ls(sample_buildout, 'var', 'slapd')
+    - __db.001...
+
+While the server is running a pid file is created::
+
+    >>> ls(sample_buildout, 'parts', 'slapd')
+    -  slapd.conf
+    -  slapd.pid
+
+Stop the slapd server::
+
+    >>> print system(bin+' stop'),
+
+Wait for it to shutdown::
+
+    >>> import time
+    >>> time.sleep(0.1)
+
+When the slapd server finishes shutting down the pid file is deleted::
+
+    >>> ls(sample_buildout, 'parts', 'slapd')
+    -  slapd.conf
+
+Specifying the slapd binary
+---------------------------
+
+The slapd binary to be used can be specified.  A buildout could, for
+example, include a part using a CMMI recipe and use the slapd binary
+from that build.
+
+Before specifying the slapd to use, it's left up to the environment::
+
+    >>> cat(sample_buildout, '.installed.cfg')
+    [buildout]...
+    [slapd]...
+    slapd = slapd...
+
+Specify a slapd in the buildout.cfg::
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = slapd
+    ...
+    ... [slapd]
+    ... recipe = z3c.recipe.ldap
+    ... slapd = /usr/sbin/slapd
+    ... use-socket = True
+    ... include =
+    ...     foo.schema
+    ...     bar.conf
+    ... suffix = "dc=localhost"
+    ... """)
+
+Run the buildout::
+
+    >>> print system(buildout),
+    Uninstalling slapd.
+    Installing slapd.
+    Generated script '/sample-buildout/bin/slapd'.
+
+Now it uses the specific slapd binary::
+
+    >>> cat(sample_buildout, '.installed.cfg')
+    [buildout]...
+    [slapd]...
+    slapd = /usr/sbin/slapd...
+
+----------------------------
+Initalizing an LDAP database
+----------------------------
+
+In the simplest form, simply provide an ldif arguemnt in the part with
+one or more filenames.
+
+    >>> write(sample_buildout, 'foo.ldif',
+    ... """
+    ... olcAttributeTypes: ( 0.9.2342.19200300.100.1.25
+    ...   NAME ( 'dc' 'domainComponent' )
+    ...   DESC 'RFC1274/2247: domain component'
+    ...   EQUALITY caseIgnoreIA5Match
+    ...   SUBSTR caseIgnoreIA5SubstringsMatch
+    ...   SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+    ... """)
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = slapd slapadd
+    ...
+    ... [slapd]
+    ... recipe = z3c.recipe.ldap
+    ... include =
+    ...     foo.schema
+    ... suffix = "dc=localhost"
+    ...
+    ... [slapadd]
+    ... recipe = z3c.recipe.ldap:slapadd
+    ... conf = ${slapd:conf}
+    ... ldif = foo.ldif
+    ... """)
+
+    >>> print system(buildout),
+    Uninstalling slapd.
+    Installing slapd.
+    Generated script '/sample-buildout/bin/slapd'.
+    Installing slapadd.
+
+Multiple LDIF files can be specified::
+
+    >>> TODO
+
+An alternate open ldap instance directory can be specified in the
+'directory' option::
+
+    >>> TODO
\ No newline at end of file

Copied: z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapadd.py (from rev 82208, z3c.recipe.ldap/trunk/z3c/recipe/ldap/__init__.py)
===================================================================
--- z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapadd.py	                        (rev 0)
+++ z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapadd.py	2007-12-09 13:55:29 UTC (rev 82210)
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+"""Recipe slapadd"""
+
+import os
+
+class Slapadd(object):
+    """This recipe is used by zc.buildout"""
+
+    def __init__(self, buildout, name, options):
+        self.name, self.options = name, options
+
+        self.ldifs = [
+            os.path.join(buildout['buildout']['directory'],
+                         ldif.strip())
+            for ldif in options['ldif'].split('\n') if ldif.strip()]
+        options['ldif'] = '\n'.join(self.ldifs)
+
+        var = options.get('var')
+        if var is None:
+            self.var = options['var'] = os.path.join(
+                buildout['buildout']['directory'],
+                'var', self.name)
+        else:
+            self.var = options['var'] = os.path.join(
+                buildout['buildout']['directory'], var)
+
+    def install(self):
+        """installer"""
+        if not os.path.exists(self.var):
+            os.mkdir(self.var)
+        
+        return tuple()
+
+    def update(self):
+        """updater"""
+        pass
+

Copied: z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapd.py (from rev 82208, z3c.recipe.ldap/trunk/z3c/recipe/ldap/__init__.py)
===================================================================
--- z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapd.py	                        (rev 0)
+++ z3c.recipe.ldap/trunk/z3c/recipe/ldap/slapd.py	2007-12-09 13:55:29 UTC (rev 82210)
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+"""Recipe ldap"""
+
+import sys, os, signal, subprocess
+
+import zc.buildout
+import zc.recipe.egg
+
+class Slapd(object):
+    """This recipe is used by zc.buildout"""
+
+    def __init__(self, buildout, name, options):
+        self.egg = zc.recipe.egg.Egg(buildout, options['recipe'], options)
+        self.name, self.options = name, options
+
+        options['location'] = os.path.join(
+            buildout['buildout']['parts-directory'], name)
+
+        if 'slapd' in options:
+            options['slapd'] = os.path.join(
+                buildout['buildout']['directory'], options['slapd'])
+        else:
+            options['slapd'] = 'slapd'
+
+        if 'conf' not in options:
+            options['conf'] = os.path.join(
+                options['location'], name+'.conf')
+
+        if 'pidfile' not in options:
+            options['pidfile'] = os.path.join(
+                options['location'], name+'.pid')
+
+        if 'directory' not in options:
+            options['directory'] = os.path.join(
+                buildout['buildout']['directory'], 'var', name)
+
+        if 'urls' in options and 'use-socket' in options:
+            raise ValueError('Cannot specify both the "urls" and '
+                             '"use-socket" options') 
+        if 'use-socket' in options:
+            options['urls'] = 'ldapi:/%s' % os.path.join(
+                options['location'], name+'.socket')
+
+        # Initialize the conf options
+        init_conf_options(
+            options, dir=buildout['buildout']['directory'])
+
+    def install(self):
+        """installer"""
+        # Install slapd.conf
+        os.makedirs(self.options['location'])
+        conf = file(self.options['conf'], 'w')
+        conf.writelines(get_conf_lines(self.options))
+        conf.close()
+
+        if not os.path.exists(self.options['directory']):
+            # Install the DB dir
+            os.makedirs(self.options['directory'])
+
+        # Install the control script
+        _, ws = self.egg.working_set(['z3c.recipe.ldap'])
+        zc.buildout.easy_install.scripts(
+            [(self.name, 'z3c.recipe.ldap.slapd', 'ctl')],
+            ws, self.options['executable'],
+            self.options['bin-directory'],
+            arguments=repr(self.options))
+
+        return (self.options['location'],)
+
+    def update(self):
+        """updater"""
+        pass
+
+conf_exclude = [
+    'slapd', 'conf', 'urls', 'recipe', 'location', 'executable',
+    'bin-directory', 'eggs-directory', 'develop-eggs-directory',
+    '_e', '_d', '_b']
+conf_paths = [
+    'include', 'pidfile', 'argsfile', 'directory', 'modulepath']
+conf_multiple = ['include', 'moduleload', 'access', 'index']
+conf_defaults = [('modulepath', '/usr/lib/ldap'),
+                 ('moduleload', 'back_bdb'),
+                 ('database', 'bdb'),
+                 ('index', 'objectClass\teq')]
+conf_order = [
+    'include', 'pidfile', 'argsfile', 'access', 'modulepath',
+    'moduleload', 'database', 'suffix', 'directory', 'index']
+
+def init_conf_options(options, dir='.', exclude=conf_exclude,
+                      paths=conf_paths, multiple=conf_multiple,
+                      defaults=conf_defaults):
+    for key, value in defaults:
+        if key not in options:
+            options[key] = value
+
+    for key, value in options.iteritems():
+        if key in exclude:
+            continue
+
+        if key in multiple:
+            values = []
+            for v in value.split('\n'):
+                v = v.strip()
+                if not v:
+                    continue
+                if key in paths:
+                    # expand file paths
+                    v = os.path.join(dir, v)
+                values.append(v)
+            options[key] = '\n'.join(values)
+            continue
+
+        if key in paths:
+            options[key] = os.path.join(dir, value)
+
+def order_keys(keys, order=conf_order):
+    for key in order:
+        if key in keys:
+            yield key
+    for key in keys:
+        if key not in order:
+            yield key
+
+def get_conf_lines(options, exclude=conf_exclude,
+                   multiple=conf_multiple, template='%s\t%s\n'):
+    for key in order_keys(options):
+        if key in exclude:
+            continue
+
+        value = options[key]
+        if key in multiple:
+            for v in value.split('\n'):
+                yield template % (key, v)
+        else:
+            yield template % (key, value)
+
+def ctl(options):
+    command, = sys.argv[1:]
+    if command.lower() == 'start':
+        args = [options['slapd'], '-f', options['conf']]
+        if 'urls' in options:
+            args.extend(['-h', options['urls']])
+        subprocess.Popen(args)
+    elif command.lower() == 'stop':
+        pidfile = file(options['pidfile'])
+        pid = int(pidfile.read())
+        pidfile.close()
+        os.kill(pid, signal.SIGTERM)
+    else:
+        raise ValueError('Command %s unsupported' % command)

Modified: z3c.recipe.ldap/trunk/z3c/recipe/ldap/tests/test_docs.py
===================================================================
--- z3c.recipe.ldap/trunk/z3c/recipe/ldap/tests/test_docs.py	2007-12-09 05:22:30 UTC (rev 82209)
+++ z3c.recipe.ldap/trunk/z3c/recipe/ldap/tests/test_docs.py	2007-12-09 13:55:29 UTC (rev 82210)
@@ -9,20 +9,26 @@
 import sys
 import os
 
-from zope.testing import doctest
+from zope.testing import doctest, renormalizing
+import zc.buildout.testing
 
 current_dir = os.path.dirname(__file__)
 
-def doc_suite(test_dir, setUp=None, tearDown=None, globs=None):
+def setUp(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    zc.buildout.testing.install_develop('zc.recipe.egg', test)
+    zc.buildout.testing.install_develop('z3c.recipe.ldap', test)
+
+def doc_suite(test_dir, setUp=setUp,
+              tearDown=zc.buildout.testing.buildoutTearDown,
+              globs=None):
     """Returns a test suite, based on doctests found in /doctest."""
-    suite = []
     if globs is None:
         globs = globals()
 
     globs['test_dir'] = current_dir
     
-    flags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE |
-             doctest.REPORT_ONLY_FIRST_FAILURE)
+    flags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
 
     package_dir = os.path.split(test_dir)[0]
     if package_dir not in sys.path:
@@ -34,14 +40,14 @@
     docs = [os.path.join(doctest_dir, doc) for doc in
             os.listdir(doctest_dir) if doc.endswith('.txt')]
 
-    for test in docs:
-        suite.append(doctest.DocFileSuite(test, optionflags=flags, 
-                                          globs=globs, setUp=setUp, 
-                                          tearDown=tearDown,
-                                          module_relative=False))
+    return unittest.TestSuite(
+        doctest.DocFileSuite(
+            test, optionflags=flags, globs=globs, setUp=setUp,
+            tearDown=tearDown, module_relative=False,
+            checker=renormalizing.RENormalizing([
+                zc.buildout.testing.normalize_path]))
+        for test in docs)
 
-    return unittest.TestSuite(suite)
-
 def test_suite():
     """returns the test suite"""
     return doc_suite(current_dir)



More information about the Checkins mailing list