[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