Skip to content

rpatterson/iiswsgi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iiswsgi

The iiswsgi module implements a FastCGI to WSGI gateway that is compatible with IIS's variation of the FastCGI protocol. It also provides distutils commands for building, distributing and installing Microsoft Web Deploy (MSDeploy) packages through the Web Platform Installer (WebPI).

The iiswsgi distribution includes two sample IIS apps which can be installed through WebPI once the custom feed has been added:

  1. Install and Launch Web Platform Installer
  2. Use the search box in the upper-right to search for Web Matrix
  3. Click add next to the most recent Web Matrix entry, then Install in the lower-right and follow the instructions
  4. Open the Options dialog by clicking the link on the lower-right
  5. Under Custom Feeds, add the URL for latest *.webpi.xml file from the iiswsgi downloads and click Add feed
  6. Under Which Web Server...?, check IIS Express and then click OK
  7. Use the search box in the upper-right to search for pyramid
  8. Click add next to Pyramid Application then Install in the lower-right and follow the instructions

Assuming an existing Python distribution with a Setup Script using setuptools and a WSGI *.ini Paste config file, roughly the following steps could be used to released to WebPI:

  1. Install iiswsgi into the Python environment used to build releases:

    >C:\Python27\Scripts\easy_install.exe -U iiswsgi
    
  2. Copy the following to the dist root and adjust as appropriate:

    • examples/pyramid.msdeploy/Manifest.xml.in
    • examples/pyramid.msdeploy/Parameters.xml
    • examples/pyramid.msdeploy/iis_install.stamp.in
    • examples/pyramid.msdeploy/web.config.in
  3. Add custom setup to setup.py:

    ...
    from iiswsgi import install_msdeploy
    ...
    class install_custom_msdeploy(install_msdeploy.install_msdeploy):
        def run(self):
            """Perform custom tasks."""
            os.environ['WEB_CONFIG_VAR'] = 'foo'
            self.pre_install()
            CUSTOM_SETUP
            self.post_install()
    ...
    setup(
    ...
          cmdclass=dict(install_msdeploy=install_custom_msdeploy),
    ...
    
  4. Build a MSDeploy package:

    >C:\Python27\python.exe setup.py bdist_msdeploy
    
  5. Add WebPI dependencies to setup.py:

    ...
    setup(
    ...
          extras_require=dict(install_msdeploy=['virtualenv'],
                              webpi_eggs=['virtualenv', 'iiswsgi']),
    ...
    
  6. Add WebPI feed metadata to setup.py:

    See examples/pyramid.msdeploy/setup.py for an example.

  7. Install the Web Platform Installer:

  8. Install fciv.exe to generate SHA1 hashes:

    Must be placed on the %PATH%. The recommended place would be %ProgramFiles%\Microsoft\Web Platform Installer because it's placed on the path when WebPI is installed.

  9. Build a local WebPI feed:

    >C:\Python27\python.exe setup.py bdist_webpi -u "{msdeploy_package_url}" -m .
    
  10. Test locally:

    1. Launch the Web Platform Installer
    2. Click on the options link to the bottom right,
    3. Enter the feed URL below and click Add Feed: file:///C:/Users/%USERNAME%/Documents/GitHub/%DIST_NAME%/dist/%DIST_NAME%-%VERSION%-py2.7-win32.webpi.xml
    4. Click OK and wait for WebPI to parse the feed
    5. Search for your dist and install
    6. Watch WebPI launch Web Matrix and open the site in a browser
  11. Upload/Release:

    >C:\Python27\python.exe setup.py bdist_msdeploy bdist_webpi -m . upload
    

If everything is working correctly, both a MSDeploy zip package and the WebPI feed should be uploaded to PyPI. Then you can instruct users to add the feed to WebPI and they can install your package.

Releasing a WSGI app on IIS involves several steps and moving pieces. See the Web Deploy Package Contents and IIS WSGI Tools sections for more technical details. Here is an overview of the process and the technologies involved.

This is a pre-requisite and is not at all specific to IIS, MSDeploy or WebPI, only Python. This is just a directory with a setup.py Setup Script that defines the distribution and it's metadata and very little is done differently from the standard Python distutils and setuptools ways of doing things. IOW, wherever possible, iiswsgi tries to re-use setup.py metadata and where it needs new metadata it uses setuptools entry points to add setup kwargs.

If the app requires extra set up beyond just setting up a virtualenv and installing dependencies, this can also be implemented in setup.py by subclassing the install_msdeploy Install MSDeploy command. See the Quick Start and the Install MSDeploy command for more details.

Microsoft's Web Deploy Tool is what WebPI uses to install an IIS app and expects a MSDeploy package, simple zip file with some metadata in it. There some special files and three iiswsgi distutils commands that help in defining and building a MSDeploy package. The commands can also be run indiviually or run all at once by running just the last step which will run the others first. Running them individually is useful to debug packaging problems.

  1. Build MSDeploy Package build_msdeploy command
  2. Install MSDeploy install_msdeploy command
  3. Test MSDeploy test_msdeploy command
  4. Build MSDeploy Distribution bdist_msdeploy command

On completion of the last command a MSDeploy zip file will be in the dist directory just like any other dist command, such as sdist. You can also upload the package using the upload command.

Logging output or managing verbosity for building the package is no different than for any other disutils/setup.py uses, output is on the console and can be redirected if you wan to capture it. See >C:\Python27\python.exe setup.py --help for more details.

The Web Platform installer can be given additional feeds in it's options dialog. This feed can define things that can be installed along with their metadata including dependencies. The bdist_webpi command can build this feed as another dist file, and can thus also be released using the upload command.

To test locally, use the bdist_webpi -u "{msdeploy_package_url}" option to put file:///... download URLs for the MSDeploy packages in the feed. Then use the file:///... URL for the feed itself in WebPI's options dialog that is printed to the console when the bdist_webpi command is run.

WebPI logs information while processing the feed in the %LOCALAPPDATA%\Microsoft/Web Platform Installer/logs/webpi diretory. When debugging feed issues just look at the most recently modified webpi#.txt file in that directory.

Once the feed is included in WebPI, the entries can be searched for and installed. After installation, but before WebPI reports completion, any runCommand providers in the MSDeploy Manifest are run which is when iiswsgi_install.exe script is invoked to find the installed app and to run distutils setup commands, install_msdeploy and test_msdeploy by default, in that distribution. Most apps will want to use the iiswsgi_install.exe -e option to setup a virtualenv before running setup commands. See MSDeploy Manifest and install_msdeploy for more details and considerations.

While installing, WebPI and MSDeploy log output into %LOCALAPPDATA%\Microsoft/Web Platform Installer/logs/install. When debugging installation issues just look at the App Title.txt file in the most recently modified date-stamped direstory within that directory. Verbosity can be controlled by adding the iiswsgi_install.exe -v option in your Manifest.xml <runCommand path=... attribute. It's also often valuable to run the install_msdeploy command locally in the installed app after an installation error to debug further.

If installation has completed, there will be a <fastCgi><application... in the global IIS config, a corresponding handler in the app's web.config and when a request comes in for the app, IIS will invoke the handler specified. For iiswsgi, the handler will be an paster serve invocation that uses the egg:iiswsgi#iis FCGI server. To use a general purpose PasteDeploy INI configuration file, you can use a handler like paster.exe serve -s "egg:iiswsgi#iis" ... to use the iiswsgi FCGI server with a configuration file that doesn't specify it.

IIS swallows all FCGI process output if there are any errors starting up which can make startup issues really hard to debug. The first step should be manually invoking the FCGI process using the fullPath and arguments attributes from the <application... element in web.config. In case that doesn't reproduce the error, the egg:iiswsgi#iis FCGI server tries to be conservative during startup to ensure that output is logged somewhere. Check the following locations for output:

  • %IIS_USER_HOME%\Logs\%IISEXPRESS_SITENAME%\iiswsgi.log
  • %IIS_USER_HOME%\Logs\iiswsgi.log
  • %TEMP%\iiswsgi.log
  • \iiswsgi.log

Verbosity is controlled by giving the paster serve -v... option to PasteScript in the web.config.in template.

A developer releasing a MSDeploy package of a Python web app, interacts with iiswsgi though the following files in a Python distribution. Aside from these files, a Web Deploy package using iiswsgi is no different than any other Python distribution or project nor should any of the iiswsgi pieces interfere with any other uses of the same distribution. In particular, it should be possible to build and upload MSDeploy package and WebPI feed dists in the same command as building and uploading any other dist.

As with other Python build, distribute, and install tasks, the setup.py script is where to control how the MSDeploy package is built, what is distributed, and how it's installed.

Use Python's source distribution MANIFEST.in template format to declare what will be in the package. Distributions installing into a virtualenv with a lot of dependencies may want to include the eggs in the virtualenv's site-packages in the MSDeploy package such that the one download will included all the needed eggs greatly reducing network activity and install time:

recursive-include lib/site-packages *
recursive-include Lib\site-packages *
global-exclude *.pyc *.pyo

Use the Manifest.xml.in template to generate the Web Deploy manifest. When using iiswsgi, it contains a runCommand provider that invokes the iswsgi_install.exe MSDeploy Install Bootstrap script. Most packages will want to install into a virtualenv by including a -e option to iiswsgi_install.exe.

The build_msdeploy command can be used to write runCommand option attributes into the hash that MSDeploy uses when processing the manifest during installation. Most apps will want to include the successReturnCodes="0x0" attribute to ensure that failures in the command are reported back to the user. Many apps will also want to adjust the waitAttempts="5" and/or waitInterval="1000" attributes to give the commands enough time to complete.

The Parameters.xml file defines the parameters WebPI will prompt the user for when installing. See examples/pyramid.msdeploy/Parameters.xml for an example of using parameters to influence custom setup.

Use the web.config.in template to generate the IIS site configuration file. When using iiswsgi, it contains a fastCgi application that invokes the egg:iiswsgi#iis iiswsgi FCGI Gateway. Most packages will want to adjust the <application... attributes that control process behavior. This is also where the *.ini config file or app_factory entry point that define the WSGI app to run are specified.

The iis_install.stamp.in template copied into place to serve as the iis_install.stamp stamp file used by the iiswsgi_install.exe MSDeploy Install Bootstrap script to find the right APPL_PHYSICAL_PATH at install time.

The setup.cfg file is only necessary if your Setup Script is not using setuptools. IOW, under setuptools the commands are automatically available is iiswsgi is installed and there's no need for this file. Without setuptools, use the following to make the iiswsgi distutils commands available to your package:

[global]
command_packages = iiswsgi

The moving parts of iiswsgi are as follows:

The egg:iiswsgi#iis paste.server_runner or paste.server_factory is the FastCGI to WSGI gateway. IIS invokes the paster script from PasteScript with a PasteDeploy INI configuration file to start a Python WSGI app as a FastCGI process. Tell paster to use the IIS FCGI gateway with paster.exe serve -s "egg:iiswsgi#iis" ... or in the PasteDeploy INI configuration file:

[server:iis]
use = egg:iiswsgi#iis

This is not intrinsically related to the distutils commands and can be used independently of them if a project should need to.

IIS' implementation of the FastCGI protocol is not fully compliant. Most significantly, what is passed in on STDIN_FILENO is not a handle to an open socket but rather to a Windows named pipe. This names pipe does not support socket-like behavior, at least under Python. As such, the egg:iiswsgi#iis gateway extends flup's WSGI to FCGI gateway to support using STDIN_FILENO opened twice, once each approximating the recv and send end of a socket as is specified in FastCGI.

The build_msdeploy distutils command compiles a MSDeploy Manifest.xml converting any runCommand attributes into the necessary hash. It will also copy into place the IIS Install Stamp File iis_install.stamp stamp file used by the MSDeploy Install Bootstrap iiswsgi_install.exe script to find the right APPL_PHYSICAL_PATH at install time.

The install_msdeploy distutils command performs common actions needed to deploy Python web apps on IIS: install dependencies, do variable substitution in web.config, and install the FastCGI application into the IIS global config.

Since most apps will require path or parameter specific bits in the web.config file, the install_msdeploy command will perform variable substitution while writing the web.config.in template to web.config. To add variables to the substitution, just use Custom Set Up to put them into os.environ before calling the base class's run() method.

Since <fastCgi><application... elements don't take effect in the web.config, the install_msdeploy command will use. For reference or debugging here's an example:

> appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%SystemDrive%\Python27\python.exe',arguments='-u %SystemDrive%\Python27\Scripts\iiswsgi-script.py -c %HOMEDRIVE%%HOMEPATH%\Documents\My Web Sites\FooApp\test.ini',maxInstances='%NUMBER_OF_PROCESSORS%',monitorChangesTo='C:\Users\Administrator\Documents\My Web Sites\FooApp\test.ini']" /commit:apphost

See the IIS FastCGI Reference for more details on how to configure IIS for FastCGI. Note that you cannot use environment variable in the monitorChangesTo argument, IIS will return an opaque 500 error.

This is also where to Custom Set Up by subclassing the install_msdeploy Install MSDeploy command in the setup.py Setup Script and using the distutils cmdclass kwarg to setup(). See Quick Start for a small example or examples\pyramid.msdeploy\setup.py for a working example.

The test_msdeploy distutils command uses paster request with a PasteDeploy INI configuration file to simulate sending a request to the app. If it fails, the command fails, making this useful to run during MSDeploy Package Installation to ensure the user sees an error in WebPI if the app isn't working even though the rest of the install succeeded. See >C:\Python27\python.exe setup.py test_msdeploy --help for more details.

The bdist_msdeploy distutils command assembles an actual MSDeploy package: It starts by running the build_msdeploy Build MSDeploy Package command. Then it runs the install_msdeploy Install MSDeploy command in case your package needs any of the results of the installation process and to test the installation process. Finally, it creates a MSDeploy package zip file with the contents contolled by the same tools that distutils provides for sdist distributions, including MANIFEST.in.

The iiswsgi_install.exe script bootstraps the MSDeploy package install process optionally setting up a virtualenv first. It finds the correct APPL_PHYSICAL_PATH, changes to that directory and invokes the Setup Script with arguments.

This console script attempts to workaround the fact that WebPI and MSDeploy don't provide any context to the app being installed. Specifically, when using the runCommand MSDeploy provider in the Manifest.xml, the process started by runCommand has no way to know which app it's being invoked for on install: not the current working directory, not in an argument, nor in any environment variable.

As such this script has to search for the app before calling it's Setup Script. It uses appcmd.exe to look in virtual directories whose site matches the app name and which contain a stamp file still in place. See >Scripts\iiswsgi_install.exe --help for more details.

The bdist_webpi distutils command assembles a WebPI feed from one or more MSDeploy packages with dependencies. The MSDeploy packages to include are defined by passing paths to distrubutions with setup.py files whose MSDeploy dist zip files have previously been built in the --msdeploy-bdists command option separated by shlex.split. The download URLs for the MSDeploy zip files is determined by expanding the msdeploy_url_template setup() kwarg with Python string.format().

The global feed metadata is taken from the distribution the command is being run for. Entries are added to the feed for the distributions lited in the --msdeploy-bdists command option and the webpi_eggs depdencies in extras_require. The WebPI dependencies and related products are taken from the lists given in the install_msdeploy and install_webpi setup() kwargs respectivels. The metadata for those entries is taken from the corresponding distributions. The following are additional setup() kwargs that are used in the feed if defined for a given distrubution:

  • title
  • author_url
  • license_url
  • display_url
  • help_url
  • published
  • icon_url
  • screenshot_url
  • discovery_file

The clean_webpi distutils command clears the WebPI caches for one or more MSDeploy package downloads and the feed itself. The MSDeploy packages to be cleared from the cache are taken from the same --msdeploy-bdists command option.

One of the more important goals of iiswsgi is to bring some greater transparency and introspection to the process of integrating with IIS. It's a very common experience for developers in the non-Window/UNIX world that developing and even deploying on Windows is much more fragile and opaque than on any other OS. Here's some of what iiswsgi does to try and address that.

Graceful Degredation on non-Windows
Fist and foremost, iiswsgi tries to degrade gracefully when run on non-windows platforms. Specifically, when some executable, environment variable, or other Windows specific piece of the environment is missing, the iiswsgi operation will not raise an exception but only log an error. This allows developing and, to a limited extent, testing MSDeploy packages on NIX platforms. A side-effect of this is that some errors may be missed when there is a lot of console output from one of the `distutils`_ commands when running *on Windows, so check your output carefully.
Logging
Finding information about what went wrong when some part of the process fails can be a lot more difficult on Windows than it is on other platforms. See the sections of How it Works for where to look for log files for each part of the process.

Moved to GitHub issues. In particular, please see if you can help. If you're running into an issue that isn't described in one of the open GitHub issues, try looking through the closed issues.

About

Serve and deploy WSGI apps on Web Platform Installer and IIS.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages