[Zope3-checkins] SVN: Zope3/trunk/ Add a mkzopeinstance script that creates an instance based on an instance

Fred L. Drake, Jr. fred at zope.com
Thu May 13 17:58:17 EDT 2004


Log message for revision 24637:
Add a mkzopeinstance script that creates an instance based on an instance
template.



-=-
Added: Zope3/trunk/bin/mkzopeinstance
===================================================================
--- Zope3/trunk/bin/mkzopeinstance	2004-05-13 21:25:32 UTC (rev 24636)
+++ Zope3/trunk/bin/mkzopeinstance	2004-05-13 21:58:16 UTC (rev 24637)
@@ -0,0 +1,40 @@
+#!/usr/bin/env python2.3
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Script to create a new Zope instance home.
+
+$Id$
+"""
+
+import os
+import sys
+
+here = os.path.dirname(os.path.realpath(__file__))
+swhome = os.path.dirname(here)
+
+for parts in [("src",), ("lib", "python")]:
+    d = os.path.join(swhome, *(parts + ("zope", "app", "process")))
+    if os.path.isdir(d):
+        d = os.path.join(swhome, *parts)
+        sys.path.insert(0, d)
+        break
+else:
+    print >>sys.stderr, "Could not locate Zope software installation!"
+    sys.exit(1)
+
+
+from zope.app.process.mkzopeinstance import main
+
+
+sys.exit(main())


Property changes on: Zope3/trunk/bin/mkzopeinstance
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/app/process/mkzopeinstance.py
===================================================================
--- Zope3/trunk/src/zope/app/process/mkzopeinstance.py	2004-05-13 21:25:32 UTC (rev 24636)
+++ Zope3/trunk/src/zope/app/process/mkzopeinstance.py	2004-05-13 21:58:16 UTC (rev 24637)
@@ -0,0 +1,182 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Implementation of the mkzopeinstance script.
+
+This creates a new instances of the Zope server instance home.  An
+'instance home' contains two things:
+
+- application server configuration and data
+
+- server process control scripts and data
+
+$Id$
+"""
+import optparse
+import os
+import shutil
+import sys
+
+from zope.app.applicationcontrol import zopeversion
+
+
+def main(argv=None):
+    """Top-level script function to create a new Zope instance."""
+    if argv is None:
+        argv = sys.argv
+    try:
+        options = parse_args(argv)
+    except SystemExit, e:
+        if e.code:
+            return 2
+        else:
+            return 0
+    app = Application(options)
+    return app.process()
+
+
+class Application:
+
+    def __init__(self, options):
+        self.options = options
+
+    def read_input_line(self, prompt):
+        # The tests replace this to make sure the right things happen.
+        return raw_input(prompt)
+
+    def process(self):
+        options = self.options
+
+        # make sure we can find the skeleton
+        if not os.path.isdir(options.skeleton):
+            print >>sys.stderr, "skeleton directory", options.skeleton
+            print >>sys.stderr, "does not exist or is not a directory"
+            return 1
+
+        # create the destination
+        if not options.destination:
+            options.destination = self.get_skeltarget()
+        options.destination = os.path.abspath(options.destination)
+        if not os.path.exists(options.destination):
+            try:
+                os.mkdir(options.destination)
+            except OSError, e:
+                print >>sys.stderr, "could not create instance home:", e
+                return 1
+        elif not os.path.isdir(options.destination):
+            print >>sys.stderr, options.destination, "is not a directory"
+            print >>sys.stderr, ("(instance homes cannot be created in"
+                                 " non-directories)")
+            return 1
+
+        # XXX for now, bail if the username/password hasn't been
+        # provided from the command line; this should be improved
+        # after the ZopeX3 alpha:
+        if not (options.username and options.password):
+            print >>sys.stderr, ("username and password must be"
+                                 " provided using the --user option")
+            return 2
+
+        # now create the instance!
+        self.copy_skeleton()
+        return 0
+
+    def get_skeltarget(self):
+        print SKELTARGET_MESSAGE
+        while 1:
+            skeltarget = self.read_input_line("Directory: ").strip()
+            if skeltarget == '':
+                print >>sys.stderr, 'You must specify a directory'
+                continue
+            else:
+                break
+        return os.path.expanduser(skeltarget)
+
+    def copy_skeleton(self):
+        options = self.options
+        # XXX we should be able to compute the script
+        script = os.path.abspath(sys.argv[0])
+        software_home = os.path.dirname(os.path.dirname(script))
+        self.replacements = [
+            ("@USERNAME@",     options.username),
+            ("@PASSWORD@",     options.password),
+            ("@PYTHON@",       sys.executable),
+            ("@INSTANCEHOME@", options.destination),
+            ("@SOFTWAREHOME@", software_home),
+            ]
+        self.copytree(self.options.skeleton, self.options.destination)
+
+    def copytree(self, src, dst):
+        # Similar to shutil.copytree(), but doesn't care about
+        # symlinks, doesn't collect errors, and uses self.copyfile()
+        # instead of shutil.copy2().
+        assert os.path.isdir(dst)
+        names = os.listdir(src)
+        for name in names:
+            srcname = os.path.join(src, name)
+            dstname = os.path.join(dst, name)
+            if os.path.isdir(srcname):
+                self.copytree(srcname, dstname)
+            else:
+                self.copyfile(srcname, dstname)
+            # XXX What about devices, sockets etc.?
+
+    def copyfile(self, src, dst):
+        if dst.endswith(".in"):
+            dst = dst[:-3]
+            text = open(src, "rU").read()
+            # perform replacements
+            for var, string in self.replacements:
+                text = text.replace(var, string)
+            f = open(dst, "w")
+            f.write(text)
+            f.close()
+            shutil.copymode(src, dst)
+            shutil.copystat(src, dst)
+        else:
+            shutil.copy2(src, dst)
+
+
+SKELTARGET_MESSAGE = """\
+Please choose a directory in which you'd like to install Zope
+'instance home' files such as database files, configuration files,
+etc.
+"""
+
+
+def parse_args(argv):
+    """Parse the command line, returning an object representing the input."""
+    path, prog = os.path.split(argv[0])
+    basedir = os.path.dirname(os.path.realpath(path))
+    # no assurance that this exists!
+    default_skeleton = os.path.join(basedir, "skel")
+    version = "%prog for " + zopeversion.ZopeVersionUtility.getZopeVersion()
+    p = optparse.OptionParser(prog=prog,
+                              usage="%prog [options]",
+                              version=version)
+    p.add_option("-d", "--dir", dest="destination", metavar="DIR",
+                 help="the dir in which the instance home should be created")
+    p.add_option("-s", "--skelsrc", dest="skeleton", metavar="DIR",
+                 default=default_skeleton,
+                 help="template skeleton directory")
+    p.add_option("-u", "--user", dest="username", metavar="USER:PASSWORD",
+                 help="set the user name and password of the initial user")
+    options, args = p.parse_args(argv[1:])
+    options.program = prog
+    options.version = version
+    if args:
+        p.error("too many arguments")
+    options.password = None
+    if options.username and ":" in options.username:
+        options.username, options.password = options.username.split(":", 1)
+    return options


Property changes on: Zope3/trunk/src/zope/app/process/mkzopeinstance.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/app/process/tests/test_mkzopeinstance.py
===================================================================
--- Zope3/trunk/src/zope/app/process/tests/test_mkzopeinstance.py	2004-05-13 21:25:32 UTC (rev 24636)
+++ Zope3/trunk/src/zope/app/process/tests/test_mkzopeinstance.py	2004-05-13 21:58:16 UTC (rev 24637)
@@ -0,0 +1,214 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for the implementation of the mkzopeinstance script.
+
+$Id$
+"""
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+from StringIO import StringIO
+
+from zope.app.process import mkzopeinstance
+
+
+class TestBase(unittest.TestCase):
+
+    def setUp(self):
+        self.stdout = StringIO()
+        self.stderr = StringIO()
+        self.old_stdout = sys.stdout
+        self.old_stderr = sys.stderr
+        sys.stdout = self.stdout
+        sys.stderr = self.stderr
+
+    def tearDown(self):
+        sys.stdout = self.old_stdout
+        sys.stderr = self.old_stderr
+
+
+class ArgumentParsingTestCase(TestBase):
+    """Ensure the command line is properly converted to an options
+    object.
+    """
+
+    def parse_args(self, args):
+        argv = ["foo/bar.py"] + args
+        options = mkzopeinstance.parse_args(argv)
+        self.assertEqual(options.program, "bar.py")
+        self.assert_(options.version)
+        return options
+
+    def test_no_arguments(self):
+        options = self.parse_args([])
+
+    def test_version_long(self):
+        self.check_stdout_content(["--version"])
+
+    def test_help_long(self):
+        self.check_stdout_content(["--help"])
+
+    def test_help_short(self):
+        self.check_stdout_content(["-h"])
+
+    def check_stdout_content(self, args):
+        try:
+            options = self.parse_args(args)
+        except SystemExit, e:
+            self.assertEqual(e.code, 0)
+            self.assert_(self.stdout.getvalue())
+            self.failIf(self.stderr.getvalue())
+        else:
+            self.fail("expected SystemExit")
+
+    def test_without_destination(self):
+        options = self.parse_args([])
+        self.assertEqual(options.destination, None)
+
+    def test_destination_long(self):
+        options = self.parse_args(["--dir", "some/dir"])
+        self.assertEqual(options.destination, "some/dir")
+
+    def test_destination_short(self):
+        options = self.parse_args(["-d", "some/dir"])
+        self.assertEqual(options.destination, "some/dir")
+
+    def test_without_skeleton(self):
+        # make sure we get *some* skeleton directory by default
+        # there's no claim that it exists
+        options = self.parse_args([])
+        self.assertNotEqual(options.skeleton, None)
+
+    def test_with_skeleton_long(self):
+        options = self.parse_args(["--skelsrc", "some/dir"])
+        self.assertEqual(options.skeleton, "some/dir")
+
+    def test_with_skeleton_short(self):
+        options = self.parse_args(["-s", "some/dir"])
+        self.assertEqual(options.skeleton, "some/dir")
+
+    def test_without_username(self):
+        options = self.parse_args([])
+        self.assertEqual(options.username, None)
+        self.assertEqual(options.password, None)
+
+    def test_username_without_password_long(self):
+        options = self.parse_args(["--user", "User"])
+        self.assertEqual(options.username, "User")
+        self.assertEqual(options.password, None)
+
+    def test_username_without_password_short(self):
+        options = self.parse_args(["-u", "User"])
+        self.assertEqual(options.username, "User")
+        self.assertEqual(options.password, None)
+
+    def test_username_with_password_long(self):
+        options = self.parse_args(["--user", "User:Pass"])
+        self.assertEqual(options.username, "User")
+        self.assertEqual(options.password, "Pass")
+
+    def test_username_with_password_short(self):
+        options = self.parse_args(["-u", "User:Pass"])
+        self.assertEqual(options.username, "User")
+        self.assertEqual(options.password, "Pass")
+
+    def test_junk_positional_arg(self):
+        try:
+            self.parse_args(["junk"])
+        except SystemExit, e:
+            self.assert_(e.code)
+        else:
+            self.fail("expected SystemExit")
+
+
+class InputCollectionTestCase(TestBase):
+
+    def setUp(self):
+        super(InputCollectionTestCase, self).setUp()
+        self.tmpdir = tempfile.mkdtemp(prefix="test-mkzopeinstance-")
+        self.skeleton = os.path.join(self.tmpdir, "skel")
+        self.instance = os.path.join(self.tmpdir, "inst")
+        os.mkdir(self.skeleton)
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir)
+        super(InputCollectionTestCase, self).tearDown()
+
+    def createOptions(self):
+        options = Options()
+        options.skeleton = self.skeleton
+        return options
+
+    def test_get_skeltarget(self):
+        options = self.createOptions()
+        input = ["  ", " foo "]
+        app = ControlledInputApplication(options, input)
+        skel = app.get_skeltarget()
+        self.assertEqual(skel, "foo")
+        self.assertEqual(input, [])
+        self.assert_(self.stdout.getvalue())
+
+    def test_process_creates_destination(self):
+        options = self.createOptions()
+        input = [self.instance]
+        app = ControlledInputApplication(options, input)
+        self.assertEqual(app.process(), 0)
+        self.assert_(os.path.isdir(self.instance))
+        self.assertEqual(input, [])
+
+    def test_process_aborts_on_file_destination(self):
+        options = self.createOptions()
+        options.destination = self.instance
+        open(self.instance, "w").close()
+        app = ControlledInputApplication(options, [])
+        self.assertEqual(app.process(), 1)
+        self.assert_(self.stderr.getvalue())
+
+    def test_process_aborts_on_failed_destination_creation(self):
+        options = self.createOptions()
+        options.destination = os.path.join(self.instance, "foo")
+        app = ControlledInputApplication(options, [])
+        self.assertEqual(app.process(), 1)
+        self.assert_(self.stderr.getvalue())
+
+
+class ControlledInputApplication(mkzopeinstance.Application):
+
+    def __init__(self, options, input_lines):
+        mkzopeinstance.Application.__init__(self, options)
+        self.__input = input_lines
+
+    def read_input_line(self, prompt):
+        return self.__input.pop(0)
+
+
+class Options:
+
+    username = "[test-username]"
+    password = "[test-password]"
+    destination = None
+    version = "[test-version]"
+    program = "[test-program]"
+
+
+def test_suite():
+    suite = unittest.makeSuite(ArgumentParsingTestCase)
+    suite.addTest(unittest.makeSuite(InputCollectionTestCase))
+    return suite
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")


Property changes on: Zope3/trunk/src/zope/app/process/tests/test_mkzopeinstance.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native




More information about the Zope3-Checkins mailing list