Commit d1c9a710 authored by Chris McDonough's avatar Chris McDonough

Created 'configure' API function. The 'configure' API function accepts a config

file path and does for scripts what "start_zope" does for interactive startup.

Use the configure function from within zopectl, replacing some gnarly exec'd code.
parent 969753a7
...@@ -12,16 +12,38 @@ ...@@ -12,16 +12,38 @@
# #
############################################################################## ##############################################################################
# this function will actually start up a Zope instance
def run(): def run():
import App.config """ Start a Zope instance """
from Zope.Startup import handlers, options, start_zope from Zope.Startup import start_zope
opts = _setconfig()
start_zope(opts.configroot)
def configure(configfile):
""" Provide an API which allows scripts like zopectl to configure
Zope before attempting to do 'app = Zope.app(). Should be used as
follows: from Zope.Startup.run import configure;
configure('/path/to/configfile'); import Zope; app = Zope.app() """
from Zope.Startup import dropPrivileges
opts = _setconfig(configfile)
dropPrivileges(opts.configroot)
def _setconfig(configfile=None):
""" Configure a Zope instance based on ZopeOptions. Optionally
accept a configfile argument (string path) in order to specify
where the configuration file exists. """
from Zope.Startup import options, handlers
from App import config
opts = options.ZopeOptions() opts = options.ZopeOptions()
if configfile:
opts.configfile=configfile
opts.realize(doc="Sorry, no option docs yet.", raise_getopt_errs=0)
else:
opts.realize(doc="Sorry, no option docs yet.") opts.realize(doc="Sorry, no option docs yet.")
handlers.handleConfig(opts.configroot, opts.confighandlers) handlers.handleConfig(opts.configroot, opts.confighandlers)
import App.config
App.config.setConfiguration(opts.configroot) App.config.setConfiguration(opts.configroot)
start_zope(opts.configroot) return opts
if __name__ == '__main__': if __name__ == '__main__':
run() run()
......
...@@ -15,16 +15,15 @@ ...@@ -15,16 +15,15 @@
import os import os
import cStringIO import cStringIO
import logging
import tempfile import tempfile
import unittest import unittest
import ZConfig import ZConfig
import Zope.Startup import Zope.Startup
from App.config import getConfiguration
from Zope.Startup import ZopeStarter from Zope.Startup import ZopeStarter
from App.config import getConfiguration, setConfiguration
import logging
TEMPNAME = tempfile.mktemp() TEMPNAME = tempfile.mktemp()
TEMPPRODUCTS = os.path.join(TEMPNAME, "Products") TEMPPRODUCTS = os.path.join(TEMPNAME, "Products")
...@@ -40,18 +39,15 @@ def getSchema(): ...@@ -40,18 +39,15 @@ def getSchema():
logger_states = {} logger_states = {}
for name in ('event', 'trace', 'access'): for name in ('event', 'trace', 'access'):
logger = logging.getLogger(name) logger = logging.getLogger(name)
logger_states[name] = {'level': logger.level, logger_states[name] = {'level':logger.level,
'propagate': logger.propagate, 'propagate':logger.propagate,
'handlers': logger.handlers, 'handlers':logger.handlers,
'filters': logger.filters} 'filters':logger.filters}
class ZopeStarterTestCase(unittest.TestCase): class ZopeStarterTestCase(unittest.TestCase):
schema = None
def setUp(self): def setUp(self):
if self.schema is None: self.schema = getSchema()
ZopeStarterTestCase.schema = getSchema()
self.original_event_logger = logging.getLogger self.original_event_logger = logging.getLogger
def tearDown(self): def tearDown(self):
...@@ -70,7 +66,6 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -70,7 +66,6 @@ class ZopeStarterTestCase(unittest.TestCase):
# of the directory is checked. This handles this in a # of the directory is checked. This handles this in a
# platform-independent way. # platform-independent way.
schema = self.schema schema = self.schema
text = "instancehome <<INSTANCE_HOME>>\n" + text
sio = cStringIO.StringIO( sio = cStringIO.StringIO(
text.replace("<<INSTANCE_HOME>>", TEMPNAME)) text.replace("<<INSTANCE_HOME>>", TEMPNAME))
try: try:
...@@ -89,7 +84,9 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -89,7 +84,9 @@ class ZopeStarterTestCase(unittest.TestCase):
import locale import locale
try: try:
try: try:
conf = self.load_config_text("locale en_GB") conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
locale en_GB""")
except ZConfig.DataConversionError, e: except ZConfig.DataConversionError, e:
# Skip this test if we don't have support. # Skip this test if we don't have support.
if e.message.startswith( if e.message.startswith(
...@@ -107,6 +104,7 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -107,6 +104,7 @@ class ZopeStarterTestCase(unittest.TestCase):
import zLOG import zLOG
import sys import sys
conf = self.load_config_text(""" conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
debug-mode on debug-mode on
<eventlog> <eventlog>
level info level info
...@@ -135,6 +133,7 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -135,6 +133,7 @@ class ZopeStarterTestCase(unittest.TestCase):
self.assertEqual(len(zLOG.EventLogger.EventLogger.logger.handlers), 1) self.assertEqual(len(zLOG.EventLogger.EventLogger.logger.handlers), 1)
self.failUnlessEqual(starter.startup_handler.stream, sys.stderr) self.failUnlessEqual(starter.startup_handler.stream, sys.stderr)
conf = self.load_config_text(""" conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
debug-mode off debug-mode off
<eventlog> <eventlog>
level info level info
...@@ -148,7 +147,9 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -148,7 +147,9 @@ class ZopeStarterTestCase(unittest.TestCase):
self.failIfEqual(starter.startup_handler.stream, sys.stderr) self.failIfEqual(starter.startup_handler.stream, sys.stderr)
def testSetupZServerThreads(self): def testSetupZServerThreads(self):
conf = self.load_config_text("zserver-threads 10") conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
zserver-threads 10""")
starter = ZopeStarter(conf) starter = ZopeStarter(conf)
starter.setupZServerThreads() starter.setupZServerThreads()
from ZServer.PubCore import _n from ZServer.PubCore import _n
...@@ -156,6 +157,7 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -156,6 +157,7 @@ class ZopeStarterTestCase(unittest.TestCase):
def testSetupServers(self): def testSetupServers(self):
conf = self.load_config_text(""" conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
<http-server> <http-server>
address 18092 address 18092
</http-server> </http-server>
...@@ -175,24 +177,31 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -175,24 +177,31 @@ class ZopeStarterTestCase(unittest.TestCase):
ZServer.FTPServer) ZServer.FTPServer)
finally: finally:
del conf.servers # should release servers del conf.servers # should release servers
pass
def testSetupServersWithConflict(self): # The rest sets up a conflict by using the same port for the HTTP
conf = self.load_config_text(""" # and FTP servers, relying on socket.bind() to raise an "address
<http-server> # already in use" exception. However, because the sockets specify
address 18092 # SO_REUSEADDR, socket.bind() may not raise that exception.
</http-server> # See <http://zope.org/Collectors/Zope/1104> for gory details.
<ftp-server>
# conflict ## conf = self.load_config_text("""
address 18092 ## instancehome <<INSTANCE_HOME>>
</ftp-server>""") ## <http-server>
starter = ZopeStarter(conf) ## address 18092
# do the job the 'handler' would have done (call prepare) ## </http-server>
for server in conf.servers: ## <ftp-server>
server.prepare('', None, 'Zope', {}, None) ## # conflict
try: ## address 18092
self.assertRaises(ZConfig.ConfigurationError, starter.setupServers) ## </ftp-server>""")
finally: ## starter = ZopeStarter(conf)
del conf.servers ## # do the job the 'handler' would have done (call prepare)
## for server in conf.servers:
## server.prepare('', None, 'Zope', {}, None)
## try:
## self.assertRaises(ZConfig.ConfigurationError, starter.setupServers)
## finally:
## del conf.servers
def testDropPrivileges(self): def testDropPrivileges(self):
# somewhat incomplete because we we're never running as root # somewhat incomplete because we we're never running as root
...@@ -205,23 +214,30 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -205,23 +214,30 @@ class ZopeStarterTestCase(unittest.TestCase):
try: try:
os.getuid = _return0 os.getuid = _return0
# no effective user # no effective user
conf = self.load_config_text("") conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>""")
starter = ZopeStarter(conf) starter = ZopeStarter(conf)
self.assertRaises(ZConfig.ConfigurationError, self.assertRaises(ZConfig.ConfigurationError,
starter.dropPrivileges) starter.dropPrivileges)
# cant find user in passwd database # cant find user in passwd database
conf = self.load_config_text("effective-user n0sucHuS3r") conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
effective-user n0sucHuS3r""")
starter = ZopeStarter(conf) starter = ZopeStarter(conf)
self.assertRaises(ZConfig.ConfigurationError, self.assertRaises(ZConfig.ConfigurationError,
starter.dropPrivileges) starter.dropPrivileges)
# can't specify '0' as effective user # can't specify '0' as effective user
conf = self.load_config_text("effective-user 0") conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
effective-user 0""")
starter = ZopeStarter(conf) starter = ZopeStarter(conf)
self.assertRaises(ZConfig.ConfigurationError, self.assertRaises(ZConfig.ConfigurationError,
starter.dropPrivileges) starter.dropPrivileges)
# setuid to test runner's uid XXX will this work cross-platform? # setuid to test runner's uid XXX will this work cross-platform?
runnerid = _old_getuid() runnerid = _old_getuid()
conf = self.load_config_text("effective-user %s" % runnerid) conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
effective-user %s""" % runnerid)
starter = ZopeStarter(conf) starter = ZopeStarter(conf)
finished = starter.dropPrivileges() finished = starter.dropPrivileges()
self.failUnless(finished) self.failUnless(finished)
...@@ -233,6 +249,7 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -233,6 +249,7 @@ class ZopeStarterTestCase(unittest.TestCase):
import logging import logging
import sys import sys
conf = self.load_config_text(""" conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
debug-mode off debug-mode off
<eventlog> <eventlog>
level info level info
...@@ -277,14 +294,27 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -277,14 +294,27 @@ class ZopeStarterTestCase(unittest.TestCase):
def testMakeLockFile(self): def testMakeLockFile(self):
# put something in the way (it should be deleted) # put something in the way (it should be deleted)
name = os.path.join(TEMPNAME, 'lock') name = os.path.join(TEMPNAME, 'lock')
conf = self.load_config_text("lock-filename %s" % name) conf = self.load_config_text("""
f = open(name, 'a') instancehome <<INSTANCE_HOME>>
f.write('hello') lock-filename %s""" % name
)
f = open(name, 'ab')
# On Windows, the first byte of the file is locked solid, and even
# we (this process) can't read from it via a file object other
# than the one passed to lock_file. So we put a blank
# in the test value first, so we can skip over it later. Also,
# because .seek(1) isn't well-defined for files opened in text
# mode, we open the file in binary mode (above and below).
f.write(' hello')
f.close() f.close()
try: try:
starter = ZopeStarter(conf) starter = ZopeStarter(conf)
starter.makeLockFile() starter.makeLockFile()
self.failIf(open(name).read().find('hello') > -1) f = open(name, 'rb')
f.seek(1) # skip over the locked byte
guts = f.read()
f.close()
self.failIf(guts.find('hello') > -1)
finally: finally:
starter.unlinkLockFile() starter.unlinkLockFile()
self.failIf(os.path.exists(name)) self.failIf(os.path.exists(name))
...@@ -292,7 +322,10 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -292,7 +322,10 @@ class ZopeStarterTestCase(unittest.TestCase):
def testMakePidFile(self): def testMakePidFile(self):
# put something in the way (it should be deleted) # put something in the way (it should be deleted)
name = os.path.join(TEMPNAME, 'pid') name = os.path.join(TEMPNAME, 'pid')
conf = self.load_config_text("pid-filename %s" % name) conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
pid-filename %s""" % name
)
f = open(name, 'a') f = open(name, 'a')
f.write('hello') f.write('hello')
f.close() f.close()
...@@ -304,8 +337,35 @@ class ZopeStarterTestCase(unittest.TestCase): ...@@ -304,8 +337,35 @@ class ZopeStarterTestCase(unittest.TestCase):
starter.unlinkPidFile() starter.unlinkPidFile()
self.failIf(os.path.exists(name)) self.failIf(os.path.exists(name))
def testZopeRunConfigure(self):
old_config = getConfiguration()
try:
os.mkdir(TEMPNAME)
os.mkdir(TEMPPRODUCTS)
except OSError, why:
if why == 17:
# already exists
pass
try:
fname = os.path.join(TEMPNAME, 'zope.conf')
from Zope import configure
f = open(fname, 'w')
f.write('instancehome %s\nzserver-threads 100\n' % TEMPNAME)
f.flush()
f.close()
configure(fname)
new_config = getConfiguration()
self.failUnlessEqual(new_config.zserver_threads, 100)
finally:
try:
os.unlink(fname)
except:
pass
setConfiguration(old_config)
def test_suite(): def test_suite():
return unittest.makeSuite(ZopeStarterTestCase) return unittest.makeSuite(ZopeStarterTestCase)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main(defaultTest="test_suite") unittest.main(defaultTest="test_suite")
...@@ -134,16 +134,8 @@ class ZopeCmd(ZDCmd): ...@@ -134,16 +134,8 @@ class ZopeCmd(ZDCmd):
ZDCmd.do_start(self, arg) ZDCmd.do_start(self, arg)
def get_startup_cmd(self, python, more): def get_startup_cmd(self, python, more):
cmdline = ( '%s -c "from Zope.Startup.options import ZopeOptions; ' cmdline = ( '%s -c "from Zope import configure;'
'from Zope.Startup import handlers as h; ' 'configure(\'%s\');' %
'from App import config; '
'opts=ZopeOptions(); '
'opts.configfile=\'%s\'; '
'opts.realize(); '
'h.handleConfig(opts.configroot,opts.confighandlers);'
'config.setConfiguration(opts.configroot); '
'from Zope.Startup import dropPrivileges; '
'dropPrivileges(opts.configroot); '%
(python, self.options.configfile) (python, self.options.configfile)
) )
return cmdline + more + '\"' return cmdline + more + '\"'
......
...@@ -56,6 +56,7 @@ def debug(*args, **kw): ...@@ -56,6 +56,7 @@ def debug(*args, **kw):
import ZPublisher import ZPublisher
return ZPublisher.test('Zope', *args, **kw) return ZPublisher.test('Zope', *args, **kw)
from Zope.Startup.run import configure
# Zope.App.startup.startup() sets the following variables in this module. # Zope.App.startup.startup() sets the following variables in this module.
DB = None DB = None
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment