├── COPYRIGHT ├── LICENSE ├── README.md ├── bin ├── control ├── install ├── setup └── upgrade ├── conf ├── gunicorn.py └── mime.types ├── env └── PYTHON_EGG_CACHE.erb ├── lib └── util ├── metadata ├── managed_files.yml └── manifest.yml ├── template ├── .openshift │ ├── action_hooks │ │ └── README.md │ ├── cron │ │ ├── README.cron │ │ ├── daily │ │ │ └── .gitignore │ │ ├── hourly │ │ │ └── .gitignore │ │ ├── minutely │ │ │ └── .gitignore │ │ ├── monthly │ │ │ └── .gitignore │ │ └── weekly │ │ │ ├── README │ │ │ ├── chrono.dat │ │ │ ├── chronograph │ │ │ ├── jobs.allow │ │ │ └── jobs.deny │ └── markers │ │ └── .gitkeep ├── app.py ├── nginx.conf.erb.sample ├── public │ └── README └── requirements.txt └── usr ├── nginx └── versions │ └── 1.4 │ ├── bin │ ├── compile │ ├── control │ └── nginx │ └── conf │ ├── nginx.conf.erb │ ├── nginx.gevent.conf.erb │ ├── nginx.gunicorn.conf.erb │ ├── nginx.server.conf.erb │ └── nginx.wsgiref.conf.erb └── python └── versions ├── 2.7 ├── bin │ ├── control │ ├── install │ ├── setup │ └── upgrade ├── lib │ ├── create-virtenv │ ├── python-context │ └── update-configuration ├── metadata │ ├── jenkins_shell_command.erb │ └── rsync.excludes └── servers │ ├── gevent_server.py │ └── wsgiref_server.py └── 3.3 ├── bin ├── control ├── install ├── setup └── upgrade ├── lib ├── create-virtenv ├── distribute_setup.py ├── python-context └── update-configuration ├── metadata ├── jenkins_shell_command.erb └── rsync.excludes └── servers ├── gevent_server.py └── wsgiref_server.py /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright 2014 Goran Sterjov 2 | Copyright 2013 Red Hat, Inc. and/or its affiliates. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## OpenShift Advanced Python Cartridge 2 | 3 | Inspired by the [Advanced Ruby Cartridge](https://github.com/openshift-cartridges/advanced-ruby-cartridge) this cartridge attempts to add support for the various WSGI-compliant python servers to the OpenShift platform. 4 | It does this by combining a modified python cartridge with the [downloadable Nginx cartridge](https://github.com/gsterjov/openshift-nginx-cartridge) as a reverse proxy. 5 | 6 | 7 | ### Why? 8 | 9 | The official python cartridge uses Apache and mod_wsgi to serve your app which isn't asynchronous and presents a problem for websockets. An alternative is to provide an app.py file which allows you to avoid mod_wsgi and use something like gevent, but that elimintates the ability to serve static files through a fast webserver like Apache or Nginx. 10 | 11 | 12 | ### Installation 13 | 14 | To install this cartridge use the cartridge reflector when creating an app 15 | 16 | rhc create-app myapp http://cartreflect-claytondev.rhcloud.com/reflect?github=gsterjov/openshift-advanced-python-cartridge 17 | 18 | 19 | ### Usage 20 | 21 | Using the cartridge isn't very different to the official python cartridge. Instead of providing a WSGI application() function at wsgi/application you instead provide the application() function at app.py. This file will be used directly by all the available servers. 22 | 23 | By default **wsgiref** is used so a working environment can be provided immediately. This is easily changed by setting the OPENSHIFT_PYTHON_SERVER environment variable and then restarting or redeploying the app. 24 | 25 | rhc env set OPENSHIFT_PYTHON_SERVER=gunicorn 26 | rhc app restart 27 | 28 | Be aware, however, that restarting/redeploying after changing servers for the first time might take a fair amount of time. This is because the server packages get compiled and installed on an as needed basis. Gevent and Gunicorn (which is configured to use gevent as its workers), for example, needs to be compiled within the app as OpenShift doesn't provide it as a system level package. 29 | 30 | 31 | ### Supported servers 32 | 33 | - wsgiref 34 | - gevent 35 | - gunicorn 36 | 37 | 38 | ### Configuration 39 | 40 | There is little to no configuration required as most of the details lay in the interaction between Nginx and the WSGI server package. All that is required is to define the application() function in app.py. 41 | Any configuration for the server package will be exposed via environment variables. 42 | 43 | #### Environment Variables 44 | 45 | OPENSHIFT_PYTHON_WORKERS - The number of workers to spawn for packages like gunicorn. 46 | Default: number of CPUs * 2 + 1 47 | 48 | 49 | ### Static files 50 | 51 | Static files will be served from the public/ directory. These files will be served directly by Nginx. 52 | 53 | 54 | ### Web Sockets 55 | 56 | Web socket support is enabled in Nginx, however it does little more than passing the requests through with the appropriate upgrade headers. More complex websocket environments will need to go for the customised nginx.conf option. 57 | 58 | In the future there might be a nicer way to support websockets as a completely separate server. For example, the application might be served out by gunicorn, but websocket services served out with twisted or tornado. These are purely thoughts at the moment however. 59 | 60 | 61 | ### Custom nginx.conf 62 | 63 | Like the standalone Nginx cartridge, its possible to provide your own server configuration to be included in the main nginx.conf file. A sample is provided in the cloned repo as nginx.conf.erb.sample. Simply remove the .sample suffix and commit the changes.nginx.conf.erb will be processed and included in the main configuration every time the server starts. 64 | -------------------------------------------------------------------------------- /bin/control: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 4 | 5 | 6 | NGINX_DIR=$OPENSHIFT_ADVANCED_PYTHON_DIR/usr/nginx/versions/$NGINX_VERSION 7 | PYTHON_DIR=$OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$OPENSHIFT_ADVANCED_PYTHON_VERSION 8 | 9 | 10 | case $1 in 11 | update-configuration) 12 | source $PYTHON_DIR/lib/update-configuration 13 | update-configuration $PYTHON_VERSION 14 | ;; 15 | stop) 16 | $NGINX_DIR/bin/control "$@" 17 | $PYTHON_DIR/bin/control "$@" 18 | ;; 19 | restart) 20 | $NGINX_DIR/bin/control "stop" 21 | $PYTHON_DIR/bin/control "stop" 22 | $PYTHON_DIR/bin/control "start" 23 | $NGINX_DIR/bin/control "start" 24 | ;; 25 | *) 26 | $PYTHON_DIR/bin/control "$@" 27 | $NGINX_DIR/bin/control "$@" 28 | ;; 29 | esac -------------------------------------------------------------------------------- /bin/install: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 4 | 5 | case "$1" in 6 | -v|--version) 7 | version="$2" 8 | esac 9 | 10 | 11 | echo "$version" > env/OPENSHIFT_ADVANCED_PYTHON_VERSION 12 | echo "1.4" > env/NGINX_VERSION 13 | 14 | 15 | for dir in logs run; do 16 | mkdir -p $dir 17 | done 18 | 19 | # Call the version specific install script 20 | exec $OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$version/bin/install $version -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 4 | 5 | case "$1" in 6 | -v|--version) 7 | version="$2" 8 | esac 9 | 10 | # Update environment 11 | source $OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$version/lib/update-configuration 12 | update-configuration 13 | 14 | # Call the version specific setup script 15 | exec $OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$version/bin/setup $version -------------------------------------------------------------------------------- /bin/upgrade: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | python_version="$1" 4 | 5 | upgrade_script="$OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$python_version/bin/upgrade" 6 | 7 | 8 | if [ -e "$upgrade_script" ] 9 | then 10 | exec "$upgrade_script" "$@" 11 | fi 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /conf/gunicorn.py: -------------------------------------------------------------------------------- 1 | import os 2 | import multiprocessing 3 | 4 | cart_dir = os.environ["OPENSHIFT_ADVANCED_PYTHON_DIR"] 5 | tmp_dir = os.environ["OPENSHIFT_TMP_DIR"] 6 | 7 | 8 | if os.environ.has_key("OPENSHIFT_PYTHON_WORKERS"): 9 | workers = os.environ["OPENSHIFT_PYTHON_WORKERS"] 10 | else: 11 | workers = multiprocessing.cpu_count() * 2 + 1 12 | 13 | 14 | worker_class = "gevent" 15 | daemon = True 16 | bind = "unix:{0}run/appserver.sock".format(cart_dir) 17 | pidfile = "{0}run/appserver.pid".format(cart_dir) 18 | 19 | accesslog = "{0}logs/appserver.access.log".format(cart_dir) 20 | errorlog = "{0}logs/appserver.error.log".format(cart_dir) 21 | 22 | worker_tmp_dir = "{0}".format(tmp_dir) 23 | tmp_upload_dir = "{0}".format(tmp_dir) -------------------------------------------------------------------------------- /conf/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/x-javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/java-archive jar war ear; 28 | application/mac-binhex40 hqx; 29 | application/msword doc; 30 | application/pdf pdf; 31 | application/postscript ps eps ai; 32 | application/rtf rtf; 33 | application/vnd.ms-excel xls; 34 | application/vnd.ms-powerpoint ppt; 35 | application/vnd.wap.wmlc wmlc; 36 | application/vnd.google-earth.kml+xml kml; 37 | application/vnd.google-earth.kmz kmz; 38 | application/x-7z-compressed 7z; 39 | application/x-cocoa cco; 40 | application/x-java-archive-diff jardiff; 41 | application/x-java-jnlp-file jnlp; 42 | application/x-makeself run; 43 | application/x-perl pl pm; 44 | application/x-pilot prc pdb; 45 | application/x-rar-compressed rar; 46 | application/x-redhat-package-manager rpm; 47 | application/x-sea sea; 48 | application/x-shockwave-flash swf; 49 | application/x-stuffit sit; 50 | application/x-tcl tcl tk; 51 | application/x-x509-ca-cert der pem crt; 52 | application/x-xpinstall xpi; 53 | application/xhtml+xml xhtml; 54 | application/zip zip; 55 | 56 | application/octet-stream bin exe dll; 57 | application/octet-stream deb; 58 | application/octet-stream dmg; 59 | application/octet-stream eot; 60 | application/octet-stream iso img; 61 | application/octet-stream msi msp msm; 62 | 63 | audio/midi mid midi kar; 64 | audio/mpeg mp3; 65 | audio/ogg ogg; 66 | audio/x-m4a m4a; 67 | audio/x-realaudio ra; 68 | 69 | video/3gpp 3gpp 3gp; 70 | video/mp4 mp4; 71 | video/mpeg mpeg mpg; 72 | video/quicktime mov; 73 | video/webm webm; 74 | video/x-flv flv; 75 | video/x-m4v m4v; 76 | video/x-mng mng; 77 | video/x-ms-asf asx asf; 78 | video/x-ms-wmv wmv; 79 | video/x-msvideo avi; 80 | } 81 | -------------------------------------------------------------------------------- /env/PYTHON_EGG_CACHE.erb: -------------------------------------------------------------------------------- 1 | <%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>virtenv/.python-eggs/ 2 | -------------------------------------------------------------------------------- /lib/util: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Utility functions for use in the cartridge scripts. 3 | 4 | function parse_args { 5 | while : 6 | do 7 | case $1 in 8 | -h | --help | -\?) 9 | echo "usage: $0 [--version[=]]" 10 | exit 0 11 | ;; 12 | -v | --version) 13 | version=$2 # You might want to check if you really got VERSION 14 | shift 2 15 | ;; 16 | --version=*) 17 | version=${1#*=} # Delete everything up till "=" 18 | shift 19 | ;; 20 | --) # End of all options 21 | shift 22 | break 23 | ;; 24 | -*) 25 | echo "WARN: Unknown option... Exiting: $1" >&2 26 | exit 1 27 | ;; 28 | *) # no more options. Stop while loop 29 | break 30 | ;; 31 | esac 32 | done 33 | } 34 | -------------------------------------------------------------------------------- /metadata/managed_files.yml: -------------------------------------------------------------------------------- 1 | processed_templates: 2 | - 'conf/*.erb' 3 | dependency_dirs: 4 | - virtenv 5 | build_dependency_dirs: 6 | - ~/.distlib -------------------------------------------------------------------------------- /metadata/manifest.yml: -------------------------------------------------------------------------------- 1 | Name: advanced-python 2 | Cartridge-Short-Name: ADVANCED_PYTHON 3 | Display-Name: Advanced Python 2.7 4 | Description: 'An advanced python cartridge utilising an Nginx reverse proxy and a choice of modern WSGI servers such as gevent or gunicorn' 5 | Version: '2.7' 6 | Versions: 7 | - '2.7' 8 | - '3.3' 9 | License: The Python License, version 2.7 10 | License-Url: http://docs.python.org/3/license.html 11 | Vendor: python.org 12 | Cartridge-Version: 0.0.1 13 | Cartridge-Vendor: gsterjov 14 | Categories: 15 | - service 16 | - python 17 | - web_framework 18 | Website: http://www.python.org 19 | Help-Topics: 20 | Developer Center: https://www.openshift.com/developers 21 | Provides: 22 | - python-2.7 23 | - python 24 | - python(version) = 2.7 25 | Publishes: 26 | Subscribes: 27 | set-env: 28 | Type: ENV:* 29 | Required: false 30 | set-doc-url: 31 | Type: STRING:urlpath 32 | Required: false 33 | Scaling: 34 | Min: 1 35 | Max: -1 36 | Endpoints: 37 | - Private-IP-Name: IP 38 | Private-Port-Name: PORT 39 | Private-Port: 8080 40 | Public-Port-Name: PROXY_PORT 41 | Protocols: 42 | - http 43 | - ws 44 | Options: 45 | primary: true 46 | Mappings: 47 | - Frontend: '' 48 | Backend: '' 49 | Options: 50 | websocket: true 51 | - Frontend: /health 52 | Backend: '' 53 | Options: 54 | health: true 55 | Version-Overrides: 56 | '3.3': 57 | Display-Name: Advanced Python 3.3 58 | License: The Python License, version 3.3 59 | Provides: 60 | - python-3.3 61 | - python 62 | - python(version) = 3.3 63 | Categories: 64 | - service 65 | - python 66 | - web_framework 67 | Install-Build-Required: false -------------------------------------------------------------------------------- /template/.openshift/action_hooks/README.md: -------------------------------------------------------------------------------- 1 | For information about action hooks supported by OpenShift, consult the documentation: 2 | 3 | http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory 4 | -------------------------------------------------------------------------------- /template/.openshift/cron/README.cron: -------------------------------------------------------------------------------- 1 | Run scripts or jobs on a periodic basis 2 | ======================================= 3 | Any scripts or jobs added to the minutely, hourly, daily, weekly or monthly 4 | directories will be run on a scheduled basis (frequency is as indicated by the 5 | name of the directory) using run-parts. 6 | 7 | run-parts ignores any files that are hidden or dotfiles (.*) or backup 8 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} 9 | 10 | The presence of two specially named files jobs.deny and jobs.allow controls 11 | how run-parts executes your scripts/jobs. 12 | jobs.deny ===> Prevents specific scripts or jobs from being executed. 13 | jobs.allow ===> Only execute the named scripts or jobs (all other/non-named 14 | scripts that exist in this directory are ignored). 15 | 16 | The principles of jobs.deny and jobs.allow are the same as those of cron.deny 17 | and cron.allow and are described in detail at: 18 | http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Automating_System_Tasks.html#s2-autotasks-cron-access 19 | 20 | See: man crontab or above link for more details and see the the weekly/ 21 | directory for an example. 22 | 23 | PLEASE NOTE: The Cron cartridge must be installed in order to run the configured jobs. 24 | -------------------------------------------------------------------------------- /template/.openshift/cron/daily/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/template/.openshift/cron/daily/.gitignore -------------------------------------------------------------------------------- /template/.openshift/cron/hourly/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/template/.openshift/cron/hourly/.gitignore -------------------------------------------------------------------------------- /template/.openshift/cron/minutely/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/template/.openshift/cron/minutely/.gitignore -------------------------------------------------------------------------------- /template/.openshift/cron/monthly/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/template/.openshift/cron/monthly/.gitignore -------------------------------------------------------------------------------- /template/.openshift/cron/weekly/README: -------------------------------------------------------------------------------- 1 | Run scripts or jobs on a weekly basis 2 | ===================================== 3 | Any scripts or jobs added to this directory will be run on a scheduled basis 4 | (weekly) using run-parts. 5 | 6 | run-parts ignores any files that are hidden or dotfiles (.*) or backup 7 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} and handles 8 | the files named jobs.deny and jobs.allow specially. 9 | 10 | In this specific example, the chronograph script is the only script or job file 11 | executed on a weekly basis (due to white-listing it in jobs.allow). And the 12 | README and chrono.dat file are ignored either as a result of being black-listed 13 | in jobs.deny or because they are NOT white-listed in the jobs.allow file. 14 | 15 | For more details, please see ../README.cron file. 16 | 17 | -------------------------------------------------------------------------------- /template/.openshift/cron/weekly/chrono.dat: -------------------------------------------------------------------------------- 1 | Time And Relative D...n In Execution (Open)Shift! 2 | -------------------------------------------------------------------------------- /template/.openshift/cron/weekly/chronograph: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "`date`: `cat $(dirname \"$0\")/chrono.dat`" 4 | -------------------------------------------------------------------------------- /template/.openshift/cron/weekly/jobs.allow: -------------------------------------------------------------------------------- 1 | # 2 | # Script or job files listed in here (one entry per line) will be 3 | # executed on a weekly-basis. 4 | # 5 | # Example: The chronograph script will be executed weekly but the README 6 | # and chrono.dat files in this directory will be ignored. 7 | # 8 | # The README file is actually ignored due to the entry in the 9 | # jobs.deny which is checked before jobs.allow (this file). 10 | # 11 | chronograph 12 | 13 | -------------------------------------------------------------------------------- /template/.openshift/cron/weekly/jobs.deny: -------------------------------------------------------------------------------- 1 | # 2 | # Any script or job files listed in here (one entry per line) will NOT be 3 | # executed (read as ignored by run-parts). 4 | # 5 | 6 | README 7 | 8 | -------------------------------------------------------------------------------- /template/.openshift/markers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/template/.openshift/markers/.gitkeep -------------------------------------------------------------------------------- /template/app.py: -------------------------------------------------------------------------------- 1 | 2 | # This is your WSGI entry point. All server frameworks 3 | # supported by the cartridge will look for application() in 4 | # the app.py file. 5 | 6 | def application(environ, start_response): 7 | 8 | ctype = 'text/plain' 9 | if environ['PATH_INFO'] == '/health': 10 | response_body = "1" 11 | elif environ['PATH_INFO'] == '/env': 12 | response_body = ['%s: %s' % (key, value) 13 | for key, value in sorted(environ.items())] 14 | response_body = '\n'.join(response_body) 15 | else: 16 | ctype = 'text/html' 17 | response_body = ''' 18 | 19 | 20 | 21 | 22 | Welcome to OpenShift 23 | 221 | 222 | 223 |
224 |
225 |

Welcome to your Advanced Python application on OpenShift

226 |
227 | 228 |
229 |
230 |
231 |

Deploying code changes

232 |

OpenShift uses the Git version control system for your source code, and grants you access to it via the Secure Shell (SSH) protocol. In order to upload and download code to your application you need to give us your public SSH key. You can upload it within the web console or install the RHC command line tool and run rhc setup to generate and upload your key automatically.

233 | 234 |

Working in your local Git repository

235 |

If you created your application from the command line and uploaded your SSH key, rhc will automatically download a copy of that source code repository (Git calls this 'cloning') to your local system.

236 | 237 |

If you created the application from the web console, you'll need to manually clone the repository to your local system. Copy the application's source code Git URL and then run:

238 | 239 |
$ git clone <git_url> <directory_to_create>
240 | 
241 | # Within your project directory
242 | # Commit your changes and push to OpenShift
243 | 
244 | $ git commit -a -m 'Some commit message'
245 | $ git push
246 | 250 | 251 |
252 | 253 | 254 |
255 |
256 | 257 |

Managing your application

258 | 259 |

Web Console

260 |

You can use the OpenShift web console to enable additional capabilities via cartridges, add collaborator access authorizations, designate custom domain aliases, and manage domain memberships.

261 | 262 |

Command Line Tools

263 |

Installing the OpenShift RHC client tools allows you complete control of your cloud environment. Read more on how to manage your application from the command line in our User Guide. 264 |

265 | 266 |

Development Resources

267 | 275 | 276 | 277 |
278 |
279 | 280 | 281 |
282 | 283 |
284 |
285 | 286 | 287 | ''' 288 | 289 | status = '200 OK' 290 | response_headers = [('Content-Type', ctype), ('Content-Length', str(len(response_body)))] 291 | # 292 | start_response(status, response_headers) 293 | return [response_body] -------------------------------------------------------------------------------- /template/nginx.conf.erb.sample: -------------------------------------------------------------------------------- 1 | # This is an example of how to override the default integration with 2 | # nginx and your chosen appserver. The http configuration is still handled 3 | # by the cartridge, however you can define your own server configuration and 4 | # directly control how requests are handled. 5 | 6 | server { 7 | listen <%= ENV['OPENSHIFT_ADVANCED_PYTHON_IP'] %>:<%= ENV['OPENSHIFT_ADVANCED_PYTHON_PORT'] %>; 8 | root <%= ENV['OPENSHIFT_REPO_DIR'] %>public; 9 | 10 | server_name _; 11 | keepalive_timeout 5; 12 | 13 | # haproxy health checks don't send any http_host headers 14 | # so we set the Host header to the app DNS if the header is empty. 15 | # This is needed for frameworks that validate the Host header such as Django. 16 | set $_host $http_host; 17 | if ($http_host = "") { 18 | set $_host "<%= ENV['OPENSHIFT_APP_DNS'] %>"; 19 | } 20 | 21 | location / { 22 | try_files $uri @proxy_to_app; 23 | } 24 | 25 | location @proxy_to_app { 26 | # websocket support 27 | proxy_http_version 1.1; 28 | proxy_set_header Upgrade $http_upgrade; 29 | proxy_set_header Connection $connection_upgrade; 30 | 31 | proxy_set_header Host $_host; 32 | proxy_set_header X-Real-IP $remote_addr; 33 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 34 | proxy_redirect off; 35 | 36 | # the upstream 'appserver' always points to the selected python server 37 | proxy_pass http://appserver; 38 | } 39 | } -------------------------------------------------------------------------------- /template/public/README: -------------------------------------------------------------------------------- 1 | Public, static content goes here. -------------------------------------------------------------------------------- /template/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/template/requirements.txt -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/bin/compile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # usage: bin/compile 4 | 5 | set -eo pipefail 6 | 7 | mkdir -p "$1/out" "$1/src" 8 | bin_dir=$(cd "$1/out" && pwd) 9 | src_dir=$(cd "$1/src" && pwd) 10 | buildpack=$(dirname $(dirname $0)) 11 | 12 | pcre_ver=${PCRE_VERSION:-8.33} 13 | pcre_file=${PCRE_FILE:-pcre-$pcre_ver.tar.gz} 14 | pcre_url=${PCRE_URL:-http://sourceforge.net/projects/pcre/files/pcre/$pcre_ver/$pcre_file} 15 | 16 | nginx_ver=${NGINX_VERSION:-1.4} 17 | nginx_patch=4 18 | nginx_file=${NGINX_FILE:-nginx-$nginx_ver.$nginx_patch.tar.gz} 19 | nginx_url=${NGINX_URL:-http://nginx.org/download/$nginx_file} 20 | 21 | 22 | if test -d $src_dir/pcre-$pcre_ver 23 | then 24 | echo "-----> Using PCRE $pcre_ver" 25 | else 26 | mkdir -p $src_dir/pcre-$pcre_ver 27 | cd $src_dir 28 | echo " First download, may take several minutes" 29 | echo -n "-----> Installing PCRE $pcre_ver..." 30 | curl -sOL $pcre_url 31 | tar zxf $pcre_file 32 | rm -f $pcre_file 33 | echo " done" 34 | fi 35 | 36 | if test -d $src_dir/nginx-$nginx_ver.$nginx_patch 37 | then 38 | echo "-----> Using Nginx $nginx_ver.$nginx_patch" 39 | else 40 | mkdir -p $src_dir/nginx-$nginx_ver.$nginx_patch 41 | cd $src_dir 42 | echo " First download, may take several minutes" 43 | echo -n "-----> Installing Nginx $nginx_ver.$nginx_patch..." 44 | curl -sO $nginx_url 45 | tar zxf $nginx_file 46 | rm -f $nginx_file 47 | echo " done" 48 | fi 49 | 50 | 51 | cd $src_dir/nginx-$nginx_ver.$nginx_patch/ 52 | ./configure --with-pcre=$src_dir/pcre-$pcre_ver/ --with-http_ssl_module 53 | make 54 | cp -f objs/nginx $bin_dir 55 | make clean -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/bin/control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 4 | 5 | 6 | # default python server 7 | if [ ! $OPENSHIFT_PYTHON_SERVER ]; then 8 | echo wsgiref > $OPENSHIFT_HOMEDIR/.env/user_vars/OPENSHIFT_PYTHON_SERVER 9 | export OPENSHIFT_PYTHON_SERVER=wsgiref 10 | fi 11 | 12 | 13 | NGINX_EXEC=$OPENSHIFT_ADVANCED_PYTHON_DIR/usr/nginx/versions/$NGINX_VERSION/bin/nginx 14 | NGINX_CONFIG_TEMPLATE_DIR=$OPENSHIFT_ADVANCED_PYTHON_DIR/usr/nginx/versions/$NGINX_VERSION/conf 15 | NGINX_CONFIG_FILE=$OPENSHIFT_ADVANCED_PYTHON_DIR/conf/nginx.conf 16 | NGINX_PID_FILE=$OPENSHIFT_ADVANCED_PYTHON_DIR/run/nginx.pid 17 | 18 | 19 | function start() { 20 | echo "Starting Nginx" 21 | 22 | # generate the config files 23 | oo-erb $NGINX_CONFIG_TEMPLATE_DIR/nginx.conf.erb > $NGINX_CONFIG_FILE 24 | oo-erb $NGINX_CONFIG_TEMPLATE_DIR/nginx.$OPENSHIFT_PYTHON_SERVER.conf.erb > conf/nginx.appserver.conf 25 | 26 | # customised server configuration 27 | if [ -f $OPENSHIFT_REPO_DIR/nginx.conf.erb ]; then 28 | oo-erb $OPENSHIFT_REPO_DIR/nginx.conf.erb > conf/nginx.server.conf 29 | else 30 | oo-erb $NGINX_CONFIG_TEMPLATE_DIR/nginx.server.conf.erb > conf/nginx.server.conf 31 | fi 32 | 33 | # nginx will always use the prefix defined at compile time unless an absolute path to the conf file is given 34 | nohup "$NGINX_EXEC" -c "$NGINX_CONFIG_FILE" > logs/nginx.log 2>&1 35 | [ "$?" == "0" ] && wait_for_pid_file $NGINX_PID_FILE 36 | } 37 | 38 | 39 | function stop() { 40 | echo "Stopping Nginx" 41 | 42 | if [ -f "$NGINX_PID_FILE" ]; then 43 | pid=`cat "$NGINX_PID_FILE" 2> /dev/null` 44 | eval "'$NGINX_EXEC' -c '$NGINX_CONFIG_FILE' -s stop" 45 | wait_for_stop $pid 46 | fi 47 | } 48 | 49 | 50 | function restart() { 51 | echo "Restarting Nginx" 52 | stop 53 | start 54 | } 55 | 56 | 57 | function status() { 58 | res=0 59 | output=$(curl -s -m 30 http://$OPENSHIFT_ADVANCED_PYTHON_IP:$OPENSHIFT_ADVANCED_PYTHON_PORT/ &> /dev/null) || res=1 60 | 61 | if [ $res -eq 0 ] 62 | then 63 | client_result "Nginx is running" 64 | client_result "$output" 65 | else 66 | client_result "Nginx is either stopped or inaccessible" 67 | fi 68 | } 69 | 70 | 71 | function reload() { 72 | echo "Reloading Nginx" 73 | 74 | if [ -f "$NGINX_PID_FILE" ] 75 | then 76 | pid=`cat "$NGINX_PID_FILE" 2> /dev/null` 77 | output=$("$NGINX_EXEC" -c "$NGINX_CONFIG_FILE" -s reload) 78 | client_result "$output" 79 | else 80 | client_result "Cannot reload Nginx as it is not running" 81 | fi 82 | } 83 | 84 | 85 | function tidy() { 86 | client_message "Emptying Nginx logs in: $OPENSHIFT_ADVANCED_PYTHON_DIR/logs" 87 | rm logs/nginx.log 88 | rm logs/access.log 89 | rm logs/error.log 90 | } 91 | 92 | 93 | function update() { 94 | version=${NGINX_VERSION:-1.4} 95 | 96 | echo "Rebuilding Nginx v$version" 97 | stop 98 | 99 | mkdir -p tmp/build 100 | usr/nginx/versions/$version/bin/compile tmp/build 101 | 102 | bin_dir="usr/nginx/versions/$version/bin" 103 | mkdir -p $bin_dir 104 | cp -f tmp/build/out/nginx $bin_dir 105 | 106 | rm -rf tmp/build 107 | } 108 | 109 | 110 | case "$1" in 111 | start) start ;; 112 | stop) stop ;; 113 | restart) restart ;; 114 | status) status ;; 115 | reload) reload ;; 116 | tidy) tidy ;; 117 | update) update ;; 118 | *) exit 0 119 | esac 120 | -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/bin/nginx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/usr/nginx/versions/1.4/bin/nginx -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/conf/nginx.conf.erb: -------------------------------------------------------------------------------- 1 | error_log <%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>logs/error.log; 2 | pid <%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>run/nginx.pid; 3 | 4 | worker_processes 1; 5 | 6 | events { 7 | worker_connections 1024; 8 | accept_mutex off; 9 | } 10 | 11 | 12 | http { 13 | include mime.types; 14 | default_type application/octet-stream; 15 | 16 | client_body_temp_path <%= ENV['OPENSHIFT_TMP_DIR'] %>client_temp; 17 | proxy_temp_path <%= ENV['OPENSHIFT_TMP_DIR'] %>proxy_temp; 18 | fastcgi_temp_path <%= ENV['OPENSHIFT_TMP_DIR'] %>fastcgi_temp; 19 | uwsgi_temp_path <%= ENV['OPENSHIFT_TMP_DIR'] %>uwsgi_temp; 20 | scgi_temp_path <%= ENV['OPENSHIFT_TMP_DIR'] %>scgi_temp; 21 | 22 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 23 | '$status $body_bytes_sent "$http_referer" ' 24 | '"$http_user_agent" "$http_x_forwarded_for"'; 25 | 26 | access_log <%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>logs/access.log main; 27 | 28 | sendfile on; 29 | gzip on; 30 | 31 | keepalive_timeout 65; 32 | 33 | 34 | map $http_upgrade $connection_upgrade { 35 | default upgrade; 36 | '' close; 37 | } 38 | 39 | 40 | include <%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>conf/nginx.appserver.conf; 41 | include <%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>conf/nginx.server.conf; 42 | } -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/conf/nginx.gevent.conf.erb: -------------------------------------------------------------------------------- 1 | upstream appserver { 2 | server unix:<%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>run/appserver.sock fail_timeout=0; 3 | } -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/conf/nginx.gunicorn.conf.erb: -------------------------------------------------------------------------------- 1 | upstream appserver { 2 | server unix:<%= ENV['OPENSHIFT_ADVANCED_PYTHON_DIR'] %>run/appserver.sock fail_timeout=0; 3 | } -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/conf/nginx.server.conf.erb: -------------------------------------------------------------------------------- 1 | server { 2 | listen <%= ENV['OPENSHIFT_ADVANCED_PYTHON_IP'] %>:<%= ENV['OPENSHIFT_ADVANCED_PYTHON_PORT'] %>; 3 | root <%= ENV['OPENSHIFT_REPO_DIR'] %>public; 4 | 5 | server_name _; 6 | keepalive_timeout 5; 7 | 8 | # haproxy health checks don't send any http_host headers 9 | # so we set the Host header to the app DNS if the header is empty. 10 | # This is needed for frameworks that validate the Host header such as Django. 11 | set $_host $http_host; 12 | if ($http_host = "") { 13 | set $_host "<%= ENV['OPENSHIFT_APP_DNS'] %>"; 14 | } 15 | 16 | location / { 17 | try_files $uri @proxy_to_app; 18 | } 19 | 20 | location @proxy_to_app { 21 | # websocket support 22 | proxy_http_version 1.1; 23 | proxy_set_header Upgrade $http_upgrade; 24 | proxy_set_header Connection $connection_upgrade; 25 | 26 | proxy_set_header Host $_host; 27 | proxy_set_header X-Real-IP $remote_addr; 28 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 29 | proxy_redirect off; 30 | 31 | proxy_pass http://appserver; 32 | } 33 | } -------------------------------------------------------------------------------- /usr/nginx/versions/1.4/conf/nginx.wsgiref.conf.erb: -------------------------------------------------------------------------------- 1 | upstream appserver { 2 | server <%= ENV['OPENSHIFT_ADVANCED_PYTHON_IP'] %>:15000 fail_timeout=30; 3 | } -------------------------------------------------------------------------------- /usr/python/versions/2.7/bin/control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 4 | source "${OPENSHIFT_ADVANCED_PYTHON_DIR}/usr/python/versions/${OPENSHIFT_ADVANCED_PYTHON_VERSION}/lib/create-virtenv" 5 | 6 | 7 | # default python server 8 | if [ ! $OPENSHIFT_PYTHON_SERVER ]; then 9 | echo wsgiref > $OPENSHIFT_HOMEDIR/.env/user_vars/OPENSHIFT_PYTHON_SERVER 10 | export OPENSHIFT_PYTHON_SERVER=wsgiref 11 | fi 12 | 13 | 14 | function start_wsgiref() { 15 | nohup python usr/python/versions/2.7/servers/wsgiref_server.py > "$OPENSHIFT_ADVANCED_PYTHON_DIR/logs/appserver.log" 2>&1 & 16 | echo $! > $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid 17 | } 18 | 19 | function start_gevent() { 20 | nohup python usr/python/versions/2.7/servers/gevent_server.py > "$OPENSHIFT_ADVANCED_PYTHON_DIR/logs/appserver.log" 2>&1 & 21 | echo $! > $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid 22 | } 23 | 24 | function start_gunicorn() { 25 | nohup gunicorn -c $OPENSHIFT_ADVANCED_PYTHON_DIR/conf/gunicorn.py app:application > "$OPENSHIFT_ADVANCED_PYTHON_DIR/logs/appserver.log" 2>&1 26 | } 27 | 28 | 29 | function start() { 30 | echo "Starting PYTHON cart" 31 | 32 | echo "Activating virtenv" 33 | activate-virtenv 34 | 35 | # app servers have changed 36 | if [ "$OPENSHIFT_PYTHON_SERVER" != "$OPENSHIFT_PYTHON_RUNNING_SERVER" ]; then 37 | build_server 38 | fi 39 | 40 | export PYTHONPATH=$PYTHONPATH:$OPENSHIFT_REPO_DIR 41 | 42 | case "$OPENSHIFT_PYTHON_SERVER" in 43 | wsgiref) 44 | start_wsgiref 45 | ;; 46 | gevent) 47 | start_gevent 48 | ;; 49 | gunicorn) 50 | start_gunicorn 51 | ;; 52 | esac 53 | } 54 | 55 | 56 | function stop() { 57 | echo "Stopping PYTHON cart" 58 | 59 | if [ -s $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid ] 60 | then 61 | kpid=$(cat $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid) 62 | iters=0 63 | while kill $kpid >/dev/null 2>&1 64 | do 65 | iters=$(($iters + 1)) 66 | [ $iters -gt 10 ] && break 67 | sleep 1 68 | done 69 | kill -KILL $kpid >/dev/null 2>&1 || : 70 | rm -f $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid 71 | 72 | if [ -S $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.sock ]; then 73 | rm -f $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.sock 74 | fi 75 | fi 76 | } 77 | 78 | 79 | function restart() { 80 | echo "Restarting PYTHON cart" 81 | stop 82 | start 83 | } 84 | 85 | function status() { 86 | res=0 87 | output=$(curl -s -m 30 http://$OPENSHIFT_ADVANCED_PYTHON_IP:$OPENSHIFT_ADVANCED_PYTHON_PORT/ &> /dev/null) || res=1 88 | 89 | if [ $res -eq 0 ] 90 | then 91 | client_result "Application is running" 92 | client_result "$output" 93 | else 94 | client_result "Application is either stopped or inaccessible" 95 | fi 96 | } 97 | 98 | function reload() { 99 | echo "Reloading PYTHON cart" 100 | restart 101 | } 102 | 103 | # Clean up any log files 104 | function tidy() { 105 | client_message "Emptying python logs in: $OPENSHIFT_ADVANCED_PYTHON_DIR/logs" 106 | rm logs/appserver.log 107 | rm logs/appserver.*.log 108 | } 109 | 110 | 111 | # install the selected server 112 | function build_server() { 113 | case "$OPENSHIFT_PYTHON_SERVER" in 114 | gevent) 115 | pip install gevent 116 | ;; 117 | gunicorn) 118 | pip install gevent gunicorn 119 | ;; 120 | esac 121 | 122 | echo $OPENSHIFT_PYTHON_SERVER > env/OPENSHIFT_PYTHON_RUNNING_SERVER 123 | export OPENSHIFT_PYTHON_RUNNING_SERVER=$OPENSHIFT_PYTHON_SERVER 124 | } 125 | 126 | 127 | function build() { 128 | if [ -n "$OPENSHIFT_PYPI_MIRROR_URL" ]; then 129 | m=$OPENSHIFT_PYPI_MIRROR_URL 130 | if curl -m 15 -f -s "$m" &>/dev/null 131 | then 132 | OPENSHIFT_PYTHON_MIRROR="-i $m" 133 | fi 134 | fi 135 | 136 | if force_clean_build_enabled_for_latest_deployment; then 137 | echo "Force-clean builds are enabled" 1>&2 138 | rm -rf $VIRTUAL_ENV/* 139 | fi 140 | 141 | if [ ! -f $VIRTUAL_ENV/bin/python ]; then 142 | echo "Recreating virtenv" 1>&2 143 | create-virtenv 144 | fi 145 | 146 | echo "Activating virtenv" 147 | activate-virtenv 148 | 149 | build_server 150 | 151 | if [ -f ${OPENSHIFT_REPO_DIR}/requirements.txt ] 152 | then 153 | ( cd $OPENSHIFT_REPO_DIR; pip install -r ${OPENSHIFT_REPO_DIR}/requirements.txt $OPENSHIFT_PYTHON_MIRROR ) 154 | fi 155 | 156 | if [ -f ${OPENSHIFT_REPO_DIR}/setup.py ] 157 | then 158 | ( cd $OPENSHIFT_REPO_DIR; python ${OPENSHIFT_REPO_DIR}/setup.py develop $OPENSHIFT_PYTHON_MIRROR ) 159 | fi 160 | 161 | relative-virtenv 162 | } 163 | 164 | function deploy() { 165 | relative-virtenv 166 | activate-virtenv 167 | } 168 | 169 | case "$1" in 170 | start) start ;; 171 | stop) stop ;; 172 | restart) restart ;; 173 | status) status ;; 174 | reload) reload ;; 175 | tidy) tidy ;; 176 | build) build ;; 177 | deploy) deploy ;; 178 | *) exit 0 179 | esac 180 | -------------------------------------------------------------------------------- /usr/python/versions/2.7/bin/install: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [[ -d /usr/lib64 ]]; then 4 | _libdir=/usr/lib64 5 | else 6 | _libdir=/usr/lib 7 | fi 8 | 9 | version="$1" 10 | 11 | # The virtual environment is assumed to exist going forward 12 | if [ ! -f $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/bin/python ] 13 | then 14 | source $OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$version/lib/python-context 15 | python-context-stdin < $OPENSHIFT_ADVANCED_PYTHON_DIR/old-virtenv-files.txt.gz.new 23 | mv -f $OPENSHIFT_ADVANCED_PYTHON_DIR/old-virtenv-files.txt.gz.new $OPENSHIFT_ADVANCED_PYTHON_DIR/old-virtenv-files.txt.gz 24 | fi 25 | 26 | # Past this point, the application is not functional if there 27 | # is a failure but retry is possible. 28 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/opt 29 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/versions 30 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/upstream-repo 31 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/* 32 | 33 | # Need the environment ERBs rendered at this point 34 | for f in $OPENSHIFT_ADVANCED_PYTHON_DIR/env/*.erb; do 35 | if [ -f "$f" ]; then 36 | d=$(basename "$f" .erb) 37 | p=$(dirname "$f") 38 | /usr/bin/oo-erb -S 2 -- "$f" > "$p/$d" 39 | rm -f "$f" 40 | fi 41 | done 42 | 43 | # Update gear configuration 44 | update-configuration 45 | 46 | # Rebuild the virtenv 47 | python-context-stdin <lib symlink 7 | virtualenv --quiet --never-download --relocatable $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv 8 | # symlinks -r -c -s $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv >/dev/null 9 | } 10 | 11 | function create-virtenv { 12 | # Initialize the virtual environment 13 | mkdir -p $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv 14 | mkdir -p $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/.python-eggs/ 15 | virtualenv --quiet --never-download --system-site-packages $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv 16 | 17 | # Bug 995284 18 | versions=( $(python -c 'import sys; print "%d %d.%d" % (sys.version_info[0], sys.version_info[0], sys.version_info[1])') ) 19 | for vers in "${versions[@]}"; do 20 | [ -L $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/bin/python$vers ] || ln -s python $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/bin/python$vers 21 | done 22 | 23 | relative-virtenv 24 | } 25 | 26 | function activate-virtenv { 27 | source $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/bin/activate 28 | } 29 | -------------------------------------------------------------------------------- /usr/python/versions/2.7/lib/python-context: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Utility function to execute in the installing python context ahead 4 | # of any updates to PATH and LD_LIBRARY_PATH. 5 | 6 | function python-context { 7 | scl enable python27 "$@" 8 | } 9 | 10 | function python-context-stdin { 11 | scl enable python27 - 12 | } 13 | -------------------------------------------------------------------------------- /usr/python/versions/2.7/lib/update-configuration: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Utility function to update cartridge configuration 4 | 5 | function update-configuration { 6 | # SCL installs of python 2.7 7 | sclpath=$(dirname $(scl enable python27 "which python")) 8 | echo "$OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/bin:$OPENSHIFT_ADVANCED_PYTHON_DIR/bin:${sclpath}" > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/OPENSHIFT_PYTHON_PATH_ELEMENT 9 | 10 | local ld_path=$(LD_LIBRARY_PATH="" scl enable python27 "printenv LD_LIBRARY_PATH") 11 | path_append ${LD_LIBRARY_PATH:-:} ${ld_path:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/OPENSHIFT_ADVANCED_PYTHON_LD_LIBRARY_PATH_ELEMENT 12 | 13 | local man_path=$(MANPATH="" scl enable python27 "printenv MANPATH") 14 | path_append ${MANPATH:-:} ${man_path:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/MANPATH 15 | 16 | local xdg_data_dirs=$(XDG_DATA_DIRS="" scl enable python27 "printenv XDG_DATA_DIRS") 17 | path_append ${XDG_DATA_DIRS:-:} ${xdg_data_dirs:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/XDG_DATA_DIRS 18 | 19 | local pkg_config_path=$(PKG_CONFIG_PATH="" scl enable python27 "printenv PKG_CONFIG_PATH") 20 | path_append ${PKG_CONFIG_PATH:-:} ${pkg_config_path:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/PKG_CONFIG_PATH 21 | 22 | echo "$OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv" > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/VIRTUAL_ENV 23 | } 24 | -------------------------------------------------------------------------------- /usr/python/versions/2.7/metadata/jenkins_shell_command.erb: -------------------------------------------------------------------------------- 1 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 2 | 3 | alias rsync="rsync --delete-after -azO -e '$GIT_SSH'" 4 | 5 | upstream_ssh="<%= ENV['OPENSHIFT_GEAR_UUID'] %>@<%= ENV['OPENSHIFT_APP_NAME'] %>-${OPENSHIFT_NAMESPACE}.<%= ENV['OPENSHIFT_CLOUD_DOMAIN'] %>" 6 | 7 | # remove previous metadata, if any 8 | rm -f $OPENSHIFT_HOMEDIR/app-deployments/current/metadata.json 9 | 10 | if ! marker_present "force_clean_build"; then 11 | # don't fail if these rsyncs fail 12 | set +e 13 | rsync $upstream_ssh:'$OPENSHIFT_BUILD_DEPENDENCIES_DIR' $OPENSHIFT_BUILD_DEPENDENCIES_DIR 14 | rsync $upstream_ssh:'$OPENSHIFT_DEPENDENCIES_DIR' $OPENSHIFT_DEPENDENCIES_DIR 15 | set -e 16 | fi 17 | 18 | # Build/update libs and run user pre_build and build 19 | gear build 20 | 21 | # Run tests 22 | # python ${OPENSHIFT_REPO_DIR}setup.py test 23 | 24 | # Deploy new build 25 | 26 | # Stop app 27 | $GIT_SSH $upstream_ssh "gear stop --conditional --exclude-web-proxy --git-ref $GIT_COMMIT" 28 | 29 | deployment_dir=`$GIT_SSH $upstream_ssh 'gear create-deployment-dir'` 30 | 31 | # Push content back to application 32 | rsync $OPENSHIFT_HOMEDIR/app-deployments/current/metadata.json $upstream_ssh:app-deployments/$deployment_dir/metadata.json 33 | rsync --exclude .git $WORKSPACE/ $upstream_ssh:app-root/runtime/repo/ 34 | rsync $OPENSHIFT_BUILD_DEPENDENCIES_DIR $upstream_ssh:app-root/runtime/build-dependencies/ 35 | rsync $OPENSHIFT_DEPENDENCIES_DIR $upstream_ssh:app-root/runtime/dependencies/ 36 | 37 | # Configure / start app 38 | $GIT_SSH $upstream_ssh "gear remotedeploy --deployment-datetime $deployment_dir" 39 | -------------------------------------------------------------------------------- /usr/python/versions/2.7/metadata/rsync.excludes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsterjov/openshift-advanced-python-cartridge/8e2621cce4336a5a5eb490fbfae20ad4711c8164/usr/python/versions/2.7/metadata/rsync.excludes -------------------------------------------------------------------------------- /usr/python/versions/2.7/servers/gevent_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | from gevent import socket 3 | from gevent.pywsgi import WSGIServer 4 | 5 | import app 6 | 7 | 8 | sock_path = "{0}run/appserver.sock".format(os.environ["OPENSHIFT_ADVANCED_PYTHON_DIR"]) 9 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 10 | sock.bind(sock_path) 11 | sock.listen(256) 12 | 13 | WSGIServer(sock, app.application).serve_forever() -------------------------------------------------------------------------------- /usr/python/versions/2.7/servers/wsgiref_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | from wsgiref.simple_server import make_server 3 | 4 | import app 5 | 6 | 7 | server = make_server(os.environ["OPENSHIFT_ADVANCED_PYTHON_IP"], 15000, app.application) 8 | print "Serving HTTP on port 15000..." 9 | 10 | server.serve_forever() -------------------------------------------------------------------------------- /usr/python/versions/3.3/bin/control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 4 | source "${OPENSHIFT_ADVANCED_PYTHON_DIR}/usr/python/versions/${OPENSHIFT_ADVANCED_PYTHON_VERSION}/lib/create-virtenv" 5 | 6 | 7 | # default python server 8 | if [ ! $OPENSHIFT_PYTHON_SERVER ]; then 9 | echo wsgiref > $OPENSHIFT_HOMEDIR/.env/user_vars/OPENSHIFT_PYTHON_SERVER 10 | export OPENSHIFT_PYTHON_SERVER=wsgiref 11 | fi 12 | 13 | 14 | function start_wsgiref() { 15 | nohup python usr/python/versions/2.7/servers/wsgiref_server.py > "$OPENSHIFT_ADVANCED_PYTHON_DIR/logs/appserver.log" 2>&1 & 16 | echo $! > $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid 17 | } 18 | 19 | function start_gevent() { 20 | nohup python usr/python/versions/2.7/servers/gevent_server.py > "$OPENSHIFT_ADVANCED_PYTHON_DIR/logs/appserver.log" 2>&1 & 21 | echo $! > $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid 22 | } 23 | 24 | function start_gunicorn() { 25 | nohup gunicorn -c $OPENSHIFT_ADVANCED_PYTHON_DIR/conf/gunicorn.py app:application > "$OPENSHIFT_ADVANCED_PYTHON_DIR/logs/appserver.log" 2>&1 26 | } 27 | 28 | 29 | function start() { 30 | echo "Starting PYTHON cart" 31 | 32 | echo "Activating virtenv" 33 | activate-virtenv 34 | 35 | # app servers have changed 36 | if [ "$OPENSHIFT_PYTHON_SERVER" != "$OPENSHIFT_PYTHON_RUNNING_SERVER" ]; then 37 | build_server 38 | fi 39 | 40 | export PYTHONPATH=$PYTHONPATH:$OPENSHIFT_REPO_DIR 41 | 42 | case "$OPENSHIFT_PYTHON_SERVER" in 43 | wsgiref) 44 | start_wsgiref 45 | ;; 46 | gevent) 47 | start_gevent 48 | ;; 49 | gunicorn) 50 | start_gunicorn 51 | ;; 52 | esac 53 | } 54 | 55 | 56 | function stop() { 57 | echo "Stopping PYTHON cart" 58 | 59 | if [ -s $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid ] 60 | then 61 | kpid=$(cat $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid) 62 | iters=0 63 | while kill $kpid >/dev/null 2>&1 64 | do 65 | iters=$(($iters + 1)) 66 | [ $iters -gt 10 ] && break 67 | sleep 1 68 | done 69 | kill -KILL $kpid >/dev/null 2>&1 || : 70 | rm -f $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.pid 71 | 72 | if [ -S $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.sock ]; then 73 | rm -f $OPENSHIFT_ADVANCED_PYTHON_DIR/run/appserver.sock 74 | fi 75 | fi 76 | } 77 | 78 | 79 | function restart() { 80 | echo "Restarting PYTHON cart" 81 | stop 82 | start 83 | } 84 | 85 | function status() { 86 | res=0 87 | output=$(curl -s -m 30 http://$OPENSHIFT_ADVANCED_PYTHON_IP:$OPENSHIFT_ADVANCED_PYTHON_PORT/ &> /dev/null) || res=1 88 | 89 | if [ $res -eq 0 ] 90 | then 91 | client_result "Application is running" 92 | client_result "$output" 93 | else 94 | client_result "Application is either stopped or inaccessible" 95 | fi 96 | } 97 | 98 | function reload() { 99 | echo "Reloading PYTHON cart" 100 | restart 101 | } 102 | 103 | # Clean up any log files 104 | function tidy() { 105 | client_message "Emptying python logs in: $OPENSHIFT_ADVANCED_PYTHON_DIR/logs" 106 | rm logs/appserver.log 107 | rm logs/appserver.*.log 108 | } 109 | 110 | 111 | # install the selected server 112 | function build_server() { 113 | case "$OPENSHIFT_PYTHON_SERVER" in 114 | gevent) 115 | pip install gevent 116 | ;; 117 | gunicorn) 118 | pip install gevent gunicorn 119 | ;; 120 | esac 121 | 122 | echo $OPENSHIFT_PYTHON_SERVER > env/OPENSHIFT_PYTHON_RUNNING_SERVER 123 | export OPENSHIFT_PYTHON_RUNNING_SERVER=$OPENSHIFT_PYTHON_SERVER 124 | } 125 | 126 | 127 | function build() { 128 | if [ -n "$OPENSHIFT_PYPI_MIRROR_URL" ]; then 129 | m=$OPENSHIFT_PYPI_MIRROR_URL 130 | if curl -m 15 -f -s "$m" &>/dev/null 131 | then 132 | OPENSHIFT_PYTHON_MIRROR="-i $m" 133 | fi 134 | fi 135 | 136 | if force_clean_build_enabled_for_latest_deployment; then 137 | echo "Force-clean builds are enabled" 1>&2 138 | rm -rf $VIRTUAL_ENV/* 139 | fi 140 | 141 | if [ ! -f $VIRTUAL_ENV/bin/python ]; then 142 | echo "Recreating virtenv" 1>&2 143 | create-virtenv 144 | fi 145 | 146 | echo "Activating virtenv" 147 | activate-virtenv 148 | 149 | build_server 150 | 151 | if [ -f ${OPENSHIFT_REPO_DIR}/requirements.txt ] 152 | then 153 | ( cd $OPENSHIFT_REPO_DIR; pip install -r ${OPENSHIFT_REPO_DIR}/requirements.txt $OPENSHIFT_PYTHON_MIRROR ) 154 | fi 155 | 156 | if [ -f ${OPENSHIFT_REPO_DIR}/setup.py ] 157 | then 158 | ( cd $OPENSHIFT_REPO_DIR; python ${OPENSHIFT_REPO_DIR}/setup.py develop $OPENSHIFT_PYTHON_MIRROR ) 159 | fi 160 | 161 | relative-virtenv 162 | } 163 | 164 | function deploy() { 165 | relative-virtenv 166 | activate-virtenv 167 | } 168 | 169 | case "$1" in 170 | start) start ;; 171 | stop) stop ;; 172 | restart) restart ;; 173 | status) status ;; 174 | reload) reload ;; 175 | tidy) tidy ;; 176 | build) build ;; 177 | deploy) deploy ;; 178 | *) exit 0 179 | esac 180 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/bin/install: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [[ -d /usr/lib64 ]]; then 4 | _libdir=/usr/lib64 5 | else 6 | _libdir=/usr/lib 7 | fi 8 | 9 | version="$1" 10 | 11 | # The virtual environment is assumed to exist going forward 12 | if [ ! -f $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/bin/python ] 13 | then 14 | source $OPENSHIFT_ADVANCED_PYTHON_DIR/usr/python/versions/$version/lib/python-context 15 | python-context-stdin < $OPENSHIFT_ADVANCED_PYTHON_DIR/old-virtenv-files.txt.gz.new 23 | mv -f $OPENSHIFT_ADVANCED_PYTHON_DIR/old-virtenv-files.txt.gz.new $OPENSHIFT_ADVANCED_PYTHON_DIR/old-virtenv-files.txt.gz 24 | fi 25 | 26 | # Past this point, the application is not functional if there 27 | # is a failure but retry is possible. 28 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/opt 29 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/versions 30 | rm -rf $OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/* 31 | 32 | # Need the environment ERBs rendered at this point 33 | for f in $OPENSHIFT_ADVANCED_PYTHON_DIR/env/*.erb; do 34 | if [ -f "$f" ]; then 35 | d=$(basename "$f" .erb) 36 | /usr/bin/oo-erb -S 2 -- "$f" > "$d" 37 | rm -f "$f" 38 | fi 39 | done 40 | 41 | # Update gear configuration 42 | update-configuration 43 | 44 | # Rebuild the virtenv 45 | python-context-stdin < /dev/null 9 | 10 | vdir=$(cd "${venv_dir}" && pwd) 11 | for zf in $(grep -l -r "#\!$vdir/venv/bin/" . ); do 12 | sed --follow-symlinks -i "s;#\!$vdir/venv/bin/;#\!/usr/bin/env ;" "$zf" 13 | done 14 | 15 | popd > /dev/null 16 | } 17 | 18 | function create-virtenv { 19 | if [ ! -d "$venv_dir" ] || ! $(ls "$venv_dir" > /dev/null 2>&1); then 20 | pyvenv --system-site-packages --clear $venv_dir 21 | fi 22 | 23 | setup_binaries=("$venv_dir/bin/easy_install" "$venv_dir/bin/pip") 24 | if ! $(ls ${setup_binaries[@]} > /dev/null 2>&1); then 25 | install_setup_tools 26 | fi 27 | 28 | relative-virtenv 29 | } 30 | 31 | function install_setup_tools() { 32 | pushd "$venv_dir" > /dev/null 33 | 34 | # Activate virtual env. 35 | source bin/activate 36 | # Run distribute_setup. 37 | python ${OPENSHIFT_ADVANCED_PYTHON_DIR}/usr/python/versions/3.3/lib/distribute_setup.py 38 | 39 | # Activate virtual env so as to get access to easy_install and install pip. 40 | source bin/activate 41 | easy_install pip 42 | 43 | popd > /dev/null 44 | } 45 | 46 | function activate-virtenv { 47 | source $venv_dir/bin/activate 48 | } 49 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/lib/distribute_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap distribute installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from distribute_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import shutil 18 | import sys 19 | import time 20 | import fnmatch 21 | import tempfile 22 | import tarfile 23 | import optparse 24 | 25 | from distutils import log 26 | 27 | try: 28 | from site import USER_SITE 29 | except ImportError: 30 | USER_SITE = None 31 | 32 | try: 33 | import subprocess 34 | 35 | def _python_cmd(*args): 36 | args = (sys.executable,) + args 37 | return subprocess.call(args) == 0 38 | 39 | except ImportError: 40 | # will be used for python 2.3 41 | def _python_cmd(*args): 42 | args = (sys.executable,) + args 43 | # quoting arguments if windows 44 | if sys.platform == 'win32': 45 | def quote(arg): 46 | if ' ' in arg: 47 | return '"%s"' % arg 48 | return arg 49 | args = [quote(arg) for arg in args] 50 | return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 51 | 52 | DEFAULT_VERSION = "0.6.49" 53 | DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" 54 | SETUPTOOLS_FAKED_VERSION = "0.6c11" 55 | 56 | SETUPTOOLS_PKG_INFO = """\ 57 | Metadata-Version: 1.0 58 | Name: setuptools 59 | Version: %s 60 | Summary: xxxx 61 | Home-page: xxx 62 | Author: xxx 63 | Author-email: xxx 64 | License: xxx 65 | Description: xxx 66 | """ % SETUPTOOLS_FAKED_VERSION 67 | 68 | 69 | def _install(tarball, install_args=()): 70 | # extracting the tarball 71 | tmpdir = tempfile.mkdtemp() 72 | log.warn('Extracting in %s', tmpdir) 73 | old_wd = os.getcwd() 74 | try: 75 | os.chdir(tmpdir) 76 | tar = tarfile.open(tarball) 77 | _extractall(tar) 78 | tar.close() 79 | 80 | # going in the directory 81 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 82 | os.chdir(subdir) 83 | log.warn('Now working in %s', subdir) 84 | 85 | # installing 86 | log.warn('Installing Distribute') 87 | if not _python_cmd('setup.py', 'install', *install_args): 88 | log.warn('Something went wrong during the installation.') 89 | log.warn('See the error message above.') 90 | # exitcode will be 2 91 | return 2 92 | finally: 93 | os.chdir(old_wd) 94 | shutil.rmtree(tmpdir) 95 | 96 | 97 | def _build_egg(egg, tarball, to_dir): 98 | # extracting the tarball 99 | tmpdir = tempfile.mkdtemp() 100 | log.warn('Extracting in %s', tmpdir) 101 | old_wd = os.getcwd() 102 | try: 103 | os.chdir(tmpdir) 104 | tar = tarfile.open(tarball) 105 | _extractall(tar) 106 | tar.close() 107 | 108 | # going in the directory 109 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 110 | os.chdir(subdir) 111 | log.warn('Now working in %s', subdir) 112 | 113 | # building an egg 114 | log.warn('Building a Distribute egg in %s', to_dir) 115 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 116 | 117 | finally: 118 | os.chdir(old_wd) 119 | shutil.rmtree(tmpdir) 120 | # returning the result 121 | log.warn(egg) 122 | if not os.path.exists(egg): 123 | raise IOError('Could not build the egg.') 124 | 125 | 126 | def _do_download(version, download_base, to_dir, download_delay): 127 | egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' 128 | % (version, sys.version_info[0], sys.version_info[1])) 129 | if not os.path.exists(egg): 130 | tarball = download_setuptools(version, download_base, 131 | to_dir, download_delay) 132 | _build_egg(egg, tarball, to_dir) 133 | sys.path.insert(0, egg) 134 | import setuptools 135 | setuptools.bootstrap_install_from = egg 136 | 137 | 138 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 139 | to_dir=os.curdir, download_delay=15, no_fake=True): 140 | # making sure we use the absolute path 141 | to_dir = os.path.abspath(to_dir) 142 | was_imported = 'pkg_resources' in sys.modules or \ 143 | 'setuptools' in sys.modules 144 | try: 145 | try: 146 | import pkg_resources 147 | 148 | # Setuptools 0.7b and later is a suitable (and preferable) 149 | # substitute for any Distribute version. 150 | try: 151 | pkg_resources.require("setuptools>=0.7b") 152 | return 153 | except (pkg_resources.DistributionNotFound, 154 | pkg_resources.VersionConflict): 155 | pass 156 | 157 | if not hasattr(pkg_resources, '_distribute'): 158 | if not no_fake: 159 | _fake_setuptools() 160 | raise ImportError 161 | except ImportError: 162 | return _do_download(version, download_base, to_dir, download_delay) 163 | try: 164 | pkg_resources.require("distribute>=" + version) 165 | return 166 | except pkg_resources.VersionConflict: 167 | e = sys.exc_info()[1] 168 | if was_imported: 169 | sys.stderr.write( 170 | "The required version of distribute (>=%s) is not available,\n" 171 | "and can't be installed while this script is running. Please\n" 172 | "install a more recent version first, using\n" 173 | "'easy_install -U distribute'." 174 | "\n\n(Currently using %r)\n" % (version, e.args[0])) 175 | sys.exit(2) 176 | else: 177 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 178 | return _do_download(version, download_base, to_dir, 179 | download_delay) 180 | except pkg_resources.DistributionNotFound: 181 | return _do_download(version, download_base, to_dir, 182 | download_delay) 183 | finally: 184 | if not no_fake: 185 | _create_fake_setuptools_pkg_info(to_dir) 186 | 187 | 188 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 189 | to_dir=os.curdir, delay=15): 190 | """Download distribute from a specified location and return its filename 191 | 192 | `version` should be a valid distribute version number that is available 193 | as an egg for download under the `download_base` URL (which should end 194 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 195 | `delay` is the number of seconds to pause before an actual download 196 | attempt. 197 | """ 198 | # making sure we use the absolute path 199 | to_dir = os.path.abspath(to_dir) 200 | try: 201 | from urllib.request import urlopen 202 | except ImportError: 203 | from urllib2 import urlopen 204 | tgz_name = "distribute-%s.tar.gz" % version 205 | url = download_base + tgz_name 206 | saveto = os.path.join(to_dir, tgz_name) 207 | src = dst = None 208 | if not os.path.exists(saveto): # Avoid repeated downloads 209 | try: 210 | log.warn("Downloading %s", url) 211 | src = urlopen(url) 212 | # Read/write all in one block, so we don't create a corrupt file 213 | # if the download is interrupted. 214 | data = src.read() 215 | dst = open(saveto, "wb") 216 | dst.write(data) 217 | finally: 218 | if src: 219 | src.close() 220 | if dst: 221 | dst.close() 222 | return os.path.realpath(saveto) 223 | 224 | 225 | def _no_sandbox(function): 226 | def __no_sandbox(*args, **kw): 227 | try: 228 | from setuptools.sandbox import DirectorySandbox 229 | if not hasattr(DirectorySandbox, '_old'): 230 | def violation(*args): 231 | pass 232 | DirectorySandbox._old = DirectorySandbox._violation 233 | DirectorySandbox._violation = violation 234 | patched = True 235 | else: 236 | patched = False 237 | except ImportError: 238 | patched = False 239 | 240 | try: 241 | return function(*args, **kw) 242 | finally: 243 | if patched: 244 | DirectorySandbox._violation = DirectorySandbox._old 245 | del DirectorySandbox._old 246 | 247 | return __no_sandbox 248 | 249 | 250 | def _patch_file(path, content): 251 | """Will backup the file then patch it""" 252 | f = open(path) 253 | existing_content = f.read() 254 | f.close() 255 | if existing_content == content: 256 | # already patched 257 | log.warn('Already patched.') 258 | return False 259 | log.warn('Patching...') 260 | _rename_path(path) 261 | f = open(path, 'w') 262 | try: 263 | f.write(content) 264 | finally: 265 | f.close() 266 | return True 267 | 268 | _patch_file = _no_sandbox(_patch_file) 269 | 270 | 271 | def _same_content(path, content): 272 | f = open(path) 273 | existing_content = f.read() 274 | f.close() 275 | return existing_content == content 276 | 277 | 278 | def _rename_path(path): 279 | new_name = path + '.OLD.%s' % time.time() 280 | log.warn('Renaming %s to %s', path, new_name) 281 | os.rename(path, new_name) 282 | return new_name 283 | 284 | 285 | def _remove_flat_installation(placeholder): 286 | if not os.path.isdir(placeholder): 287 | log.warn('Unkown installation at %s', placeholder) 288 | return False 289 | found = False 290 | for file in os.listdir(placeholder): 291 | if fnmatch.fnmatch(file, 'setuptools*.egg-info'): 292 | found = True 293 | break 294 | if not found: 295 | log.warn('Could not locate setuptools*.egg-info') 296 | return 297 | 298 | log.warn('Moving elements out of the way...') 299 | pkg_info = os.path.join(placeholder, file) 300 | if os.path.isdir(pkg_info): 301 | patched = _patch_egg_dir(pkg_info) 302 | else: 303 | patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) 304 | 305 | if not patched: 306 | log.warn('%s already patched.', pkg_info) 307 | return False 308 | # now let's move the files out of the way 309 | for element in ('setuptools', 'pkg_resources.py', 'site.py'): 310 | element = os.path.join(placeholder, element) 311 | if os.path.exists(element): 312 | _rename_path(element) 313 | else: 314 | log.warn('Could not find the %s element of the ' 315 | 'Setuptools distribution', element) 316 | return True 317 | 318 | _remove_flat_installation = _no_sandbox(_remove_flat_installation) 319 | 320 | 321 | def _after_install(dist): 322 | log.warn('After install bootstrap.') 323 | placeholder = dist.get_command_obj('install').install_purelib 324 | _create_fake_setuptools_pkg_info(placeholder) 325 | 326 | 327 | def _create_fake_setuptools_pkg_info(placeholder): 328 | if not placeholder or not os.path.exists(placeholder): 329 | log.warn('Could not find the install location') 330 | return 331 | pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) 332 | setuptools_file = 'setuptools-%s-py%s.egg-info' % \ 333 | (SETUPTOOLS_FAKED_VERSION, pyver) 334 | pkg_info = os.path.join(placeholder, setuptools_file) 335 | if os.path.exists(pkg_info): 336 | log.warn('%s already exists', pkg_info) 337 | return 338 | 339 | log.warn('Creating %s', pkg_info) 340 | try: 341 | f = open(pkg_info, 'w') 342 | except EnvironmentError: 343 | log.warn("Don't have permissions to write %s, skipping", pkg_info) 344 | return 345 | try: 346 | f.write(SETUPTOOLS_PKG_INFO) 347 | finally: 348 | f.close() 349 | 350 | pth_file = os.path.join(placeholder, 'setuptools.pth') 351 | log.warn('Creating %s', pth_file) 352 | f = open(pth_file, 'w') 353 | try: 354 | f.write(os.path.join(os.curdir, setuptools_file)) 355 | finally: 356 | f.close() 357 | 358 | _create_fake_setuptools_pkg_info = _no_sandbox( 359 | _create_fake_setuptools_pkg_info 360 | ) 361 | 362 | 363 | def _patch_egg_dir(path): 364 | # let's check if it's already patched 365 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 366 | if os.path.exists(pkg_info): 367 | if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): 368 | log.warn('%s already patched.', pkg_info) 369 | return False 370 | _rename_path(path) 371 | os.mkdir(path) 372 | os.mkdir(os.path.join(path, 'EGG-INFO')) 373 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 374 | f = open(pkg_info, 'w') 375 | try: 376 | f.write(SETUPTOOLS_PKG_INFO) 377 | finally: 378 | f.close() 379 | return True 380 | 381 | _patch_egg_dir = _no_sandbox(_patch_egg_dir) 382 | 383 | 384 | def _before_install(): 385 | log.warn('Before install bootstrap.') 386 | _fake_setuptools() 387 | 388 | 389 | def _under_prefix(location): 390 | if 'install' not in sys.argv: 391 | return True 392 | args = sys.argv[sys.argv.index('install') + 1:] 393 | for index, arg in enumerate(args): 394 | for option in ('--root', '--prefix'): 395 | if arg.startswith('%s=' % option): 396 | top_dir = arg.split('root=')[-1] 397 | return location.startswith(top_dir) 398 | elif arg == option: 399 | if len(args) > index: 400 | top_dir = args[index + 1] 401 | return location.startswith(top_dir) 402 | if arg == '--user' and USER_SITE is not None: 403 | return location.startswith(USER_SITE) 404 | return True 405 | 406 | 407 | def _fake_setuptools(): 408 | log.warn('Scanning installed packages') 409 | try: 410 | import pkg_resources 411 | except ImportError: 412 | # we're cool 413 | log.warn('Setuptools or Distribute does not seem to be installed.') 414 | return 415 | ws = pkg_resources.working_set 416 | try: 417 | setuptools_dist = ws.find( 418 | pkg_resources.Requirement.parse('setuptools', replacement=False) 419 | ) 420 | except TypeError: 421 | # old distribute API 422 | setuptools_dist = ws.find( 423 | pkg_resources.Requirement.parse('setuptools') 424 | ) 425 | 426 | if setuptools_dist is None: 427 | log.warn('No setuptools distribution found') 428 | return 429 | # detecting if it was already faked 430 | setuptools_location = setuptools_dist.location 431 | log.warn('Setuptools installation detected at %s', setuptools_location) 432 | 433 | # if --root or --preix was provided, and if 434 | # setuptools is not located in them, we don't patch it 435 | if not _under_prefix(setuptools_location): 436 | log.warn('Not patching, --root or --prefix is installing Distribute' 437 | ' in another location') 438 | return 439 | 440 | # let's see if its an egg 441 | if not setuptools_location.endswith('.egg'): 442 | log.warn('Non-egg installation') 443 | res = _remove_flat_installation(setuptools_location) 444 | if not res: 445 | return 446 | else: 447 | log.warn('Egg installation') 448 | pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') 449 | if (os.path.exists(pkg_info) and 450 | _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): 451 | log.warn('Already patched.') 452 | return 453 | log.warn('Patching...') 454 | # let's create a fake egg replacing setuptools one 455 | res = _patch_egg_dir(setuptools_location) 456 | if not res: 457 | return 458 | log.warn('Patching complete.') 459 | _relaunch() 460 | 461 | 462 | def _relaunch(): 463 | log.warn('Relaunching...') 464 | # we have to relaunch the process 465 | # pip marker to avoid a relaunch bug 466 | _cmd1 = ['-c', 'install', '--single-version-externally-managed'] 467 | _cmd2 = ['-c', 'install', '--record'] 468 | if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2: 469 | sys.argv[0] = 'setup.py' 470 | args = [sys.executable] + sys.argv 471 | sys.exit(subprocess.call(args)) 472 | 473 | 474 | def _extractall(self, path=".", members=None): 475 | """Extract all members from the archive to the current working 476 | directory and set owner, modification time and permissions on 477 | directories afterwards. `path' specifies a different directory 478 | to extract to. `members' is optional and must be a subset of the 479 | list returned by getmembers(). 480 | """ 481 | import copy 482 | import operator 483 | from tarfile import ExtractError 484 | directories = [] 485 | 486 | if members is None: 487 | members = self 488 | 489 | for tarinfo in members: 490 | if tarinfo.isdir(): 491 | # Extract directories with a safe mode. 492 | directories.append(tarinfo) 493 | tarinfo = copy.copy(tarinfo) 494 | tarinfo.mode = 448 # decimal for oct 0700 495 | self.extract(tarinfo, path) 496 | 497 | # Reverse sort directories. 498 | if sys.version_info < (2, 4): 499 | def sorter(dir1, dir2): 500 | return cmp(dir1.name, dir2.name) 501 | directories.sort(sorter) 502 | directories.reverse() 503 | else: 504 | directories.sort(key=operator.attrgetter('name'), reverse=True) 505 | 506 | # Set correct owner, mtime and filemode on directories. 507 | for tarinfo in directories: 508 | dirpath = os.path.join(path, tarinfo.name) 509 | try: 510 | self.chown(tarinfo, dirpath) 511 | self.utime(tarinfo, dirpath) 512 | self.chmod(tarinfo, dirpath) 513 | except ExtractError: 514 | e = sys.exc_info()[1] 515 | if self.errorlevel > 1: 516 | raise 517 | else: 518 | self._dbg(1, "tarfile: %s" % e) 519 | 520 | 521 | def _build_install_args(options): 522 | """ 523 | Build the arguments to 'python setup.py install' on the distribute package 524 | """ 525 | install_args = [] 526 | if options.user_install: 527 | if sys.version_info < (2, 6): 528 | log.warn("--user requires Python 2.6 or later") 529 | raise SystemExit(1) 530 | install_args.append('--user') 531 | return install_args 532 | 533 | def _parse_args(): 534 | """ 535 | Parse the command line for options 536 | """ 537 | parser = optparse.OptionParser() 538 | parser.add_option( 539 | '--user', dest='user_install', action='store_true', default=False, 540 | help='install in user site package (requires Python 2.6 or later)') 541 | parser.add_option( 542 | '--download-base', dest='download_base', metavar="URL", 543 | default=DEFAULT_URL, 544 | help='alternative URL from where to download the distribute package') 545 | options, args = parser.parse_args() 546 | # positional arguments are ignored 547 | return options 548 | 549 | def main(version=DEFAULT_VERSION): 550 | """Install or upgrade setuptools and EasyInstall""" 551 | options = _parse_args() 552 | tarball = download_setuptools(download_base=options.download_base) 553 | return _install(tarball, _build_install_args(options)) 554 | 555 | if __name__ == '__main__': 556 | sys.exit(main()) 557 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/lib/python-context: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Utility function to execute in the installing python context ahead 4 | # of any updates to PATH and LD_LIBRARY_PATH. 5 | 6 | function python-context { 7 | scl enable python33 "$@" 8 | } 9 | 10 | function python-context-stdin { 11 | scl enable python33 - 12 | } 13 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/lib/update-configuration: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Utility function to update cartridge configuration 4 | 5 | function update-configuration { 6 | # SCL installs of python 3.3 7 | sclpath=$(dirname $(scl enable python33 "which python")) 8 | echo "$OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/venv/bin:$OPENSHIFT_ADVANCED_PYTHON_DIR/bin:${sclpath}" > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/OPENSHIFT_PYTHON_PATH_ELEMENT 9 | 10 | local ld_path=$(LD_LIBRARY_PATH="" scl enable python33 "printenv LD_LIBRARY_PATH") 11 | path_append ${LD_LIBRARY_PATH:-:} ${ld_path:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/OPENSHIFT_ADVANCED_PYTHON_LD_LIBRARY_PATH_ELEMENT 12 | 13 | local man_path=$(MANPATH="" scl enable python33 "printenv MANPATH") 14 | path_append ${MANPATH:-:} ${man_path:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/MANPATH 15 | 16 | local xdg_data_dirs=$(XDG_DATA_DIRS="" scl enable python33 "printenv XDG_DATA_DIRS") 17 | path_append ${XDG_DATA_DIRS:-:} ${xdg_data_dirs:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/XDG_DATA_DIRS 18 | 19 | local pkg_config_path=$(PKG_CONFIG_PATH="" scl enable python33 "printenv PKG_CONFIG_PATH") 20 | path_append ${PKG_CONFIG_PATH:-:} ${pkg_config_path:-:} > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/PKG_CONFIG_PATH 21 | 22 | echo "$OPENSHIFT_ADVANCED_PYTHON_DIR/virtenv/venv" > $OPENSHIFT_ADVANCED_PYTHON_DIR/env/VIRTUAL_ENV 23 | } 24 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/metadata/jenkins_shell_command.erb: -------------------------------------------------------------------------------- 1 | source $OPENSHIFT_CARTRIDGE_SDK_BASH 2 | 3 | alias rsync="rsync --exclude-from='${OPENSHIFT_ADVANCED_PYTHON_DIR}/metadata/rsync.excludes' --delete-after -az -e '$GIT_SSH'" 4 | 5 | upstream_ssh="<%= ENV['OPENSHIFT_GEAR_UUID'] %>@<%= ENV['OPENSHIFT_APP_NAME'] %>-${OPENSHIFT_NAMESPACE}.<%= ENV['OPENSHIFT_CLOUD_DOMAIN'] %>" 6 | 7 | # remove previous metadata, if any 8 | rm -f $OPENSHIFT_HOMEDIR/app-deployments/current/metadata.json 9 | 10 | if ! marker_present "force_clean_build"; then 11 | # don't fail if these rsyncs fail 12 | set +e 13 | rsync $upstream_ssh:'$OPENSHIFT_BUILD_DEPENDENCIES_DIR' $OPENSHIFT_BUILD_DEPENDENCIES_DIR 14 | rsync $upstream_ssh:'$OPENSHIFT_DEPENDENCIES_DIR' $OPENSHIFT_DEPENDENCIES_DIR 15 | set -e 16 | fi 17 | 18 | # Build/update libs and run user pre_build and build 19 | gear build 20 | 21 | # Run tests 22 | # python ${OPENSHIFT_REPO_DIR}setup.py test 23 | 24 | # Deploy new build 25 | 26 | # Stop app 27 | $GIT_SSH $upstream_ssh "gear stop --conditional --exclude-web-proxy --git-ref $GIT_COMMIT" 28 | 29 | deployment_dir=`$GIT_SSH $upstream_ssh 'gear create-deployment-dir'` 30 | 31 | # Push content back to application 32 | rsync $OPENSHIFT_HOMEDIR/app-deployments/current/metadata.json $upstream_ssh:app-deployments/$deployment_dir/metadata.json 33 | rsync --exclude .git $WORKSPACE/ $upstream_ssh:app-root/runtime/repo/ 34 | rsync $OPENSHIFT_BUILD_DEPENDENCIES_DIR $upstream_ssh:app-root/runtime/build-dependencies/ 35 | rsync $OPENSHIFT_DEPENDENCIES_DIR $upstream_ssh:app-root/runtime/dependencies/ 36 | 37 | # Configure / start app 38 | $GIT_SSH $upstream_ssh "gear remotedeploy --deployment-datetime $deployment_dir" 39 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/metadata/rsync.excludes: -------------------------------------------------------------------------------- 1 | bin/activate 2 | bin/easy_install* 3 | bin/pip* 4 | bin/python* 5 | bin/pydoc 6 | pyvenv.cfg 7 | -------------------------------------------------------------------------------- /usr/python/versions/3.3/servers/gevent_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | from gevent import socket 3 | from gevent.pywsgi import WSGIServer 4 | 5 | import app 6 | 7 | 8 | sock_path = "{0}run/appserver.sock".format(os.environ["OPENSHIFT_ADVANCED_PYTHON_DIR"]) 9 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 10 | sock.bind(sock_path) 11 | sock.listen(256) 12 | 13 | WSGIServer(sock, app.application).serve_forever() -------------------------------------------------------------------------------- /usr/python/versions/3.3/servers/wsgiref_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | from wsgiref.simple_server import make_server 3 | 4 | import app 5 | 6 | 7 | server = make_server(os.environ["OPENSHIFT_ADVANCED_PYTHON_IP"], 15000, app.application) 8 | print "Serving HTTP on port 15000..." 9 | 10 | server.serve_forever() --------------------------------------------------------------------------------