Changeset - 2dad9708c89f
[Not reviewed]
default
0 2 1
Henrik Stuart - 11 years ago 2014-09-14 07:39:06
hg@hstuart.dk
paster: add install-iis command to automate IIS handler generation

A new paster command, install-iis, is added that automates generating
the ISAPI-WSGI file that allows IIS to serve up Kallithea's WSGI application
using IIS' ISAPI filters.

The paster command's output also describes the final steps necessary to complete
IIS installation.
3 files changed with 98 insertions and 38 deletions:
0 comments (0 inline, 0 general)
docs/installation_iis.rst
Show inline comments
 
@@ -41,56 +41,30 @@ to run on the website and consequently, 
 
    as the requirements to Kallithea are enabled by the existing application
 
    pool.
 

	
 
ISAPI Handler
 
.............
 

	
 
The ISAPI handler needs to be generated from a custom file. Imagining that the
 
Kallithea installation is in ``c:\inetpub\kallithea``, we would have a file in
 
the same directory called, e.g. ``dispatch.py`` with the following contents::
 

	
 
    import sys
 
The ISAPI handler can be generated using::
 

	
 
    if hasattr(sys, "isapidllhandle"):
 
        import win32traceutil
 

	
 
    import isapi_wsgi
 

	
 
    def __ExtensionFactory__():
 
        from paste.deploy import loadapp
 
        from paste.script.util.logging_config import fileConfig
 
        fileConfig('c:\\inetpub\\kallithea\\production.ini')
 
        application = loadapp('config:c:\\inetpub\\kallithea\\production.ini')
 
    paster install-iis my.ini --root=/
 

	
 
        def app(environ, start_response):
 
            user = environ.get('REMOTE_USER', None)
 
            if user is not None:
 
                os.environ['REMOTE_USER'] = user
 
            return application(environ, start_response)
 

	
 
        return isapi_wsgi.ISAPIThreadPoolHandler(app)
 
This will generate a ``dispatch.py`` file in the current directory that contains
 
the necessary components to finalize an installation into IIS. Once this file
 
has been generated, it is necessary to run the following command due to the way
 
that ISAPI-WSGI is made::
 

	
 
    if __name__=='__main__':
 
        from isapi.install import *
 
        params = ISAPIParameters()
 
        sm = [ScriptMapParams(Extension="*", Flags=0)]
 
        vd = VirtualDirParameters(Name="/",
 
                                  Description = "ISAPI-WSGI Echo Test",
 
                                  ScriptMaps = sm,
 
                                  ScriptMapUpdate = "replace")
 
        params.VirtualDirs = [vd]
 
        HandleCommandLine(params)
 
    python dispatch.py install
 

	
 
This script has two parts: First, when run directly using Python, it will
 
install a script map ISAPI handler into the root application of the default
 
website, and secondly it will be called from the ISAPI handler when invoked
 
from the website.
 
This accomplishes two things: generating an ISAPI compliant DLL file,
 
``_dispatch.dll``, and installing a script map handler into IIS for the
 
``--root`` specified above pointing to ``_dispatch.dll``.
 

	
 
The ISAPI handler is registered to all file extensions, so it will automatically
 
be the one handling all requests to the website. When the website starts the
 
ISAPI handler, it will start a thread pool managed wrapper around the paster
 
be the one handling all requests to the specified root. When the website starts
 
the ISAPI handler, it will start a thread pool managed wrapper around the paster
 
middleware WSGI handler that Kallithea runs within and each HTTP request to the
 
site will be processed through this logic henceforth.
 

	
 
Authentication with Kallithea using IIS authentication modules
 
..............................................................
 

	
kallithea/lib/paster_commands/install_iis.py
Show inline comments
 
new file 100644
 
import os
 
import sys
 
from paste.script.appinstall import AbstractInstallCommand
 
from paste.script.command import BadCommand
 

	
 
# Add location of top level folder to sys.path
 
from os.path import dirname as dn
 
rc_path = dn(dn(dn(os.path.realpath(__file__))))
 
sys.path.append(rc_path)
 

	
 
class Command(AbstractInstallCommand):
 
    default_verbosity = 1
 
    max_args = 1
 
    min_args = 1
 
    summary = 'Setup IIS given a config file'
 
    usage = 'CONFIG_FILE'
 

	
 
    description = '''
 
    Script for installing into IIS using isapi-wsgi.
 
    '''
 
    parser = AbstractInstallCommand.standard_parser(
 
        simulate=True, quiet=True, interactive=True)
 
    parser.add_option('--virtualdir',
 
                      action='store',
 
                      dest='virtualdir',
 
                      default='/',
 
                      help='The virtual folder to install into on IIS')
 

	
 
    def command(self):
 
        config_spec = self.args[0]
 
        if not config_spec.startswith('config:'):
 
            config_spec = 'config:' + config_spec
 
        config_file = config_spec[len('config:'):].split('#', 1)[0]
 
        config_file = os.path.join(os.getcwd(), config_file)
 
        try:
 
            import isapi_wsgi
 
        except ImportError:
 
            raise BadCommand('missing requirement: isapi-wsgi not installed')
 

	
 
        file = '''import sys
 

	
 
if hasattr(sys, "isapidllhandle"):
 
    import win32traceutil
 

	
 
import isapi_wsgi
 
import os
 

	
 
def __ExtensionFactory__():
 
    from paste.deploy import loadapp
 
    from paste.script.util.logging_config import fileConfig
 
    fileConfig('%(inifile)s')
 
    application = loadapp('config:%(inifile)s')
 

	
 
    def app(environ, start_response):
 
        user = environ.get('REMOTE_USER', None)
 
        if user is not None:
 
            os.environ['REMOTE_USER'] = user
 
        return application(environ, start_response)
 

	
 
    return isapi_wsgi.ISAPIThreadPoolHandler(app)
 

	
 
if __name__=='__main__':
 
    from isapi.install import *
 
    params = ISAPIParameters()
 
    sm = [ScriptMapParams(Extension="*", Flags=0)]
 
    vd = VirtualDirParameters(Name="%(virtualdir)s",
 
                              Description = "Kallithea",
 
                              ScriptMaps = sm,
 
                              ScriptMapUpdate = "replace")
 
    params.VirtualDirs = [vd]
 
    HandleCommandLine(params)
 
'''
 

	
 
        outdata = file % {
 
                'inifile': config_file.replace('\\', '\\\\'),
 
                'virtualdir': self.options.virtualdir
 
                }
 

	
 
        dispatchfile = os.path.join(os.getcwd(), 'dispatch.py')
 
        self.ensure_file(dispatchfile, outdata, False)
 
        print 'generating', dispatchfile
 

	
 
        print ('run \'python "%s" install\' with administrative privileges '
 
            'to generate the _dispatch.dll file and install it into the '
 
            'default web site') % (dispatchfile,)
setup.py
Show inline comments
 
@@ -177,8 +177,9 @@ setup(
 
    repo-scan=kallithea.lib.paster_commands.repo_scan:Command
 
    cache-keys=kallithea.lib.paster_commands.cache_keys:Command
 
    ishell=kallithea.lib.paster_commands.ishell:Command
 
    make-index=kallithea.lib.paster_commands.make_index:Command
 
    upgrade-db=kallithea.lib.dbmigrate:UpgradeDb
 
    celeryd=kallithea.lib.celerypylons.commands:CeleryDaemonCommand
 
    install-iis=kallithea.lib.paster_commands.install_iis:Command
 
    """,
 
)
0 comments (0 inline, 0 general)